Explorar el Código

LibIMAP: Wait for a full IMAP response before parsing

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.
Valtteri Koskivuori hace 1 año
padre
commit
34adf9eeae
Se han modificado 2 ficheros con 23 adiciones y 2 borrados
  1. 22 2
      Userland/Libraries/LibIMAP/Client.cpp
  2. 1 0
      Userland/Libraries/LibIMAP/Client.h

+ 22 - 2
Userland/Libraries/LibIMAP/Client.cpp

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

+ 1 - 0
Userland/Libraries/LibIMAP/Client.h

@@ -63,6 +63,7 @@ private:
     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();