Skip to content

Commit

Permalink
LibIMAP: Wait for a full IMAP response before parsing
Browse files Browse the repository at this point in the history
We're now sniffing the incoming data to verify the server has sent a
full response, instead of passing partial data to our IMAP parser.
Our parser really can't handle partial input very well, so for the time
being, this heuristic does a much better job of verifying we have full
response before parsing.
It doesn't yet handle unprompted untagged reponses, nor the
"continuation request" responses that start with a `+`, but we don't
fully support those yet.
  • Loading branch information
vkoskiv authored and ADKaster committed Aug 12, 2023
1 parent 0b7e181 commit 34adf9e
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 2 deletions.
24 changes: 22 additions & 2 deletions Userland/Libraries/LibIMAP/Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,26 @@ ErrorOr<NonnullOwnPtr<Client>> Client::connect_plaintext(StringView host, u16 po
return adopt_nonnull_own_or_enomem(new (nothrow) Client(host, port, move(socket)));
}

bool Client::verify_response_is_complete()
{
// FIXME: This is still more of a heuristic than a proper approach.
// I would imagine this breaks if we happen to get an email that
// contains this pattern we're looking for.
dbgln("Waiting for a complete IMAP response, buffer size is now {}", m_buffer.size());
Vector<StringView> statuses = { "OK"sv, "BAD"sv, "NO"sv };
auto slice_size = m_buffer.size() >= 100 ? 100 : m_buffer.size(); // Arbitrary slice size, should contain what we're looking for.
auto slice_data = MUST(m_buffer.slice(m_buffer.size() - slice_size, slice_size));
StringView slice = StringView(slice_data);
for (auto status : statuses) {
DeprecatedString pattern = DeprecatedString::formatted("A{} {}", m_current_command, status);
if (slice.contains(pattern)) {
dbgln("IMAP server replied {}, sending to parser", pattern);
return true;
}
}
return false;
}

ErrorOr<void> Client::on_ready_to_receive()
{
if (!TRY(m_socket->can_read_without_blocking()))
Expand All @@ -70,8 +90,8 @@ ErrorOr<void> Client::on_ready_to_receive()
return {};
}

if (m_buffer[m_buffer.size() - 1] == '\n') {
// Don't try parsing until we have a complete line.
// Don't try parsing until we have a complete response.
if (verify_response_is_complete()) {
auto response = m_parser.parse(move(m_buffer), m_expecting_response);
TRY(handle_parsed_response(move(response)));
m_buffer.clear();
Expand Down
1 change: 1 addition & 0 deletions Userland/Libraries/LibIMAP/Client.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class Client {
void setup_callbacks();

ErrorOr<void> on_ready_to_receive();
bool verify_response_is_complete();

ErrorOr<void> handle_parsed_response(ParseStatus&& parse_status);
ErrorOr<void> send_next_command();
Expand Down

0 comments on commit 34adf9e

Please sign in to comment.