1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 11:37:44 +00:00

LibIMAP: Stop parsing immediately on error

This makes the parser more resilient to invalid IMAP messages.

Usages of `Optional` have also been removed where the empty case is
equivalent to an empty object.
This commit is contained in:
Tim Ledbetter 2023-10-07 15:53:30 +01:00 committed by Andrew Kaster
parent 29c477598c
commit 54a28afc13
5 changed files with 333 additions and 314 deletions

View file

@ -317,33 +317,33 @@ void MailWidget::selected_mailbox(GUI::ModelIndex const& index)
auto seen = !response_data.flags().find_if([](StringView value) { return value.equals_ignoring_ascii_case("\\Seen"sv); }).is_end(); auto seen = !response_data.flags().find_if([](StringView value) { return value.equals_ignoring_ascii_case("\\Seen"sv); }).is_end();
DeprecatedString date = internal_date.to_deprecated_string(); DeprecatedString date = internal_date.to_deprecated_string();
DeprecatedString subject = envelope.subject.value_or("(No subject)"); DeprecatedString subject = envelope.subject.is_empty() ? "(No subject)" : envelope.subject;
if (subject.contains("=?"sv) && subject.contains("?="sv)) { if (subject.contains("=?"sv) && subject.contains("?="sv)) {
subject = MUST(IMAP::decode_rfc2047_encoded_words(subject)).span(); subject = MUST(IMAP::decode_rfc2047_encoded_words(subject)).span();
} }
StringBuilder sender_builder; StringBuilder sender_builder;
if (envelope.from.has_value()) { if (!envelope.from.is_empty()) {
bool first { true }; bool first { true };
for (auto const& address : *envelope.from) { for (auto const& address : envelope.from) {
if (!first) if (!first)
sender_builder.append(", "sv); sender_builder.append(", "sv);
if (address.name.has_value()) { if (!address.name.is_empty()) {
if (address.name->contains("=?"sv) && address.name->contains("?="sv)) if (address.name.contains("=?"sv) && address.name.contains("?="sv))
sender_builder.append(MUST(IMAP::decode_rfc2047_encoded_words(*address.name))); sender_builder.append(MUST(IMAP::decode_rfc2047_encoded_words(address.name)));
else else
sender_builder.append(*address.name); sender_builder.append(address.name);
sender_builder.append(" <"sv); sender_builder.append(" <"sv);
sender_builder.append(address.mailbox.value_or({})); sender_builder.append(address.mailbox);
sender_builder.append('@'); sender_builder.append('@');
sender_builder.append(address.host.value_or({})); sender_builder.append(address.host);
sender_builder.append('>'); sender_builder.append('>');
} else { } else {
sender_builder.append(address.mailbox.value_or({})); sender_builder.append(address.mailbox);
sender_builder.append('@'); sender_builder.append('@');
sender_builder.append(address.host.value_or({})); sender_builder.append(address.host);
} }
} }
} }
@ -474,13 +474,13 @@ void MailWidget::selected_email_to_load(GUI::ModelIndex const& index)
} }
auto& body_data = fetch_response_data.body_data(); auto& body_data = fetch_response_data.body_data();
auto body_text_part_iterator = body_data.find_if([](Tuple<IMAP::FetchCommand::DataItem, Optional<DeprecatedString>>& data) { auto body_text_part_iterator = body_data.find_if([](Tuple<IMAP::FetchCommand::DataItem, DeprecatedString>& data) {
const auto data_item = data.get<0>(); const auto data_item = data.get<0>();
return data_item.section.has_value() && data_item.section->type == IMAP::FetchCommand::DataItem::SectionType::Parts; return data_item.section.has_value() && data_item.section->type == IMAP::FetchCommand::DataItem::SectionType::Parts;
}); });
VERIFY(body_text_part_iterator != body_data.end()); VERIFY(body_text_part_iterator != body_data.end());
auto& encoded_data = body_text_part_iterator->get<1>().value(); auto& encoded_data = body_text_part_iterator->get<1>();
DeprecatedString decoded_data; DeprecatedString decoded_data;

View file

