Blob


1 /*
2 * Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <ctype.h>
18 #include <errno.h>
19 #include <stddef.h>
20 #include <string.h>
21 #include <strings.h>
23 #include "mheader.h"
25 #include <stdio.h>
26 char *
27 osmtpd_mheader_skip_sp(char *ptr, int optional)
28 {
29 if (ptr[0] == 0x20)
30 return ptr + 1;
31 return optional ? ptr : NULL;
32 }
34 char *
35 osmtpd_mheader_skip_htab(char *ptr, int optional)
36 {
37 if (ptr[0] == 0x9)
38 return ptr + 1;
39 return optional ? ptr : NULL;
40 }
42 char *
43 osmtpd_mheader_skip_wsp(char *ptr, int optional)
44 {
45 char *start = ptr;
47 if ((ptr = osmtpd_mheader_skip_sp(start, 0)) != NULL ||
48 (ptr = osmtpd_mheader_skip_htab(start, 0)) != NULL)
49 return ptr;
50 return optional ? ptr : NULL;
51 }
53 char *
54 osmtpd_mheader_skip_crlf(char *ptr, int optional)
55 {
56 if (ptr[0] == 13 && ptr[1] == 10)
57 return ptr + 2;
58 return optional ? ptr : NULL;
59 }
61 char *
62 osmtpd_mheader_skip_vchar(char *ptr, int optional)
63 {
64 if (ptr[0] >= 0x21 && ptr[0] <= 0x7e)
65 return ptr + 1;
66 return optional ? ptr : NULL;
67 }
69 char *
70 osmtpd_mheader_skip_lf(char *ptr, int optional)
71 {
72 if (ptr[0] == 0xa)
73 return ptr + 1;
74 return optional ? ptr : NULL;
75 }
77 char *
78 osmtpd_mheader_skip_cr(char *ptr, int optional)
79 {
80 if (ptr[0] == 0xd)
81 return ptr + 1;
82 return optional ? ptr : NULL;
83 }
85 char *
86 osmtpd_mheader_skip_alpha(char *ptr, int optional)
87 {
88 if ((ptr[0] >= 0x41 && ptr[0] <= 0x5a) ||
89 (ptr[0] >= 0x61 && ptr[0] <= 0x7a))
90 return ptr + 1;
91 return optional ? ptr : NULL;
92 }
94 char *
95 osmtpd_mheader_skip_digit(char *ptr, int optional)
96 {
97 if (ptr[0] >= 0x30 && ptr[0] <= 0x39)
98 return ptr + 1;
99 return optional ? ptr : NULL;
102 char *
103 osmtpd_mheader_skip_dquote(char *ptr, int optional)
105 if (ptr[0] == 0x22)
106 return ptr + 1;
107 return optional ? ptr : NULL;
110 char *
111 osmtpd_mheader_skip_obs_fws(char *ptr, int optional)
113 char *start = ptr, *prev;
115 if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL)
116 return optional ? start : NULL;
117 prev = ptr;
118 while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
119 prev = ptr;
121 ptr = prev;
122 while (1) {
123 if ((ptr = osmtpd_mheader_skip_crlf(ptr, 0)) == NULL)
124 return prev;
125 if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL)
126 return prev;
127 prev = ptr;
128 while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
129 prev = ptr;
130 ptr = prev;
134 char *
135 osmtpd_mheader_skip_fws(char *ptr, int optional)
137 char *start = ptr, *prev = ptr;
139 while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
140 prev = ptr;
141 if ((ptr = osmtpd_mheader_skip_crlf(prev, 1)) == prev)
142 ptr = start;
143 if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL)
144 return osmtpd_mheader_skip_obs_fws(start, optional);
145 prev = ptr;
146 while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
147 prev = ptr;
148 return prev;
151 char *
152 osmtpd_mheader_skip_obs_no_ws_ctl(char *ptr, int optional)
154 if ((ptr[0] >= 1 && ptr[0] <= 8) || ptr[0] == 11 || ptr[0] == 12 ||
155 (ptr[0] >= 14 && ptr[0] <= 31) || ptr[0] == 127)
156 return ptr + 1;
157 return optional ? ptr : NULL;
160 char *
161 osmtpd_mheader_skip_obs_ctext(char *ptr, int optional)
163 return osmtpd_mheader_skip_obs_no_ws_ctl(ptr, optional);
166 char *
167 osmtpd_mheader_skip_ctext(char *ptr, int optional)
169 char *start = ptr;
171 if ((ptr[0] >= 33 && ptr[0] <= 39) || (ptr[0] >= 42 && ptr[0] <= 91) ||
172 (ptr[0] >= 93 && ptr[0] <= 126))
173 return ptr + 1;
174 if ((ptr = osmtpd_mheader_skip_obs_ctext(ptr, 0)) != NULL)
175 return ptr;
176 return optional ? start : NULL;
179 char *
180 osmtpd_mheader_skip_obs_qp(char *ptr, int optional)
182 char *start = ptr;
184 if (ptr[0] == '\\' && (
185 (ptr = osmtpd_mheader_skip_obs_no_ws_ctl(start + 1, 0)) != NULL ||
186 (ptr = osmtpd_mheader_skip_lf(start + 1, 0)) != NULL ||
187 (ptr = osmtpd_mheader_skip_cr(start + 1, 0)) != NULL))
188 return ptr;
189 return optional ? start : NULL;
192 char *
193 osmtpd_mheader_skip_quoted_pair(char *ptr, int optional)
195 char *start = ptr;
197 if (ptr[0] == '\\' && (
198 (ptr = osmtpd_mheader_skip_vchar(start + 1, 0)) != NULL ||
199 (ptr = osmtpd_mheader_skip_wsp(start + 1, 0)) != NULL))
200 return ptr;
201 return osmtpd_mheader_skip_obs_qp(start, optional);
204 char *
205 osmtpd_mheader_skip_ccontent(char *ptr, int optional)
207 char *start = ptr;
209 if ((ptr = osmtpd_mheader_skip_ctext(ptr, 0)) != NULL)
210 return ptr;
211 if ((ptr = osmtpd_mheader_skip_quoted_pair(start, 0)) != NULL)
212 return ptr;
213 if ((ptr = osmtpd_mheader_skip_comment(start, 0)) != NULL)
214 return ptr;
215 return optional ? start : NULL;
218 char *
219 osmtpd_mheader_skip_comment(char *ptr, int optional)
221 char *start = ptr;
223 if (ptr++[0] != '(')
224 return optional ? start : NULL;
225 while (1) {
226 ptr = osmtpd_mheader_skip_fws(ptr, 1);
227 if (ptr[0] == ')')
228 return ptr + 1;
229 if ((ptr = osmtpd_mheader_skip_ccontent(ptr, 0)) == NULL)
230 return optional ? start : NULL;
234 char *
235 osmtpd_mheader_skip_cfws(char *ptr, int optional)
237 char *start = ptr, *prev;
239 while (1) {
240 ptr = osmtpd_mheader_skip_fws(ptr, 1);
241 prev = ptr;
242 if ((ptr = osmtpd_mheader_skip_comment(ptr, 0)) == NULL) {
243 ptr = prev;
244 break;
247 return ptr == start && !optional ? NULL : ptr;
250 char *
251 osmtpd_mheader_skip_atext(char *ptr, int optional)
253 char *start = ptr;
255 if ((ptr = osmtpd_mheader_skip_alpha(start, 0)) != NULL ||
256 (ptr = osmtpd_mheader_skip_digit(start, 0)) != NULL)
257 return ptr;
258 ptr = start;
259 if (ptr[0] == '!' || ptr[0] == '#' || ptr[0] == '$' || ptr[0] == '%' ||
260 ptr[0] == '&' || ptr[0] == '\'' || ptr[0] == '*' || ptr[0] == '+' ||
261 ptr[0] == '-' || ptr[0] == '/' || ptr[0] == '=' || ptr[0] == '?' ||
262 ptr[0] == '^' || ptr[0] == '_' || ptr[0] == '`' || ptr[0] == '{' ||
263 ptr[0] == '|' || ptr[0] == '}' || ptr[0] == '~')
264 return ptr + 1;
265 return optional ? start : NULL;
268 char *
269 osmtpd_mheader_skip_atom(char *ptr, int optional)
271 char *start = ptr, *prev;
273 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
274 if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL)
275 return optional ? start : NULL;
276 do {
277 prev = ptr;
278 ptr = osmtpd_mheader_skip_atext(ptr, 1);
279 } while (prev != ptr);
280 return osmtpd_mheader_skip_cfws(ptr, 1);
283 char *
284 osmtpd_mheader_skip_dot_atom_text(char *ptr, int optional)
286 char *start = ptr, *prev;
288 if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL)
289 return optional ? start : NULL;
290 do {
291 prev = ptr;
292 ptr = osmtpd_mheader_skip_atext(ptr, 1);
293 } while (ptr != prev);
295 while (ptr[0] == '.') {
296 ptr++;
297 if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL)
298 return prev;
299 do {
300 prev = ptr;
301 ptr = osmtpd_mheader_skip_atext(ptr, 1);
302 } while (ptr != prev);
304 return ptr;
307 char *
308 osmtpd_mheader_skip_dot_atom(char *ptr, int optional)
310 char *start = ptr;
312 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
313 if ((ptr = osmtpd_mheader_skip_dot_atom_text(ptr, 0)) == NULL)
314 return optional ? start : NULL;
315 return osmtpd_mheader_skip_cfws(ptr, 1);
319 char *
320 osmtpd_mheader_skip_obs_qtext(char *ptr, int optional)
322 return osmtpd_mheader_skip_obs_no_ws_ctl(ptr, optional);
325 char *
326 osmtpd_mheader_skip_qtext(char *ptr, int optional)
328 char *start = ptr;
330 if (ptr[0] == 33 || (ptr[0] >= 35 && ptr[0] <= 91) ||
331 (ptr[0] >= 93 && ptr[0] <= 126))
332 return ptr + 1;
333 if ((ptr = osmtpd_mheader_skip_obs_qtext(ptr, 0)) != NULL)
334 return ptr;
335 return optional ? start : NULL;
338 char *
339 osmtpd_mheader_skip_qcontent(char *ptr, int optional)
341 char *start = ptr;
343 if ((ptr = osmtpd_mheader_skip_qtext(ptr, 0)) != NULL)
344 return ptr;
345 return osmtpd_mheader_skip_quoted_pair(start, optional);
348 char *
349 osmtpd_mheader_skip_quoted_string(char *ptr, int optional)
351 char *start = ptr, *prev;
353 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
354 if ((ptr = osmtpd_mheader_skip_dquote(ptr, 0)) == NULL)
355 return optional ? start : NULL;
356 prev = ptr;
357 while (1) {
358 ptr = osmtpd_mheader_skip_fws(ptr, 1);
359 if ((ptr = osmtpd_mheader_skip_qcontent(ptr, 0)) == NULL)
360 break;
361 prev = ptr;
363 if ((ptr = osmtpd_mheader_skip_dquote(prev, 0)) == NULL)
364 return optional ? start : NULL;
365 return osmtpd_mheader_skip_cfws(ptr, 1);
368 char *
369 osmtpd_mheader_skip_word(char *ptr, int optional)
371 char *start = ptr;
373 if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) != NULL)
374 return ptr;
375 return osmtpd_mheader_skip_quoted_string(start, optional);
378 char *
379 osmtpd_mheader_skip_obs_phrase(char *ptr, int optional)
381 char *start = ptr, *prev;
383 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
384 return optional ? start : NULL;
385 while (1) {
386 prev = ptr;
387 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) != NULL)
388 continue;
389 ptr = prev;
390 if (ptr[0] == '.') {
391 ptr++;
392 continue;
394 if ((ptr = osmtpd_mheader_skip_cfws(ptr, 0)) != NULL)
395 continue;
396 return prev;
400 char *
401 osmtpd_mheader_skip_phrase(char *ptr, int optional)
403 /* obs-phrase is a superset of phrae */
404 return osmtpd_mheader_skip_obs_phrase(ptr, optional);
405 #if 0
406 char *start = ptr, *prev;
408 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
409 return optional ? start : NULL;
410 while (1) {
411 prev = ptr;
412 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
413 return prev;
415 #endif
418 char *
419 osmtpd_mheader_skip_obs_local_part(char *ptr, int optional)
421 char *start = ptr, *prev;
423 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
424 return optional ? start : NULL;
425 prev = ptr;
426 while (ptr[0] == '.') {
427 ptr++;
428 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
429 return prev;
430 prev = ptr;
432 return ptr;
435 char *
436 osmtpd_mheader_skip_local_part(char *ptr, int optional)
438 char *start = ptr;
440 if ((ptr = osmtpd_mheader_skip_dot_atom(ptr, 0)) != NULL)
441 return ptr;
442 ptr = start;
443 if ((ptr = osmtpd_mheader_skip_quoted_string(ptr, 0)) != NULL)
444 return ptr;
445 return osmtpd_mheader_skip_obs_local_part(start, optional);
448 char *
449 osmtpd_mheader_skip_obs_dtext(char *ptr, int optional)
451 char *start = ptr;
453 if ((ptr = osmtpd_mheader_skip_obs_no_ws_ctl(ptr, 0)) != NULL)
454 return ptr;
455 return osmtpd_mheader_skip_quoted_pair(start, optional);
458 char *
459 osmtpd_mheader_skip_dtext(char *ptr, int optional)
461 if ((ptr[0] >= 33 && ptr[0] <= 90) || (ptr[0] >= 94 && ptr[0] <= 126))
462 return ptr + 1;
463 return osmtpd_mheader_skip_obs_dtext(ptr, optional);
467 char *
468 osmtpd_mheader_skip_domain_literal(char *ptr, int optional)
470 char *start = ptr, *prev;
472 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
473 if (ptr++[0] != '[')
474 return optional ? start : NULL;
475 while (1) {
476 ptr = osmtpd_mheader_skip_fws(ptr, 1);
477 prev = ptr;
478 if ((ptr = osmtpd_mheader_skip_dtext(ptr, 0)) == NULL) {
479 ptr = prev;
480 break;
483 if (ptr[0] != ']')
484 return optional ? start : NULL;
485 ptr++;
486 return osmtpd_mheader_skip_cfws(ptr, 1);
489 char *
490 osmtpd_mheader_skip_obs_domain(char *ptr, int optional)
492 char *start = ptr, *prev;
494 if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) == NULL)
495 return optional ? start : NULL;
496 prev = ptr;
497 while (1) {
498 if (ptr++[0] != '.')
499 return prev;
500 if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) == NULL)
501 return prev;
502 prev = ptr;
506 char *
507 osmtpd_mheader_skip_domain(char *ptr, int optional)
509 char *start = ptr;
511 if ((ptr = osmtpd_mheader_skip_dot_atom(start, 0)) != NULL)
512 return ptr;
513 if ((ptr = osmtpd_mheader_skip_domain_literal(start, 0)) != NULL)
514 return ptr;
515 return osmtpd_mheader_skip_obs_domain(start, optional);
518 char *
519 osmtpd_mheader_skip_display_name(char *ptr, int optional)
521 return osmtpd_mheader_skip_phrase(ptr, optional);
524 char *
525 osmtpd_mheader_skip_obs_domain_list(char *ptr, int optional)
527 char *start = ptr, *prev = ptr;
529 while (1) {
530 if (ptr[0] == ',') {
531 ptr++;
532 prev = ptr;
533 continue;
534 } else if ((ptr = osmtpd_mheader_skip_cfws(ptr, 0)) != NULL) {
535 prev = ptr;
536 continue;
538 break;
540 ptr = prev;
542 if (ptr++[0] != '@')
543 return optional ? start : NULL;
544 if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL)
545 return optional ? start : NULL;
546 while (1) {
547 if (ptr[0] != ',')
548 break;
549 ptr++;
550 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
551 if (ptr[0] != '@')
552 continue;
553 prev = ptr;
554 if ((ptr = osmtpd_mheader_skip_domain(ptr + 1, 0)) == NULL) {
555 ptr = prev;
556 break;
559 return ptr;
562 char *
563 osmtpd_mheader_skip_obs_route(char *ptr, int optional)
565 char *start = ptr;
567 if ((ptr = osmtpd_mheader_skip_obs_domain_list(ptr, 0)) == NULL)
568 return optional ? start : NULL;
569 if (ptr++[0] != ':')
570 return optional ? start : NULL;
571 return ptr;
574 char *
575 osmtpd_mheader_skip_addr_spec(char *ptr, int optional)
577 char *start = ptr;
579 if ((ptr = osmtpd_mheader_skip_local_part(ptr, 0)) == NULL)
580 return optional ? start : NULL;
581 if (ptr++[0] != '@')
582 return optional ? start : NULL;
583 if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL)
584 return optional ? start : NULL;
585 return ptr;
588 char *
589 osmtpd_mheader_skip_obs_angle_addr(char *ptr, int optional)
591 char *start = ptr;
593 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
594 if (ptr++[0] != '<')
595 return optional ? start : NULL;
596 if ((ptr = osmtpd_mheader_skip_obs_route(ptr, 0)) == NULL)
597 return optional ? start : NULL;
598 if ((ptr = osmtpd_mheader_skip_addr_spec(ptr, 0)) == NULL)
599 return optional ? start : NULL;
600 if (ptr++[0] != '>')
601 return optional ? start : NULL;
602 return osmtpd_mheader_skip_cfws(ptr, 1);
605 char *
606 osmtpd_mheader_skip_angle_addr(char *ptr, int optional)
608 char *start = ptr;
610 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
611 if (ptr++[0] != '<')
612 return osmtpd_mheader_skip_obs_angle_addr(start, optional);
613 if ((ptr = osmtpd_mheader_skip_addr_spec(ptr, 0)) == NULL)
614 return osmtpd_mheader_skip_obs_angle_addr(start, optional);
615 if (ptr++[0] != '>')
616 return osmtpd_mheader_skip_obs_angle_addr(start, optional);
617 return osmtpd_mheader_skip_cfws(ptr, 1);
620 char *
621 osmtpd_mheader_skip_name_addr(char *ptr, int optional)
623 char *start = ptr;
625 ptr = osmtpd_mheader_skip_display_name(ptr, 1);
626 if ((ptr = osmtpd_mheader_skip_angle_addr(ptr, 0)) == NULL)
627 return optional ? start : NULL;
628 return ptr;
631 char *
632 osmtpd_mheader_domain_uncomment(char *ptr)
634 char *domain0, *domain, *tmp, *end;
636 if (osmtpd_mheader_skip_dot_atom(ptr, 0) != NULL) {
637 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
638 return strndup(ptr,
639 osmtpd_mheader_skip_dot_atom_text(ptr, 0) - ptr);
641 if ((tmp = osmtpd_mheader_skip_domain_literal(ptr, 0)) != NULL) {
642 ptr = osmtpd_mheader_skip_cfws(ptr, 1) + 1;
643 domain0 = domain = strndup(ptr, (size_t)(tmp - ptr));
644 if (domain0 == NULL)
645 return NULL;
646 end = domain0 + (tmp - ptr) + 1;
647 domain++;
648 while (1) {
649 tmp = osmtpd_mheader_skip_fws(domain, 1);
650 if (tmp != domain) {
651 memmove(domain, tmp, end - tmp);
652 end -= (tmp - domain);
654 tmp = osmtpd_mheader_skip_dtext(domain, 0);
655 if (tmp == NULL)
656 break;
657 domain = tmp;
659 /* domain[0] == ']' */
660 domain[0] = '\0';
661 return domain0;
663 return strndup(ptr, osmtpd_mheader_skip_obs_domain(ptr, 1) - ptr);
666 /* Return the domain component of the first mailbox */
667 char *
668 osmtpd_mheader_from_domain(char *ptr)
670 char *tmp;
672 /* from */
673 if (strncasecmp(ptr, "from:", 5) == 0) {
674 ptr += 5;
675 /* obs-from */
676 } else if (strncasecmp(ptr, "from", 4) == 0) {
677 ptr += 4;
678 do {
679 tmp = ptr;
680 } while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL);
681 ptr = tmp;
682 if (ptr++[0] != ':')
683 return NULL;
684 } else {
685 errno = EINVAL;
686 return NULL;
689 /* Both from and obs-from use Mailbox-list CRLF */
690 /* obs-mbox-list has just a prefix compared to mailbox-list */
691 while (1) {
692 tmp = ptr;
693 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
694 if (ptr++[0] != ',') {
695 ptr = tmp;
696 break;
699 /* We're only interested in the first mailbox */
700 if (osmtpd_mheader_skip_name_addr(ptr, 0) != NULL) {
701 ptr = osmtpd_mheader_skip_display_name(ptr, 1);
702 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
703 /* < */
704 ptr++;
705 /* addr-spec */
706 ptr = osmtpd_mheader_skip_local_part(ptr, 0);
707 /* @ */
708 ptr++;
709 return osmtpd_mheader_domain_uncomment(ptr);
711 if (osmtpd_mheader_skip_addr_spec(ptr, 0) != NULL) {
712 ptr = osmtpd_mheader_skip_local_part(ptr, 0);
713 /* @ */
714 ptr++;
715 return osmtpd_mheader_domain_uncomment(ptr);
717 errno = EINVAL;
718 return NULL;