]> dgit.raspbian.org Git - dovecot.git/commitdiff
[PATCH 2/2] imap-hibernate: Use imap-parser API for parsing DONE command
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Wed, 22 Apr 2026 09:58:56 +0000 (12:58 +0300)
committerNoah Meyerhans <noahm@debian.org>
Wed, 16 Sep 2026 19:06:35 +0000 (15:06 -0400)
Replace the ad-hoc DONE/IDLE tokenizer with imap-parser.h: use
imap_parser_read_tag() to read the tag and imap_parser_read_word() to
read the DONE and IDLE keywords. This delegates bounds checks and
character validation to the shared parser.

Gbp-Pq: Name 0002-imap-hibernate-Use-imap-parser-API-for-parsing-DONE-.patch

src/imap-hibernate/imap-client.c

index 2110fe8e8e5f943902b414c77167d4524a794da3..564ef44f4f98894d966ce0bbf77e8c52a72eb8ea 100644 (file)
@@ -21,6 +21,7 @@
 #include "master-service-settings.h"
 #include "imap-keepalive.h"
 #include "imap-master-connection.h"
+#include "imap-parser.h"
 #include "imap-client.h"
 
 #include <unistd.h>
@@ -334,64 +335,78 @@ static void imap_client_move_back(struct imap_client *client)
 }
 
 static enum imap_client_input_state
-imap_client_input_parse(const unsigned char *data, size_t size, const char **tag_r)
+imap_client_input_parse_input(struct istream *input, struct imap_parser *parser,
+                             const char **tag_r)
 {
-       const unsigned char *tag_start, *tag_end;
-
+       const unsigned char *rest;
+       size_t rest_size;
+       const char *word, *tag;
        enum imap_client_input_state state = IMAP_CLIENT_INPUT_STATE_DONE_LF;
+       int ret;
 
-       /* skip over DONE[\r]\n */
-       if (i_memcasecmp(data, "DONE", I_MIN(size, 4)) != 0)
-               return IMAP_CLIENT_INPUT_STATE_BAD;
-       if (size <= 4)
+       /* read DONE */
+       word = imap_parser_read_word(parser);
+       if (word == NULL)
                return IMAP_CLIENT_INPUT_STATE_UNKNOWN;
-       data += 4; size -= 4;
+       if (strcasecmp(word, "DONE") != 0)
+               return IMAP_CLIENT_INPUT_STATE_BAD;
 
-       if (data[0] == '\r') {
+       /* read [\r]\n after DONE */
+       rest = i_stream_get_data(input, &rest_size);
+       if (rest_size == 0)
+               return IMAP_CLIENT_INPUT_STATE_UNKNOWN;
+       if (rest[0] == '\r') {
                state = IMAP_CLIENT_INPUT_STATE_DONE_CRLF;
-               data++; size--;
+               i_stream_skip(input, 1);
+               rest = i_stream_get_data(input, &rest_size);
+               if (rest_size == 0)
+                       return IMAP_CLIENT_INPUT_STATE_UNKNOWN;
        }
-       if (size == 0)
-               return IMAP_CLIENT_INPUT_STATE_UNKNOWN;
-       if (data[0] != '\n')
+       if (rest[0] != '\n')
                return IMAP_CLIENT_INPUT_STATE_BAD;
-       data++; size--;
-       if (size == 0)
+       i_stream_skip(input, 1);
+
+       /* optionally followed by "<tag> IDLE[\r]\n" - checking this assumes
+          that the DONE and IDLE are sent in the same IP packet, otherwise
+          we'll unnecessarily recreate the imap process and immediately resume
+          IDLE there. if this becomes an issue we could add a small delay to
+          the imap process creation and wait for the IDLE command during it. */
+       rest = i_stream_get_data(input, &rest_size);
+       if (rest_size == 0)
                return state;
 
-       tag_start = data;
-
-       /* skip over tag */
-       while(size > 0 &&
-             data[0] != ' ' &&
-             data[0] != '\r' &&
-             data[0] != '\t' &&
-             data[0] != '\0') { data++; size--; }
-
-       tag_end = data;
-
-       if (size == 0)
+       ret = imap_parser_read_tag(parser, &tag);
+       if (ret <= 0)
                return state;
-       if (data[0] != ' ')
-               return IMAP_CLIENT_INPUT_STATE_BAD;
-       data++; size--;
-
-       /* skip over IDLE[\r]\n - checking this assumes that the DONE and IDLE
-          are sent in the same IP packet, otherwise we'll unnecessarily
-          recreate the imap process and immediately resume IDLE there. if this
-          becomes an issue we could add a small delay to the imap process
-          creation and wait for the IDLE command during it. */
-       if (size <= 4 || i_memcasecmp(data, "IDLE", 4) != 0)
+
+       /* read IDLE */
+       word = imap_parser_read_word(parser);
+       if (word == NULL || strcasecmp(word, "IDLE") != 0)
                return state;
-       data += 4; size -= 4;
 
-       if (data[0] == '\r') {
-               data++; size--;
-       }
-       if (size == 1 && data[0] == '\n') {
-               *tag_r = t_strdup_until(tag_start, tag_end);
-               return IMAP_CLIENT_INPUT_STATE_DONEIDLE;
+       /* require [\r]\n and nothing else left */
+       rest = i_stream_get_data(input, &rest_size);
+       if (rest_size > 0 && rest[0] == '\r') {
+               i_stream_skip(input, 1);
+               rest = i_stream_get_data(input, &rest_size);
        }
+       if (rest_size != 1 || rest[0] != '\n')
+               return state;
+
+       *tag_r = t_strdup(tag);
+       return IMAP_CLIENT_INPUT_STATE_DONEIDLE;
+}
+
+static enum imap_client_input_state
+imap_client_input_parse(const unsigned char *data, size_t size, const char **tag_r)
+{
+       struct istream *input = i_stream_create_from_data(data, size);
+       struct imap_parser *parser =
+               imap_parser_create(input, NULL, size, NULL);
+       enum imap_client_input_state state =
+               imap_client_input_parse_input(input, parser, tag_r);
+       imap_parser_unref(&parser);
+       i_stream_unref(&input);
        return state;
 }