address, username_r, delim_r, detail_r);
}
+int smtp_address_parse_any(const char *in, const char **address_r,
+ const char **endp_r)
+{
+ const unsigned char *p, *pend, *poffset;
+ bool path = FALSE;
+ bool quoted = FALSE;
+
+ if (endp_r != NULL)
+ *endp_r = in;
+
+ poffset = p = (const unsigned char *)in;
+ pend = p + strlen(in);
+ if (*p == '<') {
+ path = TRUE;
+ p++;
+ poffset = p;
+ }
+ if (*p == '"') {
+ quoted = TRUE;
+ p++;
+ }
+
+ while (p < pend) {
+ if (quoted && *p == '\\') {
+ p++;
+ if (p == pend || *p < 0x20)
+ return -1;
+ p++;
+ if (p == pend)
+ break;
+ }
+ switch (*p) {
+ case '"':
+ quoted = FALSE;
+ break;
+ case ' ':
+ if (!quoted) {
+ if (path)
+ return -1;
+ if (address_r != NULL)
+ *address_r = t_strdup_until(poffset, p);
+ if (endp_r != NULL)
+ *endp_r = (const char *)p;
+ return 0;
+ }
+ break;
+ case '>':
+ if (!quoted) {
+ if (address_r != NULL)
+ *address_r = t_strdup_until(poffset, p);
+ if (endp_r != NULL)
+ *endp_r = (const char *)(p + 1);
+ return 0;
+ }
+ break;
+ default:
+ if (*p < 0x20)
+ return -1;
+ break;
+ }
+ p++;
+ }
+ if (quoted || path)
+ return -1;
+ if (address_r != NULL)
+ *address_r = t_strdup_until(poffset, p);
+ if (endp_r != NULL)
+ *endp_r = (const char *)p;
+ return 0;
+}
+
/*
* SMTP address construction
*/
const char **username_r, char *delim_r,
const char **detail_r);
+/* Parse any (possibly broken) address on the input to the best of our ability
+ until end of input or unquoted ` '. Things that are truly evil (unending
+ quoted string, control characters and a path without a closing '>') will
+ still fail and return -1. If the parse was successful, it will return 0.
+ The parsed address string is returned in address_r. Any outer < and > are
+ omitted in the parsed address. The endp_r parameter is used to return a
+ pointer to the end of the path string, so that the caller can continue
+ parsing from there.*/
+int smtp_address_parse_any(const char *in, const char **address_r,
+ const char **endp_r);
+
/*
* SMTP address construction
*/
#include "test-lib.h"
#include "str.h"
+#include "str-sanitize.h"
#include "test-common.h"
#include "smtp-address.h"
} T_END;
}
+/*
+ * Skip address tests
+ */
+
+struct any_address_parse_test {
+ const char *input;
+ const char *address;
+ size_t pos;
+ int ret;
+};
+
+static const struct any_address_parse_test
+any_address_parse_tests[] = {
+ {
+ .input = "",
+ .address = "",
+ .pos = 0,
+ .ret = 0,
+ },
+ {
+ .input = " ",
+ .address = "",
+ .pos = 0,
+ .ret = 0,
+ },
+ {
+ .input = "frop@example.com",
+ .address = "frop@example.com",
+ .pos = 16,
+ .ret = 0,
+ },
+ {
+ .input = "frop@example.com ",
+ .address = "frop@example.com",
+ .pos = 16,
+ .ret = 0,
+ },
+ {
+ .input = "<frop@example.com>",
+ .address = "frop@example.com",
+ .pos = 18,
+ .ret = 0,
+ },
+ {
+ .input = "<frop@example.com> ",
+ .address = "frop@example.com",
+ .pos = 18,
+ .ret = 0,
+ },
+ {
+ .input = "<frop@example.com",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "<frop@example.com ",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "fr\"op@example.com",
+ .address = "fr\"op@example.com",
+ .pos = 17,
+ .ret = 0,
+ },
+ {
+ .input = "fr\"op@example.com ",
+ .address = "fr\"op@example.com",
+ .pos = 17,
+ .ret = 0,
+ },
+ {
+ .input = "fr<op@example.com",
+ .address = "fr<op@example.com",
+ .pos = 17,
+ .ret = 0,
+ },
+ {
+ .input = "fr<op@example.com ",
+ .address = "fr<op@example.com",
+ .pos = 17,
+ .ret = 0,
+ },
+ {
+ .input = "\"frop\"@example.com",
+ .address = "\"frop\"@example.com",
+ .pos = 18,
+ .ret = 0,
+ },
+ {
+ .input = "\"frop\"@example.com ",
+ .address = "\"frop\"@example.com",
+ .pos = 18,
+ .ret = 0,
+ },
+ {
+ .input = "\"frop\\\"@example.com",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "\"frop\\\"@example.com ",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "<\"fr>op\"@example.com>",
+ .address = "\"fr>op\"@example.com",
+ .pos = 21,
+ .ret = 0,
+ },
+ {
+ .input = "<\"fr>op\"@example.com> ",
+ .address = "\"fr>op\"@example.com",
+ .pos = 21,
+ .ret = 0,
+ },
+ {
+ .input = "<\"fr>op\"@example.com",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "<\"fr>op\"@example.com ",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "<\"frop\">",
+ .address = "\"frop\"",
+ .pos = 8,
+ .ret = 0,
+ },
+ {
+ .input = "<\"frop\"> ",
+ .address = "\"frop\"",
+ .pos = 8,
+ .ret = 0,
+ },
+ {
+ .input = "<\"frop\"",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "<\"frop\" ",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "\"frop\\\" ",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "\"frop\\\"",
+ .pos = 0,
+ .ret = -1,
+ },
+};
+
+unsigned int any_address_parse_tests_count =
+ N_ELEMENTS(any_address_parse_tests);
+
+static void test_smtp_parse_any_address(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < any_address_parse_tests_count; i++) T_BEGIN {
+ const struct any_address_parse_test *test;
+ const char *address = NULL, *pos = NULL;
+ int ret;
+
+ test = &any_address_parse_tests[i];
+ ret = smtp_address_parse_any(test->input, &address, &pos);
+
+ test_begin(t_strdup_printf("smtp parse any [%d]", i));
+ test_out_quiet(t_strdup_printf("parse(\"%s\")",
+ str_sanitize(test->input, 256)),
+ (ret == test->ret) &&
+ ((size_t)(pos - test->input) == test->pos) &&
+ (null_strcmp(test->address, address ) == 0));
+ test_end();
+ } T_END;
+}
+
/*
* Tests
*/
test_smtp_path_parse_invalid,
test_smtp_username_parse_invalid,
test_smtp_address_detail_parse,
+ test_smtp_parse_any_address,
NULL
};
return test_run(test_functions);