From: Timo Sirainen Date: Wed, 22 Apr 2026 09:58:56 +0000 (+0300) Subject: [PATCH 2/2] imap-hibernate: Use imap-parser API for parsing DONE command X-Git-Tag: archive/raspbian/1%2.4.1+dfsg1-6+rpi1+deb13u7^2~42 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=bffc0a8a2798c83b925f8f93331b963091971c80;p=dovecot.git [PATCH 2/2] imap-hibernate: Use imap-parser API for parsing DONE command 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 --- diff --git a/src/imap-hibernate/imap-client.c b/src/imap-hibernate/imap-client.c index 2110fe8..564ef44 100644 --- a/src/imap-hibernate/imap-client.c +++ b/src/imap-hibernate/imap-client.c @@ -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 @@ -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 " 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; }