@ -169,23 +169,23 @@ private:
}; };
struct Address { struct Address {
Optional<DeprecatedString> name; DeprecatedString name;
Optional<DeprecatedString> source_route; DeprecatedString source_route;
Optional<DeprecatedString> mailbox; DeprecatedString mailbox;
Optional<DeprecatedString> host; DeprecatedString host;
}; };
struct Envelope { struct Envelope {
Optional<DeprecatedString> date; // Format of date not specified. DeprecatedString date; // Format of date not specified.
Optional<DeprecatedString> subject; DeprecatedString subject;
Optional<Vector<Address>> from; Vector<Address> from;
Optional<Vector<Address>> sender; Vector<Address> sender;
Optional<Vector<Address>> reply_to; Vector<Address> reply_to;
Optional<Vector<Address>> to; Vector<Address> to;
Optional<Vector<Address>> cc; Vector<Address> cc;
Optional<Vector<Address>> bcc; Vector<Address> bcc;
Optional<DeprecatedString> in_reply_to; DeprecatedString in_reply_to;
Optional<DeprecatedString> message_id; DeprecatedString message_id;
}; };
class BodyStructure; class BodyStructure;
@ -199,28 +199,28 @@ struct MultiPartBodyStructureData {
Vector<OwnPtr<BodyStructure>> bodies; Vector<OwnPtr<BodyStructure>> bodies;
Vector<DeprecatedString> langs; Vector<DeprecatedString> langs;
DeprecatedString multipart_subtype; DeprecatedString multipart_subtype;
Optional<HashMap<DeprecatedString, DeprecatedString>> params; HashMap<DeprecatedString, DeprecatedString> params;
Optional<DeprecatedString> location; DeprecatedString location;
Optional<Vector<BodyExtension>> extensions; Vector<BodyExtension> extensions;
}; };
struct BodyStructureData { struct BodyStructureData {
DeprecatedString type; DeprecatedString type;
DeprecatedString subtype; DeprecatedString subtype;
Optional<DeprecatedString> id {}; DeprecatedString id {};
Optional<DeprecatedString> desc {}; DeprecatedString desc {};
DeprecatedString encoding; DeprecatedString encoding;
HashMap<DeprecatedString, DeprecatedString> fields; HashMap<DeprecatedString, DeprecatedString> fields;
unsigned bytes { 0 }; unsigned bytes { 0 };
unsigned lines { 0 }; unsigned lines { 0 };
Optional<Tuple<Envelope, NonnullOwnPtr<BodyStructure>>> contanied_message; Optional<Tuple<Envelope, NonnullOwnPtr<BodyStructure>>> contanied_message;
Optional<DeprecatedString> md5 {}; DeprecatedString md5 {};
Optional<Tuple<DeprecatedString, HashMap<DeprecatedString, DeprecatedString>>> disposition {}; Optional<Tuple<DeprecatedString, HashMap<DeprecatedString, DeprecatedString>>> disposition {};
Optional<Vector<DeprecatedString>> langs {}; Optional<Vector<DeprecatedString>> langs {};
Optional<DeprecatedString> location {}; DeprecatedString location {};
Optional<Vector<BodyExtension>> extensions {}; Vector<BodyExtension> extensions {};
}; };
class BodyStructure { class BodyStructure {
@ -332,13 +332,13 @@ public:
m_response_type |= static_cast<unsigned>(type); m_response_type |= static_cast<unsigned>(type);
} }
void add_body_data(FetchCommand::DataItem&& data_item, Optional<DeprecatedString>&& body) void add_body_data(FetchCommand::DataItem&& data_item, DeprecatedString&& body)
{ {
add_response_type(FetchResponseType::Body); add_response_type(FetchResponseType::Body);
m_bodies.append({ move(data_item), move(body) }); m_bodies.append({ move(data_item), move(body) });
} }
Vector<Tuple<FetchCommand::DataItem, Optional<DeprecatedString>>>& body_data() Vector<Tuple<FetchCommand::DataItem, DeprecatedString>>& body_data()
{ {
VERIFY(contains_response_type(FetchResponseType::Body)); VERIFY(contains_response_type(FetchResponseType::Body));
return m_bodies; return m_bodies;
@ -411,7 +411,7 @@ public:
private: private:
Vector<DeprecatedString> m_flags; Vector<DeprecatedString> m_flags;
Vector<Tuple<FetchCommand::DataItem, Optional<DeprecatedString>>> m_bodies; Vector<Tuple<FetchCommand::DataItem, DeprecatedString>> m_bodies;
Core::DateTime m_internal_date; Core::DateTime m_internal_date;
Envelope m_envelope; Envelope m_envelope;
unsigned m_uid { 0 }; unsigned m_uid { 0 };

View file

@ -11,6 +11,16 @@
namespace IMAP { namespace IMAP {
ParseStatus Parser::parse(ByteBuffer&& buffer, bool expecting_tag) ParseStatus Parser::parse(ByteBuffer&& buffer, bool expecting_tag)
{
auto response_or_error = try_parse(move(buffer), expecting_tag);
if (response_or_error.is_error())
return { false, {} };
auto response = response_or_error.release_value();
return response;
}
ErrorOr<ParseStatus> Parser::try_parse(ByteBuffer&& buffer, bool expecting_tag)
{ {
dbgln_if(IMAP_PARSER_DEBUG, "Parser received {} bytes:\n\"{}\"", buffer.size(), StringView(buffer.data(), buffer.size())); dbgln_if(IMAP_PARSER_DEBUG, "Parser received {} bytes:\n\"{}\"", buffer.size(), StringView(buffer.data(), buffer.size()));
if (m_incomplete) { if (m_incomplete) {
@ -23,29 +33,25 @@ ParseStatus Parser::parse(ByteBuffer&& buffer, bool expecting_tag)
} }
if (consume_if("+"sv)) { if (consume_if("+"sv)) {
consume(" "sv); TRY(consume(" "sv));
auto data = consume_until_end_of_line(); auto data = consume_until_end_of_line();
consume("\r\n"sv); TRY(consume(" "sv));
return { true, { ContinueRequest { data } } }; return ParseStatus { true, { ContinueRequest { data } } };
} }
while (consume_if("*"sv)) { while (consume_if("*"sv)) {
parse_untagged(); TRY(parse_untagged());
} }
if (expecting_tag) { if (expecting_tag) {
if (at_end()) { if (at_end()) {
m_incomplete = true; m_incomplete = true;
return { true, {} }; return ParseStatus { true, {} };
} }
parse_response_done(); TRY(parse_response_done());
} }
if (m_parsing_failed) { return ParseStatus { true, { move(m_response) } };
return { false, {} };
} else {
return { true, { { move(m_response) } } };
}
} }
bool Parser::consume_if(StringView x) bool Parser::consume_if(StringView x)
@ -68,14 +74,14 @@ bool Parser::consume_if(StringView x)
return true; return true;
} }
void Parser::parse_response_done() ErrorOr<void> Parser::parse_response_done()
{ {
consume("A"sv); TRY(consume("A"sv));
auto tag = MUST(parse_number()); auto tag = TRY(parse_number());
consume(" "sv); TRY(consume(" "sv));
ResponseStatus status = parse_status(); ResponseStatus status = TRY(parse_status());
consume(" "sv); TRY(consume(" "sv));
m_response.m_tag = tag; m_response.m_tag = tag;
m_response.m_status = status; m_response.m_status = status;
@ -87,17 +93,19 @@ void Parser::parse_response_done()
m_position += 1; m_position += 1;
} }
consume("\r\n"sv); TRY(consume("\r\n"sv));
m_response.m_response_text = response_data.to_deprecated_string(); m_response.m_response_text = response_data.to_deprecated_string();
return {};
} }
void Parser::consume(StringView x) ErrorOr<void> Parser::consume(StringView x)
{ {
if (!consume_if(x)) { if (!consume_if(x)) {
dbgln("\"{}\" not matched at {}, (buffer length {})", x, m_position, m_buffer.size()); dbgln("\"{}\" not matched at {}, (buffer length {})", x, m_position, m_buffer.size());
return Error::from_string_literal("Token not matched");
m_parsing_failed = true;
} }
return {};
} }
Optional<unsigned> Parser::try_parse_number() Optional<unsigned> Parser::try_parse_number()
@ -123,108 +131,108 @@ ErrorOr<unsigned> Parser::parse_number()
{ {
auto number = try_parse_number(); auto number = try_parse_number();
if (!number.has_value()) { if (!number.has_value()) {
m_parsing_failed = true; dbgln("Failed to parse number at {}, (buffer length {})", m_position, m_buffer.size());
return Error::from_string_view("Failed to parse expected number"sv); return Error::from_string_view("Failed to parse expected number"sv);
} }
return number.value(); return number.value();
} }
void Parser::parse_untagged() ErrorOr<void> Parser::parse_untagged()
{ {
consume(" "sv); TRY(consume(" "sv));
// Certain messages begin with a number like: // Certain messages begin with a number like:
// * 15 EXISTS // * 15 EXISTS
auto number = try_parse_number(); auto number = try_parse_number();
if (number.has_value()) { if (number.has_value()) {
consume(" "sv); TRY(consume(" "sv));
auto data_type = parse_atom(); auto data_type = parse_atom();
if (data_type == "EXISTS"sv) { if (data_type == "EXISTS"sv) {
m_response.data().set_exists(number.value()); m_response.data().set_exists(number.value());
consume("\r\n"sv); TRY(consume("\r\n"sv));
} else if (data_type == "RECENT"sv) { } else if (data_type == "RECENT"sv) {
m_response.data().set_recent(number.value()); m_response.data().set_recent(number.value());
consume("\r\n"sv); TRY(consume("\r\n"sv));
} else if (data_type == "FETCH"sv) { } else if (data_type == "FETCH"sv) {
auto fetch_response = parse_fetch_response(); auto fetch_response = TRY(parse_fetch_response());
m_response.data().add_fetch_response(number.value(), move(fetch_response)); m_response.data().add_fetch_response(number.value(), move(fetch_response));
} else if (data_type == "EXPUNGE"sv) { } else if (data_type == "EXPUNGE"sv) {
m_response.data().add_expunged(number.value()); m_response.data().add_expunged(number.value());
consume("\r\n"sv); TRY(consume("\r\n"sv));
} }
return; return {};
} }
if (consume_if("CAPABILITY"sv)) { if (consume_if("CAPABILITY"sv)) {
parse_capability_response(); TRY(parse_capability_response());
} else if (consume_if("LIST"sv)) { } else if (consume_if("LIST"sv)) {
auto item = parse_list_item(); auto item = TRY(parse_list_item());
m_response.data().add_list_item(move(item)); m_response.data().add_list_item(move(item));
} else if (consume_if("LSUB"sv)) { } else if (consume_if("LSUB"sv)) {
auto item = parse_list_item(); auto item = TRY(parse_list_item());
m_response.data().add_lsub_item(move(item)); m_response.data().add_lsub_item(move(item));
} else if (consume_if("FLAGS"sv)) { } else if (consume_if("FLAGS"sv)) {
consume(" "sv); TRY(consume(" "sv));
auto flags = parse_list(+[](StringView x) { return DeprecatedString(x); }); auto flags = TRY(parse_list(+[](StringView x) { return DeprecatedString(x); }));
m_response.data().set_flags(move(flags)); m_response.data().set_flags(move(flags));
consume("\r\n"sv); TRY(consume("\r\n"sv));
} else if (consume_if("OK"sv)) { } else if (consume_if("OK"sv)) {
consume(" "sv); TRY(consume(" "sv));
if (consume_if("["sv)) { if (consume_if("["sv)) {
auto actual_type = parse_atom(); auto actual_type = parse_atom();
if (actual_type == "CLOSED"sv) { if (actual_type == "CLOSED"sv) {
// No-op. // No-op.
} else if (actual_type == "UIDNEXT"sv) { } else if (actual_type == "UIDNEXT"sv) {
consume(" "sv); TRY(consume(" "sv));
auto n = MUST(parse_number()); auto n = TRY(parse_number());
m_response.data().set_uid_next(n); m_response.data().set_uid_next(n);
} else if (actual_type == "UIDVALIDITY"sv) { } else if (actual_type == "UIDVALIDITY"sv) {
consume(" "sv); TRY(consume(" "sv));
auto n = MUST(parse_number()); auto n = TRY(parse_number());
m_response.data().set_uid_validity(n); m_response.data().set_uid_validity(n);
} else if (actual_type == "UNSEEN"sv) { } else if (actual_type == "UNSEEN"sv) {
consume(" "sv); TRY(consume(" "sv));
auto n = MUST(parse_number()); auto n = TRY(parse_number());
m_response.data().set_unseen(n); m_response.data().set_unseen(n);
} else if (actual_type == "PERMANENTFLAGS"sv) { } else if (actual_type == "PERMANENTFLAGS"sv) {
consume(" "sv); TRY(consume(" "sv));
auto flags = parse_list(+[](StringView x) { return DeprecatedString(x); }); auto flags = TRY(parse_list(+[](StringView x) { return DeprecatedString(x); }));
m_response.data().set_permanent_flags(move(flags)); m_response.data().set_permanent_flags(move(flags));
} else if (actual_type == "HIGHESTMODSEQ"sv) { } else if (actual_type == "HIGHESTMODSEQ"sv) {
consume(" "sv); TRY(consume(" "sv));
MUST(parse_number()); TRY(parse_number());
// No-op for now. // No-op for now.
} else { } else {
dbgln("Unknown: {}", actual_type); dbgln("Unknown: {}", actual_type);
consume_while([](u8 x) { return x != ']'; }); consume_while([](u8 x) { return x != ']'; });
} }
consume("]"sv); TRY(consume("]"sv));
} }
consume_until_end_of_line(); consume_until_end_of_line();
consume("\r\n"sv); TRY(consume("\r\n"sv));
} else if (consume_if("SEARCH"sv)) { } else if (consume_if("SEARCH"sv)) {
Vector<unsigned> ids; Vector<unsigned> ids;
while (!consume_if("\r\n"sv)) { while (!consume_if("\r\n"sv)) {
consume(" "sv); TRY(consume(" "sv));
auto id = MUST(parse_number()); auto id = TRY(parse_number());
ids.append(id); ids.append(id);
} }
m_response.data().set_search_results(move(ids)); m_response.data().set_search_results(move(ids));
} else if (consume_if("BYE"sv)) { } else if (consume_if("BYE"sv)) {
auto message = consume_until_end_of_line(); auto message = consume_until_end_of_line();
consume("\r\n"sv); TRY(consume("\r\n"sv));
m_response.data().set_bye(message.is_empty() ? Optional<DeprecatedString>() : Optional<DeprecatedString>(message)); m_response.data().set_bye(message.is_empty() ? Optional<DeprecatedString>() : Optional<DeprecatedString>(message));
} else if (consume_if("STATUS"sv)) { } else if (consume_if("STATUS"sv)) {
consume(" "sv); TRY(consume(" "sv));
auto mailbox = parse_astring(); auto mailbox = TRY(parse_astring());
consume(" ("sv); TRY(consume(" ("sv));
auto status_item = StatusItem(); auto status_item = StatusItem();
status_item.set_mailbox(mailbox); status_item.set_mailbox(mailbox);
while (!consume_if(")"sv)) { while (!consume_if(")"sv)) {
auto status_att = parse_atom(); auto status_att = parse_atom();
consume(" "sv); TRY(consume(" "sv));
auto value = MUST(parse_number()); auto value = TRY(parse_number());
auto type = StatusItemType::Recent; auto type = StatusItemType::Recent;
if (status_att == "MESSAGES"sv) { if (status_att == "MESSAGES"sv) {
@ -239,177 +247,179 @@ void Parser::parse_untagged()
type = StatusItemType::Recent; type = StatusItemType::Recent;
} else { } else {
dbgln("Unmatched status attribute: {}", status_att); dbgln("Unmatched status attribute: {}", status_att);
m_parsing_failed = true; return Error::from_string_literal("Failed to parse status attribute");
} }
status_item.set(type, value); status_item.set(type, value);
if (!at_end() && m_buffer[m_position] != ')') if (!at_end() && m_buffer[m_position] != ')')
consume(" "sv); TRY(consume(" "sv));
} }
m_response.data().set_status(move(status_item)); m_response.data().set_status(move(status_item));
consume_if(" "sv); // Not in the spec but the Outlook server sends a space for some reason. consume_if(" "sv); // Not in the spec but the Outlook server sends a space for some reason.
consume("\r\n"sv); TRY(consume("\r\n"sv));
} else { } else {
auto x = consume_until_end_of_line(); auto x = consume_until_end_of_line();
consume("\r\n"sv); TRY(consume("\r\n"sv));
dbgln("ignored {}", x); dbgln("ignored {}", x);
} }
return {};
} }
StringView Parser::parse_quoted_string() ErrorOr<StringView> Parser::parse_quoted_string()
{ {
dbgln_if(IMAP_PARSER_DEBUG, "p: {}, parse_quoted_string()", m_position); dbgln_if(IMAP_PARSER_DEBUG, "p: {}, parse_quoted_string()", m_position);
auto str = consume_while([](u8 x) { return x != '"'; }); auto str = consume_while([](u8 x) { return x != '"'; });
consume("\""sv); TRY(consume("\""sv));
dbgln_if(IMAP_PARSER_DEBUG, "p: {}, ret \"{}\"", m_position, str); dbgln_if(IMAP_PARSER_DEBUG, "p: {}, ret \"{}\"", m_position, str);
return str; return str;
} }
StringView Parser::parse_string() ErrorOr<StringView> Parser::parse_string()
{ {
if (consume_if("\""sv)) { if (consume_if("\""sv))
return parse_quoted_string(); return parse_quoted_string();
} else {
return parse_literal_string(); return parse_literal_string();
}
} }
Optional<StringView> Parser::parse_nstring() ErrorOr<StringView> Parser::parse_nstring()
{ {
dbgln_if(IMAP_PARSER_DEBUG, "p: {} parse_nstring()", m_position); dbgln_if(IMAP_PARSER_DEBUG, "p: {} parse_nstring()", m_position);
if (consume_if("NIL"sv)) if (consume_if("NIL"sv))
return {}; return StringView {};
else
return { parse_string() }; return { TRY(parse_string()) };
} }
FetchResponseData Parser::parse_fetch_response() ErrorOr<FetchResponseData> Parser::parse_fetch_response()
{ {
consume(" ("sv); TRY(consume(" ("sv));
auto fetch_response = FetchResponseData(); auto fetch_response = FetchResponseData();
while (!consume_if(")"sv)) { while (!consume_if(")"sv)) {
auto data_item = parse_fetch_data_item(); auto data_item = TRY(parse_fetch_data_item());
switch (data_item.type) { switch (data_item.type) {
case FetchCommand::DataItemType::BodyStructure: { case FetchCommand::DataItemType::BodyStructure: {
consume(" ("sv); TRY(consume(" ("sv));
auto structure = parse_body_structure(); auto structure = TRY(parse_body_structure());
fetch_response.set_body_structure(move(structure)); fetch_response.set_body_structure(move(structure));
break; break;
} }
case FetchCommand::DataItemType::Envelope: { case FetchCommand::DataItemType::Envelope: {
consume(" "sv); TRY(consume(" "sv));
fetch_response.set_envelope(parse_envelope()); fetch_response.set_envelope(TRY(parse_envelope()));
break; break;
} }
case FetchCommand::DataItemType::Flags: { case FetchCommand::DataItemType::Flags: {
consume(" "sv); TRY(consume(" "sv));
auto flags = parse_list(+[](StringView x) { return DeprecatedString(x); }); auto flags = TRY(parse_list(+[](StringView x) { return DeprecatedString(x); }));
fetch_response.set_flags(move(flags)); fetch_response.set_flags(move(flags));
break; break;
} }
case FetchCommand::DataItemType::InternalDate: { case FetchCommand::DataItemType::InternalDate: {
consume(" \""sv); TRY(consume(" \""sv));
auto date_view = consume_while([](u8 x) { return x != '"'; }); auto date_view = consume_while([](u8 x) { return x != '"'; });
consume("\""sv); TRY(consume("\""sv));
auto date = Core::DateTime::parse("%d-%b-%Y %H:%M:%S %z"sv, date_view).value(); auto date = Core::DateTime::parse("%d-%b-%Y %H:%M:%S %z"sv, date_view).value();
fetch_response.set_internal_date(date); fetch_response.set_internal_date(date);
break; break;
} }
case FetchCommand::DataItemType::UID: { case FetchCommand::DataItemType::UID: {
consume(" "sv); TRY(consume(" "sv));
fetch_response.set_uid(MUST(parse_number())); fetch_response.set_uid(TRY(parse_number()));
break; break;
} }
case FetchCommand::DataItemType::PeekBody: case FetchCommand::DataItemType::PeekBody:
// Spec doesn't allow for this in a response. // Spec doesn't allow for this in a response.
m_parsing_failed = true; return Error::from_string_literal("Unexpected fetch command type");
break;
case FetchCommand::DataItemType::BodySection: { case FetchCommand::DataItemType::BodySection: {
auto body = parse_nstring(); auto body = TRY(parse_nstring());
fetch_response.add_body_data(move(data_item), Optional<DeprecatedString>(move(body))); fetch_response.add_body_data(move(data_item), body);
break; break;
} }
} }
if (!at_end() && m_buffer[m_position] != ')') if (!at_end() && m_buffer[m_position] != ')')
consume(" "sv); TRY(consume(" "sv));
} }
consume("\r\n"sv); TRY(consume("\r\n"sv));
return fetch_response; return fetch_response;
} }
Envelope Parser::parse_envelope()
ErrorOr<Envelope> Parser::parse_envelope()
{ {
consume("("sv); TRY(consume("("sv));
auto date = parse_nstring(); auto date = TRY(parse_nstring());
consume(" "sv); TRY(consume(" "sv));
auto subject = parse_nstring(); auto subject = TRY(parse_nstring());
consume(" "sv); TRY(consume(" "sv));
auto from = parse_address_list(); auto from = TRY(parse_address_list());
consume(" "sv); TRY(consume(" "sv));
auto sender = parse_address_list(); auto sender = TRY(parse_address_list());
consume(" "sv); TRY(consume(" "sv));
auto reply_to = parse_address_list(); auto reply_to = TRY(parse_address_list());
consume(" "sv); TRY(consume(" "sv));
auto to = parse_address_list(); auto to = TRY(parse_address_list());
consume(" "sv); TRY(consume(" "sv));
auto cc = parse_address_list(); auto cc = TRY(parse_address_list());
consume(" "sv); TRY(consume(" "sv));
auto bcc = parse_address_list(); auto bcc = TRY(parse_address_list());
consume(" "sv); TRY(consume(" "sv));
auto in_reply_to = parse_nstring(); auto in_reply_to = TRY(parse_nstring());
consume(" "sv); TRY(consume(" "sv));
auto message_id = parse_nstring(); auto message_id = TRY(parse_nstring());
consume(")"sv); TRY(consume(")"sv));
Envelope envelope = { Envelope envelope = {
date.has_value() ? AK::Optional<DeprecatedString>(date.value()) : AK::Optional<DeprecatedString>(), date,
subject.has_value() ? AK::Optional<DeprecatedString>(subject.value()) : AK::Optional<DeprecatedString>(), subject,
from, from,
sender, sender,
reply_to, reply_to,
to, to,
cc, cc,
bcc, bcc,
in_reply_to.has_value() ? AK::Optional<DeprecatedString>(in_reply_to.value()) : AK::Optional<DeprecatedString>(), in_reply_to,
message_id.has_value() ? AK::Optional<DeprecatedString>(message_id.value()) : AK::Optional<DeprecatedString>(), message_id
}; };
return envelope; return envelope;
} }
BodyStructure Parser::parse_body_structure()
ErrorOr<BodyStructure> Parser::parse_body_structure()
{ {
if (!at_end() && m_buffer[m_position] == '(') { if (!at_end() && m_buffer[m_position] == '(') {
auto data = MultiPartBodyStructureData(); auto data = MultiPartBodyStructureData();
while (consume_if("("sv)) { while (consume_if("("sv)) {
auto child = parse_body_structure(); auto child = TRY(parse_body_structure());
data.bodies.append(make<BodyStructure>(move(child))); data.bodies.append(make<BodyStructure>(move(child)));
} }
consume(" "sv); TRY(consume(" "sv));
data.multipart_subtype = parse_string(); data.multipart_subtype = TRY(parse_string());
if (!consume_if(")"sv)) { if (!consume_if(")"sv)) {
consume(" "sv); TRY(consume(" "sv));
data.params = consume_if("NIL"sv) ? Optional<HashMap<DeprecatedString, DeprecatedString>>() : parse_body_fields_params(); data.params = consume_if("NIL"sv) ? HashMap<DeprecatedString, DeprecatedString> {} : TRY(parse_body_fields_params());
if (!consume_if(")"sv)) { if (!consume_if(")"sv)) {
consume(" "sv); TRY(consume(" "sv));
if (!consume_if("NIL"sv)) { if (!consume_if("NIL"sv)) {
data.disposition = { parse_disposition() }; data.disposition = { TRY(parse_disposition()) };
} }
if (!consume_if(")"sv)) { if (!consume_if(")"sv)) {
consume(" "sv); TRY(consume(" "sv));
if (!consume_if("NIL"sv)) { if (!consume_if("NIL"sv)) {
data.langs = { parse_langs() }; data.langs = { TRY(parse_langs()) };
} }
if (!consume_if(")"sv)) { if (!consume_if(")"sv)) {
consume(" "sv); TRY(consume(" "sv));
data.location = consume_if("NIL"sv) ? Optional<DeprecatedString>() : Optional<DeprecatedString>(parse_string()); data.location = consume_if("NIL"sv) ? DeprecatedString {} : DeprecatedString(TRY(parse_string()));
if (!consume_if(")"sv)) { if (!consume_if(")"sv)) {
consume(" "sv); TRY(consume(" "sv));
Vector<BodyExtension> extensions; Vector<BodyExtension> extensions;
while (!consume_if(")"sv)) { while (!consume_if(")"sv)) {
extensions.append(parse_body_extension()); extensions.append(TRY(parse_body_extension()));
consume_if(" "sv); consume_if(" "sv);
} }
data.extensions = { move(extensions) }; data.extensions = { move(extensions) };
@ -420,127 +430,129 @@ BodyStructure Parser::parse_body_structure()
} }
return BodyStructure(move(data)); return BodyStructure(move(data));
} else {
return parse_one_part_body();
} }
return parse_one_part_body();
} }
// body-type-1part // body-type-1part
BodyStructure Parser::parse_one_part_body() ErrorOr<BodyStructure> Parser::parse_one_part_body()
{ {
// NOTE: We share common parts between body-type-basic, body-type-msg and body-type-text types for readability. // NOTE: We share common parts between body-type-basic, body-type-msg and body-type-text types for readability.
BodyStructureData data; BodyStructureData data;
// media-basic / media-message / media-text // media-basic / media-message / media-text
data.type = parse_string(); data.type = TRY(parse_string());
consume(" "sv); TRY(consume(" "sv));
data.subtype = parse_string(); data.subtype = TRY(parse_string());
consume(" "sv); TRY(consume(" "sv));
// body-fields // body-fields
data.fields = parse_body_fields_params(); data.fields = TRY(parse_body_fields_params());
consume(" "sv); TRY(consume(" "sv));
data.id = Optional<DeprecatedString>(parse_nstring()); data.id = TRY(parse_nstring());
consume(" "sv); TRY(consume(" "sv));
data.desc = Optional<DeprecatedString>(parse_nstring()); data.desc = TRY(parse_nstring());
consume(" "sv); TRY(consume(" "sv));
data.encoding = parse_string(); data.encoding = TRY(parse_string());
consume(" "sv); TRY(consume(" "sv));
data.bytes = MUST(parse_number()); data.bytes = TRY(parse_number());
if (data.type.equals_ignoring_ascii_case("TEXT"sv)) { if (data.type.equals_ignoring_ascii_case("TEXT"sv)) {
// body-type-text // body-type-text
// NOTE: "media-text SP body-fields" part is already parsed. // NOTE: "media-text SP body-fields" part is already parsed.
consume(" "sv); TRY(consume(" "sv));
data.lines = MUST(parse_number()); data.lines = TRY(parse_number());
} else if (data.type.equals_ignoring_ascii_case("MESSAGE"sv) && data.subtype.is_one_of_ignoring_ascii_case("RFC822"sv, "GLOBAL"sv)) { } else if (data.type.equals_ignoring_ascii_case("MESSAGE"sv) && data.subtype.is_one_of_ignoring_ascii_case("RFC822"sv, "GLOBAL"sv)) {
// body-type-msg // body-type-msg
// NOTE: "media-message SP body-fields" part is already parsed. // NOTE: "media-message SP body-fields" part is already parsed.
consume(" "sv); TRY(consume(" "sv));
auto envelope = parse_envelope(); auto envelope = TRY(parse_envelope());
consume(" ("sv); TRY(consume(" ("sv));
auto body = parse_body_structure(); auto body = TRY(parse_body_structure());
data.contanied_message = Tuple { move(envelope), make<BodyStructure>(move(body)) }; data.contanied_message = Tuple { move(envelope), make<BodyStructure>(move(body)) };
consume(" "sv); TRY(consume(" "sv));
data.lines = MUST(parse_number()); data.lines = TRY(parse_number());
} else { } else {
// body-type-basic // body-type-basic
// NOTE: "media-basic SP body-fields" is already parsed. // NOTE: "media-basic SP body-fields" is already parsed.
} }
if (!consume_if(")"sv)) { if (!consume_if(")"sv)) {
consume(" "sv); TRY(consume(" "sv));
// body-ext-1part // body-ext-1part
[&]() { TRY([&]() -> ErrorOr<void> {
data.md5 = Optional<DeprecatedString>(parse_nstring()); data.md5 = TRY(parse_nstring());
if (consume_if(")"sv)) if (consume_if(")"sv))
return; return {};
consume(" "sv); TRY(consume(" "sv));
if (!consume_if("NIL"sv)) { if (!consume_if("NIL"sv)) {
auto disposition = parse_disposition(); data.disposition = { TRY(parse_disposition()) };
data.disposition = { disposition };
} }
if (consume_if(")"sv)) if (consume_if(")"sv))
return; return {};
consume(" "sv); TRY(consume(" "sv));
if (!consume_if("NIL"sv)) { if (!consume_if("NIL"sv)) {
data.langs = { parse_langs() }; data.langs = { TRY(parse_langs()) };
} }
if (consume_if(")"sv)) if (consume_if(")"sv))
return; return {};
consume(" "sv); TRY(consume(" "sv));
data.location = Optional<DeprecatedString>(parse_nstring()); data.location = TRY(parse_nstring());
Vector<BodyExtension> extensions; Vector<BodyExtension> extensions;
while (!consume_if(")"sv)) { while (!consume_if(")"sv)) {
extensions.append(parse_body_extension()); extensions.append(TRY(parse_body_extension()));
consume_if(" "sv); consume_if(" "sv);
} }
data.extensions = { move(extensions) }; data.extensions = { move(extensions) };
}(); return {};
}());
} }
return BodyStructure(move(data)); return BodyStructure(move(data));
} }
Vector<DeprecatedString> Parser::parse_langs()
ErrorOr<Vector<DeprecatedString>> Parser::parse_langs()
{ {
AK::Vector<DeprecatedString> langs; AK::Vector<DeprecatedString> langs;
if (!consume_if("("sv)) { if (!consume_if("("sv)) {
langs.append(parse_string()); langs.append(TRY(parse_string()));
} else { } else {
while (!consume_if(")"sv)) { while (!consume_if(")"sv)) {
langs.append(parse_string()); langs.append(TRY(parse_string()));
consume_if(" "sv); consume_if(" "sv);
} }
} }
return langs; return langs;
} }
Tuple<DeprecatedString, HashMap<DeprecatedString, DeprecatedString>> Parser::parse_disposition()
ErrorOr<Tuple<DeprecatedString, HashMap<DeprecatedString, DeprecatedString>>> Parser::parse_disposition()
{ {
consume("("sv); TRY(consume("("sv));
auto disposition_type = parse_string(); auto disposition_type = TRY(parse_string());
consume(" "sv); TRY(consume(" "sv));
auto disposition_vals = parse_body_fields_params(); auto disposition_vals = TRY(parse_body_fields_params());
consume(")"sv); TRY(consume(")"sv));
return { move(disposition_type), move(disposition_vals) }; return Tuple<DeprecatedString, HashMap<DeprecatedString, DeprecatedString>> { move(disposition_type), move(disposition_vals) };
} }
StringView Parser::parse_literal_string() ErrorOr<StringView> Parser::parse_literal_string()
{ {
dbgln_if(IMAP_PARSER_DEBUG, "p: {}, parse_literal_string()", m_position); dbgln_if(IMAP_PARSER_DEBUG, "p: {}, parse_literal_string()", m_position);
consume("{"sv); TRY(consume("{"sv));
auto num_bytes = MUST(parse_number()); auto num_bytes = TRY(parse_number());
consume("}\r\n"sv); TRY(consume("}\r\n"sv));
if (m_buffer.size() < m_position + num_bytes) { if (m_buffer.size() < m_position + num_bytes) {
m_parsing_failed = true; dbgln("Attempted to parse string with length: {} at position {} (buffer length {})", num_bytes, m_position, m_position);
return ""sv; return Error::from_string_literal("Failed to parse string");
} }
m_position += num_bytes; m_position += num_bytes;
@ -549,31 +561,32 @@ StringView Parser::parse_literal_string()
return s; return s;
} }
ListItem Parser::parse_list_item() ErrorOr<ListItem> Parser::parse_list_item()
{ {
consume(" "sv); TRY(consume(" "sv));
auto flags_vec = parse_list(parse_mailbox_flag); auto flags_vec = TRY(parse_list(parse_mailbox_flag));
unsigned flags = 0; unsigned flags = 0;
for (auto flag : flags_vec) { for (auto flag : flags_vec) {
flags |= static_cast<unsigned>(flag); flags |= static_cast<unsigned>(flag);
} }
consume(" \""sv); TRY(consume(" \""sv));
auto reference = consume_while([](u8 x) { return x != '"'; }); auto reference = consume_while([](u8 x) { return x != '"'; });
consume("\" "sv); TRY(consume("\" "sv));
auto mailbox = parse_astring(); auto mailbox = TRY(parse_astring());
consume("\r\n"sv); TRY(consume("\r\n"sv));
return ListItem { flags, DeprecatedString(reference), DeprecatedString(mailbox) }; return ListItem { flags, DeprecatedString(reference), DeprecatedString(mailbox) };
} }
void Parser::parse_capability_response() ErrorOr<void> Parser::parse_capability_response()
{ {
auto capability = AK::Vector<DeprecatedString>(); auto capability = AK::Vector<DeprecatedString>();
while (!consume_if("\r\n"sv)) { while (!consume_if("\r\n"sv)) {
consume(" "sv); TRY(consume(" "sv));
auto x = DeprecatedString(parse_atom()); auto x = DeprecatedString(parse_atom());
capability.append(x); capability.append(x);
} }
m_response.data().add_capabilities(move(capability)); m_response.data().add_capabilities(move(capability));
return {};
} }
StringView Parser::parse_atom() StringView Parser::parse_atom()
@ -596,7 +609,7 @@ StringView Parser::parse_atom()
return s; return s;
} }
ResponseStatus Parser::parse_status() ErrorOr<ResponseStatus> Parser::parse_status()
{ {
auto atom = parse_atom(); auto atom = parse_atom();
@ -608,19 +621,19 @@ ResponseStatus Parser::parse_status()
return ResponseStatus::No; return ResponseStatus::No;
} }
m_parsing_failed = true; dbgln("Invalid ResponseStatus value: {}", atom);
return ResponseStatus::Bad; return Error::from_string_literal("Failed to parse status type");
} }
template<typename T> template<typename T>
Vector<T> Parser::parse_list(T converter(StringView)) ErrorOr<Vector<T>> Parser::parse_list(T converter(StringView))
{ {
consume("("sv); TRY(consume("("sv));
Vector<T> x; Vector<T> x;
bool first = true; bool first = true;
while (!consume_if(")"sv)) { while (!consume_if(")"sv)) {
if (!first) if (!first)
consume(" "sv); TRY(consume(" "sv));
auto item = consume_while([](u8 x) { auto item = consume_while([](u8 x) {
return x != ' ' && x != ')'; return x != ' ' && x != ')';
}); });
@ -683,7 +696,7 @@ StringView Parser::consume_until_end_of_line()
return consume_while([](u8 x) { return x != '\r'; }); return consume_while([](u8 x) { return x != '\r'; });
} }
FetchCommand::DataItem Parser::parse_fetch_data_item() ErrorOr<FetchCommand::DataItem> Parser::parse_fetch_data_item()
{ {
auto msg_attr = consume_while([](u8 x) { return is_ascii_alpha(x) != 0; }); auto msg_attr = consume_while([](u8 x) { return is_ascii_alpha(x) != 0; });
if (msg_attr.equals_ignoring_ascii_case("BODY"sv) && consume_if("["sv)) { if (msg_attr.equals_ignoring_ascii_case("BODY"sv) && consume_if("["sv)) {
@ -695,21 +708,21 @@ FetchCommand::DataItem Parser::parse_fetch_data_item()
if (section_type.equals_ignoring_ascii_case("HEADER.FIELDS"sv)) { if (section_type.equals_ignoring_ascii_case("HEADER.FIELDS"sv)) {
data_item.section->type = FetchCommand::DataItem::SectionType::HeaderFields; data_item.section->type = FetchCommand::DataItem::SectionType::HeaderFields;
data_item.section->headers = Vector<DeprecatedString>(); data_item.section->headers = Vector<DeprecatedString>();
consume(" "sv); TRY(consume(" "sv));
auto headers = parse_list(+[](StringView x) { return x; }); auto headers = TRY(parse_list(+[](StringView x) { return x; }));
for (auto& header : headers) { for (auto& header : headers) {
data_item.section->headers->append(header); data_item.section->headers->append(header);
} }
consume("]"sv); TRY(consume("]"sv));
} else if (section_type.equals_ignoring_ascii_case("HEADER.FIELDS.NOT"sv)) { } else if (section_type.equals_ignoring_ascii_case("HEADER.FIELDS.NOT"sv)) {
data_item.section->type = FetchCommand::DataItem::SectionType::HeaderFieldsNot; data_item.section->type = FetchCommand::DataItem::SectionType::HeaderFieldsNot;
data_item.section->headers = Vector<DeprecatedString>(); data_item.section->headers = Vector<DeprecatedString>();
consume(" ("sv); TRY(consume(" ("sv));
auto headers = parse_list(+[](StringView x) { return x; }); auto headers = TRY(parse_list(+[](StringView x) { return x; }));
for (auto& header : headers) { for (auto& header : headers) {
data_item.section->headers->append(header); data_item.section->headers->append(header);
} }
consume("]"sv); TRY(consume("]"sv));
} else if (is_ascii_digit(section_type[0])) { } else if (is_ascii_digit(section_type[0])) {
data_item.section->type = FetchCommand::DataItem::SectionType::Parts; data_item.section->type = FetchCommand::DataItem::SectionType::Parts;
data_item.section->parts = Vector<unsigned>(); data_item.section->parts = Vector<unsigned>();
@ -732,13 +745,13 @@ FetchCommand::DataItem Parser::parse_fetch_data_item()
data_item.section->type = FetchCommand::DataItem::SectionType::Header; data_item.section->type = FetchCommand::DataItem::SectionType::Header;
} else { } else {
dbgln("Unmatched section type {}", section_type); dbgln("Unmatched section type {}", section_type);
m_parsing_failed = true; return Error::from_string_literal("Failed to parse section type");
} }
if (consume_if("<"sv)) { if (consume_if("<"sv)) {
auto start = MUST(parse_number()); auto start = TRY(parse_number());
data_item.partial_fetch = true; data_item.partial_fetch = true;
data_item.start = (int)start; data_item.start = (int)start;
consume(">"sv); TRY(consume(">"sv));
} }
consume_if(" "sv); consume_if(" "sv);
return data_item; return data_item;
@ -764,82 +777,88 @@ FetchCommand::DataItem Parser::parse_fetch_data_item()
}; };
} else { } else {
dbgln("msg_attr not matched: {}", msg_attr); dbgln("msg_attr not matched: {}", msg_attr);
m_parsing_failed = true; return Error::from_string_literal("Failed to parse msg_attr");
return FetchCommand::DataItem {};
} }
} }
Optional<Vector<Address>> Parser::parse_address_list()
ErrorOr<Vector<Address>> Parser::parse_address_list()
{ {
if (consume_if("NIL"sv)) if (consume_if("NIL"sv))
return {}; return Vector<Address> {};
auto addresses = Vector<Address>(); auto addresses = Vector<Address>();
consume("("sv); TRY(consume("("sv));
while (!consume_if(")"sv)) { while (!consume_if(")"sv)) {
addresses.append(parse_address()); addresses.append(TRY(parse_address()));
if (!at_end() && m_buffer[m_position] != ')') if (!at_end() && m_buffer[m_position] != ')')
consume(" "sv); TRY(consume(" "sv));
} }
return { addresses }; return { addresses };
} }
Address Parser::parse_address() ErrorOr<Address> Parser::parse_address()
{ {
consume("("sv); TRY(consume("("sv));
auto address = Address(); auto address = Address();
auto name = parse_nstring(); auto name = TRY(parse_nstring());
address.name = Optional<DeprecatedString>(move(name)); address.name = name;
consume(" "sv); TRY(consume(" "sv));
auto source_route = parse_nstring(); auto source_route = TRY(parse_nstring());
address.source_route = Optional<DeprecatedString>(move(source_route)); address.source_route = source_route;
consume(" "sv); TRY(consume(" "sv));
auto mailbox = parse_nstring(); auto mailbox = TRY(parse_nstring());
address.mailbox = Optional<DeprecatedString>(move(mailbox)); address.mailbox = mailbox;
consume(" "sv); TRY(consume(" "sv));
auto host = parse_nstring(); auto host = TRY(parse_nstring());
address.host = Optional<DeprecatedString>(move(host)); address.host = host;
consume(")"sv); TRY(consume(")"sv));
return address; return address;
} }
StringView Parser::parse_astring()
ErrorOr<StringView> Parser::parse_astring()
{ {
if (!at_end() && (m_buffer[m_position] == '{' || m_buffer[m_position] == '"')) if (!at_end() && (m_buffer[m_position] == '{' || m_buffer[m_position] == '"'))
return parse_string(); return parse_string();
else
return parse_atom(); return parse_atom();
} }
HashMap<DeprecatedString, DeprecatedString> Parser::parse_body_fields_params()
ErrorOr<HashMap<DeprecatedString, DeprecatedString>> Parser::parse_body_fields_params()
{ {
if (consume_if("NIL"sv)) if (consume_if("NIL"sv))
return {}; return HashMap<DeprecatedString, DeprecatedString> {};
HashMap<DeprecatedString, DeprecatedString> fields; HashMap<DeprecatedString, DeprecatedString> fields;
consume("("sv); TRY(consume("("sv));
while (!consume_if(")"sv)) { while (!consume_if(")"sv)) {
auto key = parse_string(); auto key = TRY(parse_string());
consume(" "sv); TRY(consume(" "sv));
auto value = parse_string(); auto value = TRY(parse_string());
fields.set(key, value); fields.set(key, value);
consume_if(" "sv); consume_if(" "sv);
} }
return fields; return fields;
} }
BodyExtension Parser::parse_body_extension()
ErrorOr<BodyExtension> Parser::parse_body_extension()
{ {
if (consume_if("NIL"sv)) { if (consume_if("NIL"sv))
return BodyExtension { Optional<DeprecatedString> {} }; return BodyExtension { Optional<DeprecatedString> {} };
} else if (consume_if("("sv)) {
if (consume_if("("sv)) {
Vector<OwnPtr<BodyExtension>> extensions; Vector<OwnPtr<BodyExtension>> extensions;
while (!consume_if(")"sv)) { while (!consume_if(")"sv)) {
extensions.append(make<BodyExtension>(parse_body_extension())); extensions.append(make<BodyExtension>(TRY(parse_body_extension())));
consume_if(" "sv); consume_if(" "sv);
} }
return BodyExtension { move(extensions) }; return BodyExtension { move(extensions) };
} else if (!at_end() && (m_buffer[m_position] == '"' || m_buffer[m_position] == '{')) {
return BodyExtension { { parse_string() } };
} else {
return BodyExtension { MUST(parse_number()) };
} }
if (!at_end() && (m_buffer[m_position] == '"' || m_buffer[m_position] == '{'))
return BodyExtension { { TRY(parse_string()) } };
return BodyExtension { TRY(parse_number()) };
} }
} }

View file

@ -25,7 +25,9 @@ public:
private: private:
static MailboxFlag parse_mailbox_flag(StringView); static MailboxFlag parse_mailbox_flag(StringView);
void consume(StringView); ErrorOr<ParseStatus> try_parse(ByteBuffer&& buffer, bool expecting_tag);
ErrorOr<void> consume(StringView);
bool consume_if(StringView); bool consume_if(StringView);
StringView consume_while(Function<bool(u8)> should_consume); StringView consume_while(Function<bool(u8)> should_consume);
StringView consume_until_end_of_line(); StringView consume_until_end_of_line();
@ -35,39 +37,38 @@ private:
ErrorOr<unsigned> parse_number(); ErrorOr<unsigned> parse_number();
Optional<unsigned> try_parse_number(); Optional<unsigned> try_parse_number();
void parse_response_done(); ErrorOr<void> parse_response_done();
void parse_untagged(); ErrorOr<void> parse_untagged();
void parse_capability_response(); ErrorOr<void> parse_capability_response();
StringView parse_atom(); StringView parse_atom();
StringView parse_quoted_string(); ErrorOr<StringView> parse_quoted_string();
StringView parse_literal_string(); ErrorOr<StringView> parse_literal_string();
StringView parse_string(); ErrorOr<StringView> parse_string();
StringView parse_astring(); ErrorOr<StringView> parse_astring();
Optional<StringView> parse_nstring(); ErrorOr<StringView> parse_nstring();
ResponseStatus parse_status(); ErrorOr<ResponseStatus> parse_status();
ListItem parse_list_item(); ErrorOr<ListItem> parse_list_item();
FetchCommand::DataItem parse_fetch_data_item(); ErrorOr<FetchCommand::DataItem> parse_fetch_data_item();
FetchResponseData parse_fetch_response(); ErrorOr<FetchResponseData> parse_fetch_response();
Optional<Vector<Address>> parse_address_list(); ErrorOr<Vector<Address>> parse_address_list();
Address parse_address(); ErrorOr<Address> parse_address();
HashMap<DeprecatedString, DeprecatedString> parse_body_fields_params(); ErrorOr<HashMap<DeprecatedString, DeprecatedString>> parse_body_fields_params();
BodyStructure parse_body_structure(); ErrorOr<BodyStructure> parse_body_structure();
BodyStructure parse_one_part_body(); ErrorOr<BodyStructure> parse_one_part_body();
BodyExtension parse_body_extension(); ErrorOr<BodyExtension> parse_body_extension();
Tuple<DeprecatedString, HashMap<DeprecatedString, DeprecatedString>> parse_disposition(); ErrorOr<Tuple<DeprecatedString, HashMap<DeprecatedString, DeprecatedString>>> parse_disposition();
Vector<DeprecatedString> parse_langs(); ErrorOr<Vector<DeprecatedString>> parse_langs();
Envelope parse_envelope(); ErrorOr<Envelope> parse_envelope();
template<typename T> template<typename T>
Vector<T> parse_list(T (*converter)(StringView)); ErrorOr<Vector<T>> parse_list(T (*converter)(StringView));
// To retain state if parsing is not finished // To retain state if parsing is not finished
ByteBuffer m_buffer; ByteBuffer m_buffer;
SolidResponse m_response; SolidResponse m_response;
unsigned m_position { 0 }; unsigned m_position { 0 };
bool m_incomplete { false }; bool m_incomplete { false };
bool m_parsing_failed { false };
}; };
} }

View file

@ -125,12 +125,11 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
.first() .first()
.get<IMAP::FetchResponseData>() .get<IMAP::FetchResponseData>()
.body_data() .body_data()
.find_if([](Tuple<IMAP::FetchCommand::DataItem, Optional<DeprecatedString>>& data) { .find_if([](Tuple<IMAP::FetchCommand::DataItem, DeprecatedString>& data) {
const auto data_item = data.get<0>(); const auto data_item = data.get<0>();
return data_item.section.has_value() && data_item.section->type == IMAP::FetchCommand::DataItem::SectionType::HeaderFields; return data_item.section.has_value() && data_item.section->type == IMAP::FetchCommand::DataItem::SectionType::HeaderFields;
}) })
->get<1>() ->get<1>());
.value());
} }
// FIXME: There is a discrepancy between IMAP::Sequence wanting signed ints // FIXME: There is a discrepancy between IMAP::Sequence wanting signed ints