1 /* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "str.h"
5 #include "message-address.h"
6 #include "test-common.h"
7
8 enum test_message_address {
9 TEST_MESSAGE_ADDRESS_FLAG_SKIP_LIST = BIT(0),
10 };
11
cmp_addr(const struct message_address * a1,const struct message_address * a2)12 static bool cmp_addr(const struct message_address *a1,
13 const struct message_address *a2)
14 {
15 return null_strcmp(a1->name, a2->name) == 0 &&
16 null_strcmp(a1->route, a2->route) == 0 &&
17 null_strcmp(a1->mailbox, a2->mailbox) == 0 &&
18 null_strcmp(a1->domain, a2->domain) == 0 &&
19 a1->invalid_syntax == a2->invalid_syntax;
20 }
21
22 static const struct message_address *
test_parse_address(const char * input,bool fill_missing)23 test_parse_address(const char *input, bool fill_missing)
24 {
25 const enum message_address_parse_flags flags =
26 fill_missing ? MESSAGE_ADDRESS_PARSE_FLAG_FILL_MISSING : 0;
27 /* duplicate the input (without trailing NUL) so valgrind notices
28 if there's any out-of-bounds access */
29 size_t input_len = strlen(input);
30 unsigned char *input_dup = i_memdup(input, input_len);
31 const struct message_address *addr =
32 message_address_parse(pool_datastack_create(),
33 input_dup, input_len, UINT_MAX, flags);
34 i_free(input_dup);
35 return addr;
36 }
37
test_message_address(void)38 static void test_message_address(void)
39 {
40 static const struct test {
41 const char *input;
42 const char *wanted_output;
43 const char *wanted_filled_output;
44 struct message_address addr;
45 struct message_address filled_addr;
46 enum test_message_address flags;
47 } tests[] = {
48 /* user@domain -> <user@domain> */
49 { "user@domain", "<user@domain>", NULL,
50 { NULL, NULL, NULL, "user", "domain", FALSE },
51 { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
52 { "\"user\"@domain", "<user@domain>", NULL,
53 { NULL, NULL, NULL, "user", "domain", FALSE },
54 { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
55 { "\"user name\"@domain", "<\"user name\"@domain>", NULL,
56 { NULL, NULL, NULL, "user name", "domain", FALSE },
57 { NULL, NULL, NULL, "user name", "domain", FALSE }, 0 },
58 { "\"user@na\\\\me\"@domain", "<\"user@na\\\\me\"@domain>", NULL,
59 { NULL, NULL, NULL, "user@na\\me", "domain", FALSE },
60 { NULL, NULL, NULL, "user@na\\me", "domain", FALSE }, 0 },
61 { "\"user\\\"name\"@domain", "<\"user\\\"name\"@domain>", NULL,
62 { NULL, NULL, NULL, "user\"name", "domain", FALSE },
63 { NULL, NULL, NULL, "user\"name", "domain", FALSE }, 0 },
64 { "\"\"@domain", "<\"\"@domain>", NULL,
65 { NULL, NULL, NULL, "", "domain", FALSE },
66 { NULL, NULL, NULL, "", "domain", FALSE }, 0 },
67 { "user", "<user>", "<user@MISSING_DOMAIN>",
68 { NULL, NULL, NULL, "user", "", TRUE },
69 { NULL, NULL, NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
70 { "@domain", "<\"\"@domain>", "<MISSING_MAILBOX@domain>",
71 { NULL, NULL, NULL, "", "domain", TRUE },
72 { NULL, NULL, NULL, "MISSING_MAILBOX", "domain", TRUE }, 0 },
73
74 /* Display Name -> Display Name */
75 { "Display Name", "\"Display Name\"", "\"Display Name\" <MISSING_MAILBOX@MISSING_DOMAIN>",
76 { NULL, "Display Name", NULL, "", "", TRUE },
77 { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
78 { "\"Display Name\"", "\"Display Name\"", "\"Display Name\" <MISSING_MAILBOX@MISSING_DOMAIN>",
79 { NULL, "Display Name", NULL, "", "", TRUE },
80 { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
81 { "Display \"Name\"", "\"Display Name\"", "\"Display Name\" <MISSING_MAILBOX@MISSING_DOMAIN>",
82 { NULL, "Display Name", NULL, "", "", TRUE },
83 { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
84 { "\"Display\" \"Name\"", "\"Display Name\"", "\"Display Name\" <MISSING_MAILBOX@MISSING_DOMAIN>",
85 { NULL, "Display Name", NULL, "", "", TRUE },
86 { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
87 { "\"\"", "", "<MISSING_MAILBOX@MISSING_DOMAIN>",
88 { NULL, "", NULL, "", "", TRUE },
89 { NULL, "", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
90
91 /* <user@domain> -> <user@domain> */
92 { "<user@domain>", NULL, NULL,
93 { NULL, NULL, NULL, "user", "domain", FALSE },
94 { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
95 { "<\"user\"@domain>", "<user@domain>", NULL,
96 { NULL, NULL, NULL, "user", "domain", FALSE },
97 { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
98 { "<\"user name\"@domain>", NULL, NULL,
99 { NULL, NULL, NULL, "user name", "domain", FALSE },
100 { NULL, NULL, NULL, "user name", "domain", FALSE }, 0 },
101 { "<\"user@na\\\\me\"@domain>", NULL, NULL,
102 { NULL, NULL, NULL, "user@na\\me", "domain", FALSE },
103 { NULL, NULL, NULL, "user@na\\me", "domain", FALSE }, 0 },
104 { "<\"user\\\"name\"@domain>", NULL, NULL,
105 { NULL, NULL, NULL, "user\"name", "domain", FALSE },
106 { NULL, NULL, NULL, "user\"name", "domain", FALSE }, 0 },
107 { "<\"\"@domain>", NULL, NULL,
108 { NULL, NULL, NULL, "", "domain", FALSE },
109 { NULL, NULL, NULL, "", "domain", FALSE }, 0 },
110 { "<user>", NULL, "<user@MISSING_DOMAIN>",
111 { NULL, NULL, NULL, "user", "", TRUE },
112 { NULL, NULL, NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
113 { "<@route>", "<@route:\"\">", "<INVALID_ROUTE:MISSING_MAILBOX@MISSING_DOMAIN>",
114 { NULL, NULL, "@route", "", "", TRUE },
115 { NULL, NULL, "INVALID_ROUTE", "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
116
117 /* user@domain (Display Name) -> "Display Name" <user@domain> */
118 { "user@domain (DisplayName)", "DisplayName <user@domain>", NULL,
119 { NULL, "DisplayName", NULL, "user", "domain", FALSE },
120 { NULL, "DisplayName", NULL, "user", "domain", FALSE }, 0 },
121 { "user@domain (Display Name)", "\"Display Name\" <user@domain>", NULL,
122 { NULL, "Display Name", NULL, "user", "domain", FALSE },
123 { NULL, "Display Name", NULL, "user", "domain", FALSE }, 0 },
124 { "user@domain (Display\"Name)", "\"Display\\\"Name\" <user@domain>", NULL,
125 { NULL, "Display\"Name", NULL, "user", "domain", FALSE },
126 { NULL, "Display\"Name", NULL, "user", "domain", FALSE }, 0 },
127 { "user (Display Name)", "\"Display Name\" <user>", "\"Display Name\" <user@MISSING_DOMAIN>",
128 { NULL, "Display Name", NULL, "user", "", TRUE },
129 { NULL, "Display Name", NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
130 { "@domain (Display Name)", "\"Display Name\" <\"\"@domain>", "\"Display Name\" <MISSING_MAILBOX@domain>",
131 { NULL, "Display Name", NULL, "", "domain", TRUE },
132 { NULL, "Display Name", NULL, "MISSING_MAILBOX", "domain", TRUE }, 0 },
133 { "user@domain ()", "<user@domain>", NULL,
134 { NULL, NULL, NULL, "user", "domain", FALSE },
135 { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
136
137 /* Display Name <user@domain> -> "Display Name" <user@domain> */
138 { "DisplayName <user@domain>", NULL, NULL,
139 { NULL, "DisplayName", NULL, "user", "domain", FALSE },
140 { NULL, "DisplayName", NULL, "user", "domain", FALSE }, 0 },
141 { "Display Name <user@domain>", "\"Display Name\" <user@domain>", NULL,
142 { NULL, "Display Name", NULL, "user", "domain", FALSE },
143 { NULL, "Display Name", NULL, "user", "domain", FALSE }, 0 },
144 { "\"Display Name\" <user@domain>", NULL, NULL,
145 { NULL, "Display Name", NULL, "user", "domain", FALSE },
146 { NULL, "Display Name", NULL, "user", "domain", FALSE }, 0 },
147 { "\"Display\\\"Name\" <user@domain>", NULL, NULL,
148 { NULL, "Display\"Name", NULL, "user", "domain", FALSE },
149 { NULL, "Display\"Name", NULL, "user", "domain", FALSE }, 0 },
150 { "Display Name <user>", "\"Display Name\" <user>", "\"Display Name\" <user@MISSING_DOMAIN>",
151 { NULL, "Display Name", NULL, "user", "", TRUE },
152 { NULL, "Display Name", NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
153 { "\"\" <user@domain>", "<user@domain>", NULL,
154 { NULL, NULL, NULL, "user", "domain", FALSE },
155 { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
156
157 /* <@route:user@domain> -> <@route:user@domain> */
158 { "<@route:user@domain>", NULL, NULL,
159 { NULL, NULL, "@route", "user", "domain", FALSE },
160 { NULL, NULL, "@route", "user", "domain", FALSE }, 0 },
161 { "<@route,@route2:user@domain>", NULL, NULL,
162 { NULL, NULL, "@route,@route2", "user", "domain", FALSE },
163 { NULL, NULL, "@route,@route2", "user", "domain", FALSE }, 0 },
164 { "<@route@route2:user@domain>", "<@route,@route2:user@domain>", NULL,
165 { NULL, NULL, "@route,@route2", "user", "domain", FALSE },
166 { NULL, NULL, "@route,@route2", "user", "domain", FALSE }, 0 },
167 { "<@route@route2:user>", "<@route,@route2:user>", "<@route,@route2:user@MISSING_DOMAIN>",
168 { NULL, NULL, "@route,@route2", "user", "", TRUE },
169 { NULL, NULL, "@route,@route2", "user", "MISSING_DOMAIN", TRUE }, 0 },
170 { "<@route@route2:\"\"@domain>", "<@route,@route2:\"\"@domain>", NULL,
171 { NULL, NULL, "@route,@route2", "", "domain", FALSE },
172 { NULL, NULL, "@route,@route2", "", "domain", FALSE }, 0 },
173
174 /* Display Name <@route:user@domain> ->
175 "Display Name" <@route:user@domain> */
176 { "Display Name <@route:user@domain>", "\"Display Name\" <@route:user@domain>", NULL,
177 { NULL, "Display Name", "@route", "user", "domain", FALSE },
178 { NULL, "Display Name", "@route", "user", "domain", FALSE }, 0 },
179 { "Display Name <@route,@route2:user@domain>", "\"Display Name\" <@route,@route2:user@domain>", NULL,
180 { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE },
181 { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE }, 0 },
182 { "Display Name <@route@route2:user@domain>", "\"Display Name\" <@route,@route2:user@domain>", NULL,
183 { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE },
184 { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE }, 0 },
185 { "Display Name <@route@route2:user>", "\"Display Name\" <@route,@route2:user>", "\"Display Name\" <@route,@route2:user@MISSING_DOMAIN>",
186 { NULL, "Display Name", "@route,@route2", "user", "", TRUE },
187 { NULL, "Display Name", "@route,@route2", "user", "MISSING_DOMAIN", TRUE }, 0 },
188 { "Display Name <@route@route2:\"\"@domain>", "\"Display Name\" <@route,@route2:\"\"@domain>", NULL,
189 { NULL, "Display Name", "@route,@route2", "", "domain", FALSE },
190 { NULL, "Display Name", "@route,@route2", "", "domain", FALSE }, 0 },
191
192 /* other tests: */
193 { "\"foo: <a@b>;,\" <user@domain>", NULL, NULL,
194 { NULL, "foo: <a@b>;,", NULL, "user", "domain", FALSE },
195 { NULL, "foo: <a@b>;,", NULL, "user", "domain", FALSE }, 0 },
196 { "<>", "", "<MISSING_MAILBOX@MISSING_DOMAIN>",
197 { NULL, NULL, NULL, "", "", TRUE },
198 { NULL, NULL, NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
199 { "<@>", "", "<INVALID_ROUTE:MISSING_MAILBOX@MISSING_DOMAIN>",
200 { NULL, NULL, NULL, "", "", TRUE },
201 { NULL, NULL, "INVALID_ROUTE", "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
202
203 /* Test against a out-of-bounds read bug - keep these two tests
204 together in this same order: */
205 { "aaaa@", "<aaaa>", "<aaaa@MISSING_DOMAIN>",
206 { NULL, NULL, NULL, "aaaa", "", TRUE },
207 { NULL, NULL, NULL, "aaaa", "MISSING_DOMAIN", TRUE }, 0 },
208 { "a(aa", "", "<MISSING_MAILBOX@MISSING_DOMAIN>",
209 { NULL, NULL, NULL, "", "", TRUE },
210 { NULL, NULL, NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE },
211 TEST_MESSAGE_ADDRESS_FLAG_SKIP_LIST },
212 };
213 static struct message_address group_prefix = {
214 NULL, NULL, NULL, "group", NULL, FALSE
215 };
216 static struct message_address group_suffix = {
217 NULL, NULL, NULL, NULL, NULL, FALSE
218 };
219 const struct message_address *addr;
220 string_t *str, *group;
221 const char *wanted_string;
222 unsigned int i;
223
224 test_begin("message address parsing");
225 str = t_str_new(128);
226 group = t_str_new(256);
227
228 for (i = 0; i < N_ELEMENTS(tests)*2; i++) {
229 const struct test *test = &tests[i/2];
230 const struct message_address *test_wanted_addr;
231 bool fill_missing = i%2 != 0;
232
233 test_wanted_addr = !fill_missing ?
234 &test->addr : &test->filled_addr;
235 addr = test_parse_address(test->input, fill_missing);
236 test_assert_idx(addr != NULL && addr->next == NULL &&
237 cmp_addr(addr, test_wanted_addr), i);
238
239 /* test the address alone */
240 str_truncate(str, 0);
241 message_address_write(str, addr);
242 if (fill_missing && test->wanted_filled_output != NULL)
243 wanted_string = test->wanted_filled_output;
244 else if (test->wanted_output != NULL)
245 wanted_string = test->wanted_output;
246 else
247 wanted_string = test->input;
248 test_assert_idx(strcmp(str_c(str), wanted_string) == 0, i);
249
250 if ((test->flags & TEST_MESSAGE_ADDRESS_FLAG_SKIP_LIST) != 0)
251 continue;
252
253 /* test the address as a list of itself */
254 for (unsigned int list_length = 2; list_length <= 5; list_length++) {
255 str_truncate(group, 0);
256 str_append(group, test->input);
257 for (unsigned int j = 1; j < list_length; j++) {
258 if ((j % 2) == 0)
259 str_append(group, ",");
260 else
261 str_append(group, " , \n ");
262 str_append(group, test->input);
263 }
264
265 addr = test_parse_address(str_c(group), fill_missing);
266 for (unsigned int j = 0; j < list_length; j++) {
267 test_assert_idx(addr != NULL &&
268 cmp_addr(addr, test_wanted_addr), i);
269 if (addr != NULL)
270 addr = addr->next;
271 }
272 test_assert_idx(addr == NULL, i);
273 }
274
275 /* test the address as a group of itself */
276 for (unsigned int list_length = 1; list_length <= 5; list_length++) {
277 str_truncate(group, 0);
278 str_printfa(group, "group: %s", test->input);
279 for (unsigned int j = 1; j < list_length; j++) {
280 if ((j % 2) == 0)
281 str_append(group, ",");
282 else
283 str_append(group, " , \n ");
284 str_append(group, test->input);
285 }
286 str_append_c(group, ';');
287
288 addr = test_parse_address(str_c(group), fill_missing);
289 test_assert(addr != NULL && cmp_addr(addr, &group_prefix));
290 addr = addr->next;
291 for (unsigned int j = 0; j < list_length; j++) {
292 test_assert_idx(addr != NULL &&
293 cmp_addr(addr, test_wanted_addr), i);
294 if (addr != NULL)
295 addr = addr->next;
296 }
297 test_assert_idx(addr != NULL && addr->next == NULL &&
298 cmp_addr(addr, &group_suffix), i);
299 }
300 }
301 test_end();
302
303 test_begin("message address parsing with empty group");
304 str_truncate(group, 0);
305 str_append(group, "group:;");
306 addr = test_parse_address(str_c(group), FALSE);
307 str_truncate(str, 0);
308 message_address_write(str, addr);
309 test_assert(addr != NULL && cmp_addr(addr, &group_prefix));
310 addr = addr->next;
311 test_assert(addr != NULL && addr->next == NULL &&
312 cmp_addr(addr, &group_suffix));
313 test_assert(strcmp(str_c(str), "group:;") == 0);
314 test_end();
315
316 test_begin("message address parsing empty string");
317 test_assert(message_address_parse(unsafe_data_stack_pool, &uchar_nul, 0, 10,
318 MESSAGE_ADDRESS_PARSE_FLAG_FILL_MISSING) == NULL);
319 str_truncate(str, 0);
320 message_address_write(str, NULL);
321 test_assert(str_len(str) == 0);
322 test_end();
323 }
324
test_message_address_nuls(void)325 static void test_message_address_nuls(void)
326 {
327 const unsigned char input[] =
328 "\"user\0nuls\\\0-esc\"@[domain\0nuls\\\0-esc] (comment\0nuls\\\0-esc)";
329 const struct message_address output = {
330 NULL, "comment\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc", NULL,
331 "user\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc",
332 "[domain\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc]", FALSE
333 };
334 const struct message_address *addr;
335
336 test_begin("message address parsing with NULs");
337 addr = message_address_parse(pool_datastack_create(),
338 input, sizeof(input)-1, UINT_MAX, 0);
339 test_assert(addr != NULL && cmp_addr(addr, &output));
340 test_end();
341 }
342
test_message_address_nuls_display_name(void)343 static void test_message_address_nuls_display_name(void)
344 {
345 const unsigned char input[] =
346 "\"displayname\0nuls\\\0-esc\" <\"user\0nuls\\\0-esc\"@[domain\0nuls\\\0-esc]>";
347 const struct message_address output = {
348 NULL, "displayname\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc", NULL,
349 "user\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc",
350 "[domain\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc]", FALSE
351 };
352 const struct message_address *addr;
353
354 test_begin("message address parsing with NULs in display-name");
355 addr = message_address_parse(pool_datastack_create(),
356 input, sizeof(input)-1, UINT_MAX, 0);
357 test_assert(addr != NULL && cmp_addr(addr, &output));
358 test_end();
359 }
360
test_message_address_non_strict_dots(void)361 static void test_message_address_non_strict_dots(void)
362 {
363 const char *const inputs[] = {
364 ".@example.com",
365 "..@example.com",
366 "..foo@example.com",
367 "..foo..@example.com",
368 "..foo..bar..@example.com",
369 };
370 const struct message_address *addr;
371 struct message_address output = {
372 NULL, NULL, NULL, "local-part",
373 "example.com", FALSE
374 };
375
376 test_begin("message address parsing with non-strict dots");
377 for (unsigned int i = 0; i < N_ELEMENTS(inputs); i++) {
378 const unsigned char *addr_input =
379 (const unsigned char *)inputs[i];
380 /* invalid with strict-dots flag */
381 addr = message_address_parse(pool_datastack_create(),
382 addr_input, strlen(inputs[i]), UINT_MAX,
383 MESSAGE_ADDRESS_PARSE_FLAG_STRICT_DOTS);
384 test_assert_idx(addr != NULL && addr->invalid_syntax, i);
385
386 /* valid without the strict-dots flag */
387 addr = message_address_parse(pool_datastack_create(),
388 addr_input, strlen(inputs[i]), UINT_MAX, 0);
389 output.mailbox = t_strcut(inputs[i], '@');
390 test_assert_idx(addr != NULL && cmp_addr(addr, &output), i);
391 }
392 test_end();
393 }
394
395 static int
test_parse_path(const char * input,const struct message_address ** addr_r)396 test_parse_path(const char *input, const struct message_address **addr_r)
397 {
398 struct message_address *addr;
399 char *input_dup;
400 int ret;
401
402 /* duplicate the input (without trailing NUL) so valgrind notices
403 if there's any out-of-bounds access */
404 size_t input_len = strlen(input);
405 if (input_len > 0)
406 input = input_dup = i_memdup(input, input_len);
407 ret = message_address_parse_path(pool_datastack_create(),
408 (const unsigned char *)input, input_len,
409 &addr);
410 if (input_len > 0)
411 i_free(input_dup);
412 *addr_r = addr;
413 return ret;
414 }
415
test_message_address_path(void)416 static void test_message_address_path(void)
417 {
418 static const struct test {
419 const char *input;
420 const char *wanted_output;
421 struct message_address addr;
422 } tests[] = {
423 { "<>", NULL,
424 { NULL, NULL, NULL, NULL, NULL, FALSE } },
425 { " < > ", "<>",
426 { NULL, NULL, NULL, NULL, NULL, FALSE } },
427 { "<user@domain>", NULL,
428 { NULL, NULL, NULL, "user", "domain", FALSE } },
429 { " <user@domain> ", "<user@domain>",
430 { NULL, NULL, NULL, "user", "domain", FALSE } },
431 { "user@domain", "<user@domain>",
432 { NULL, NULL, NULL, "user", "domain", FALSE } },
433 { " user@domain ", "<user@domain>",
434 { NULL, NULL, NULL, "user", "domain", FALSE } },
435 { "<\"user\"@domain>", "<user@domain>",
436 { NULL, NULL, NULL, "user", "domain", FALSE } },
437 { "<\"user name\"@domain>", NULL,
438 { NULL, NULL, NULL, "user name", "domain", FALSE } },
439 { "<\"user@na\\\\me\"@domain>", NULL,
440 { NULL, NULL, NULL, "user@na\\me", "domain", FALSE } },
441 { "<\"user\\\"name\"@domain>", NULL,
442 { NULL, NULL, NULL, "user\"name", "domain", FALSE } },
443 { "<\"\"@domain>", NULL,
444 { NULL, NULL, NULL, "", "domain", FALSE } },
445 { "<@source", "<>",
446 { NULL, NULL, NULL, NULL, NULL, TRUE } },
447 };
448 const struct message_address *addr;
449 string_t *str;
450 const char *wanted_string;
451 unsigned int i;
452
453 test_begin("message address path parsing");
454 str = t_str_new(128);
455
456 for (i = 0; i < N_ELEMENTS(tests); i++) {
457 const struct test *test = &tests[i];
458 const struct message_address *test_wanted_addr;
459 int ret;
460
461 test_wanted_addr = &test->addr;
462 ret = test_parse_path(test->input, &addr);
463 if (addr->invalid_syntax)
464 test_assert_idx(ret == -1, i);
465 else
466 test_assert_idx(ret == 0, i);
467 test_assert_idx(addr != NULL && addr->next == NULL &&
468 cmp_addr(addr, test_wanted_addr), i);
469
470 /* test the address alone */
471 str_truncate(str, 0);
472 message_address_write(str, addr);
473 if (test->wanted_output != NULL)
474 wanted_string = test->wanted_output;
475 else
476 wanted_string = test->input;
477 test_assert_idx(strcmp(str_c(str), wanted_string) == 0, i);
478 }
479 test_end();
480 }
481
test_message_address_path_invalid(void)482 static void test_message_address_path_invalid(void)
483 {
484 static const char *tests[] = {
485 "",
486 "<",
487 " < ",
488 ">",
489 " > ",
490 "<user@domain",
491 " <user@domain ",
492 "user@domain>",
493 " user@domain> ",
494 "<user>",
495 "<@route@route2:user>",
496 "<@domain>",
497 "@domain",
498 " @domain ",
499 "<user@>",
500 "user@",
501 " user@ ",
502 "<user@domain>bladiebla",
503 "user@domain@"
504 };
505 const struct message_address *addr;
506 unsigned int i;
507
508 test_begin("message address path invalid");
509
510 for (i = 0; i < N_ELEMENTS(tests); i++) {
511 const char *test = tests[i];
512 int ret;
513
514 ret = test_parse_path(test, &addr);
515 test_assert_idx(ret < 0, i);
516 }
517 test_end();
518 }
519
main(void)520 int main(void)
521 {
522 static void (*const test_functions[])(void) = {
523 test_message_address,
524 test_message_address_nuls,
525 test_message_address_nuls_display_name,
526 test_message_address_non_strict_dots,
527 test_message_address_path,
528 test_message_address_path_invalid,
529 NULL
530 };
531 return test_run(test_functions);
532 }
533