]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-imap: Add imap_parser_read_tag() and _read_command_name()
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 17 Aug 2020 14:32:11 +0000 (17:32 +0300)
committerTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 4 Jan 2021 22:59:38 +0000 (00:59 +0200)
src/lib-imap/imap-parser.c
src/lib-imap/imap-parser.h
src/lib-imap/test-imap-parser.c

index b6c6e63fb1a6405b1abafcfa508737e07776cf19..52d79282fa1ff6a04060ca5614342d27a4d7f9d0 100644 (file)
@@ -947,3 +947,70 @@ const char *imap_parser_read_word(struct imap_parser *parser)
                return NULL;
        }
 }
+
+static int
+imap_parser_read_next_atom(struct imap_parser *parser, bool parsing_tag,
+                          const char **atom_r)
+{
+       const unsigned char *data;
+       size_t i, data_size;
+
+       data = i_stream_get_data(parser->input, &data_size);
+
+       /*
+          tag            = 1*<any ASTRING-CHAR except "+">
+          ASTRING-CHAR   = ATOM-CHAR / resp-specials
+          ATOM-CHAR      = <any CHAR except atom-specials>
+
+          x-command      = "X" atom <experimental command arguments>
+          atom           = 1*ATOM-CHAR
+       */
+       for (i = 0; i < data_size; i++) {
+               /* explicitly check for atom-specials, because
+                  IS_ATOM_PARSER_INPUT() allows some atom-specials */
+               switch (data[i]) {
+               case ' ':
+               case '\r':
+               case '\n':
+                       data_size = i + (data[i] == ' ' ? 1 : 0);
+                       parser->line_size += data_size;
+                       i_stream_skip(parser->input, data_size);
+                       *atom_r = p_strndup(parser->pool, data, i);
+                       /* don't allow empty string */
+                       return i == 0 ? -1 : 1;
+               /* atom-specials: */
+               case '(':
+               case ')':
+               case '{':
+               /* list-wildcards: */
+               case '%':
+               case '*':
+               /* quoted-specials: */
+               case '"':
+               case '\\':
+               /* resp-specials: */
+               case ']':
+                       return -1;
+               case '+':
+                       if (parsing_tag)
+                               return -1;
+                       break;
+               default:
+                       if ((unsigned char)data[i] < ' ' ||
+                           (unsigned char)data[i] >= 0x80)
+                               return -1;
+               }
+       }
+       return 0;
+}
+
+int imap_parser_read_tag(struct imap_parser *parser, const char **tag_r)
+{
+       return imap_parser_read_next_atom(parser, TRUE, tag_r);
+}
+
+int imap_parser_read_command_name(struct imap_parser *parser,
+                                 const char **name_r)
+{
+       return imap_parser_read_next_atom(parser, FALSE, name_r);
+}
index e5d01c17f2bbeb593c2c07bf209af4e8358b63f3..5e09d61d2b2d15fc66618de69815162bd7c14d2b 100644 (file)
@@ -101,5 +101,12 @@ int imap_parser_finish_line(struct imap_parser *parser, unsigned int count,
 /* Read one word - used for reading tag and command name.
    Returns NULL if more data is needed. */
 const char *imap_parser_read_word(struct imap_parser *parser);
+/* Read command tag. Returns 1 if tag was returned, 0 if more data is needed,
+   -1 if input isn't a valid tag. */
+int imap_parser_read_tag(struct imap_parser *parser, const char **tag_r);
+/* Read command name. Returns 1 if command name was returned, 0 if more data is
+   needed, -1 if input isn't a valid command name string. */
+int imap_parser_read_command_name(struct imap_parser *parser,
+                                 const char **name_r);
 
 #endif
index 93ef8fd59b92ed058a77c33f6da195883ed249f0..3ca4e348584fc3a2440e014013f2b30d19707f9b 100644 (file)
@@ -79,11 +79,78 @@ static void test_imap_parser_partial_list(void)
        test_end();
 }
 
+static void test_imap_parser_read_tag_cmd(void)
+{
+       enum read_type {
+               BOTH,
+               TAG,
+               COMMAND
+       };
+       struct {
+               const char *input;
+               const char *tag;
+               int ret;
+               enum read_type type;
+       } tests[] = {
+               { "tag foo", "tag", 1, BOTH },
+               { "tag\r", "tag", 1, BOTH },
+               { "tag\rfoo", "tag", 1, BOTH },
+               { "tag\nfoo", "tag", 1, BOTH },
+               { "tag\r\nfoo", "tag", 1, BOTH },
+               { "\n", NULL, -1, BOTH },
+               { "tag", NULL, 0, BOTH },
+               { "tag\t", NULL, -1, BOTH },
+               { "tag\001", NULL, -1, BOTH },
+               { "tag\x80", NULL, -1, BOTH },
+               { "tag(", NULL, -1, BOTH },
+               { "tag)", NULL, -1, BOTH },
+               { "tag{", NULL, -1, BOTH },
+               { "tag/ ", "tag/", 1, BOTH },
+               { "tag%", NULL, -1, BOTH },
+               { "tag*", NULL, -1, BOTH },
+               { "tag\"", NULL, -1, BOTH },
+               { "tag\\", NULL, -1, BOTH },
+               { "tag+", NULL, -1, TAG },
+               { "tag+ ", "tag+", 1, COMMAND },
+       };
+       struct istream *input;
+       struct imap_parser *parser;
+       const char *atom;
+       int ret;
+
+       test_begin("imap_parser_read_tag and imap_parser_read_command_name");
+       for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) {
+               if (tests[i].type != COMMAND) {
+                       input = test_istream_create(tests[i].input);
+                       test_assert(i_stream_read(input) > 0);
+                       parser = imap_parser_create(input, NULL, 1024);
+                       ret = imap_parser_read_tag(parser, &atom);
+                       test_assert_idx(ret == tests[i].ret, i);
+                       test_assert_idx(ret <= 0 || strcmp(tests[i].tag, atom) == 0, i);
+                       imap_parser_unref(&parser);
+                       i_stream_destroy(&input);
+               }
+
+               if (tests[i].type != TAG) {
+                       input = test_istream_create(tests[i].input);
+                       test_assert(i_stream_read(input) > 0);
+                       parser = imap_parser_create(input, NULL, 1024);
+                       ret = imap_parser_read_command_name(parser, &atom);
+                       test_assert_idx(ret == tests[i].ret, i);
+                       test_assert_idx(ret <= 0 || strcmp(tests[i].tag, atom) == 0, i);
+                       imap_parser_unref(&parser);
+                       i_stream_destroy(&input);
+               }
+       }
+       test_end();
+}
+
 int main(void)
 {
        static void (*const test_functions[])(void) = {
                test_imap_parser_crlf,
                test_imap_parser_partial_list,
+               test_imap_parser_read_tag_cmd,
                NULL
        };
        return test_run(test_functions);