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>
27 char *
28 osmtpd_mheader_skip_sp(char *ptr, int optional)
29 {
30 if (ptr[0] == 0x20)
31 return ptr + 1;
32 return optional ? ptr : NULL;
33 }
35 char *
36 osmtpd_mheader_skip_htab(char *ptr, int optional)
37 {
38 if (ptr[0] == 0x9)
39 return ptr + 1;
40 return optional ? ptr : NULL;
41 }
43 char *
44 osmtpd_mheader_skip_char(char *ptr, int optional)
45 {
46 if (ptr[0] >= 0x01 && ptr[0] <= 0x7f)
47 return ptr + 1;
48 return optional ? ptr : NULL;
49 }
51 char *
52 osmtpd_mheader_skip_ctl(char *ptr, int optional)
53 {
54 if ((ptr[0] >= 0x00 && ptr[0] <= 0x1f) || ptr[0] == 0x7f)
55 return ptr + 1;
56 return optional ? ptr : NULL;
57 }
59 char *
60 osmtpd_mheader_skip_wsp(char *ptr, int optional)
61 {
62 char *start = ptr;
64 if ((ptr = osmtpd_mheader_skip_sp(start, 0)) != NULL ||
65 (ptr = osmtpd_mheader_skip_htab(start, 0)) != NULL)
66 return ptr;
67 return optional ? start : NULL;
68 }
70 char *
71 osmtpd_mheader_skip_crlf(char *ptr, int optional)
72 {
73 if (ptr[0] == 13 && ptr[1] == 10)
74 return ptr + 2;
75 return optional ? ptr : NULL;
76 }
78 char *
79 osmtpd_mheader_skip_vchar(char *ptr, int optional)
80 {
81 if (ptr[0] >= 0x21 && ptr[0] <= 0x7e)
82 return ptr + 1;
83 return optional ? ptr : NULL;
84 }
86 char *
87 osmtpd_mheader_skip_lf(char *ptr, int optional)
88 {
89 if (ptr[0] == 0xa)
90 return ptr + 1;
91 return optional ? ptr : NULL;
92 }
94 char *
95 osmtpd_mheader_skip_cr(char *ptr, int optional)
96 {
97 if (ptr[0] == 0xd)
98 return ptr + 1;
99 return optional ? ptr : NULL;
102 char *
103 osmtpd_mheader_skip_alpha(char *ptr, int optional)
105 if ((ptr[0] >= 0x41 && ptr[0] <= 0x5a) ||
106 (ptr[0] >= 0x61 && ptr[0] <= 0x7a))
107 return ptr + 1;
108 return optional ? ptr : NULL;
111 char *
112 osmtpd_mheader_skip_digit(char *ptr, int optional)
114 if (ptr[0] >= 0x30 && ptr[0] <= 0x39)
115 return ptr + 1;
116 return optional ? ptr : NULL;
119 char *
120 osmtpd_mheader_skip_letdig(char *ptr, int optional)
122 char *start = ptr;
124 if ((ptr = osmtpd_mheader_skip_alpha(start, 0)) == NULL &&
125 (ptr = osmtpd_mheader_skip_digit(start, 0)) == NULL)
126 return optional ? start : NULL;
127 return ptr;
130 char *
131 osmtpd_mheader_skip_ldhstring(char *ptr, int optional)
133 char *start = ptr, *prev;
134 int letdig = 0;
136 while (1) {
137 prev = ptr;
138 if ((ptr = osmtpd_mheader_skip_alpha(prev, 0)) != NULL ||
139 (ptr = osmtpd_mheader_skip_digit(prev, 0)) != NULL) {
140 letdig = 1;
141 continue;
143 if (prev[0] == '-') {
144 letdig = 0;
145 ptr = prev + 1;
146 continue;
148 ptr = prev;
149 break;
151 if (letdig)
152 return ptr;
153 if (ptr == start)
154 return optional ? start : NULL;
155 return ptr;
158 char *
159 osmtpd_mheader_skip_dquote(char *ptr, int optional)
161 if (ptr[0] == 0x22)
162 return ptr + 1;
163 return optional ? ptr : NULL;
166 char *
167 osmtpd_mheader_skip_hexoctet(char *ptr, int optional)
169 char *start = ptr;
171 if (ptr[0] != '=')
172 return optional ? ptr : NULL;
173 ptr++;
174 if (ptr[0] == 'A' || ptr[0] == 'B' || ptr[0] == 'C' || ptr[0] == 'D' ||
175 ptr[0] == 'E' || ptr[0] == 'F')
176 ptr++;
177 else if ((ptr = osmtpd_mheader_skip_digit(ptr, 0)) == NULL)
178 return optional ? start : NULL;
179 if (ptr[0] == 'A' || ptr[0] == 'B' || ptr[0] == 'C' || ptr[0] == 'D' ||
180 ptr[0] == 'E' || ptr[0] == 'F')
181 ptr++;
182 else if ((ptr = osmtpd_mheader_skip_digit(ptr, 0)) == NULL)
183 return optional ? start : NULL;
184 return ptr;
187 char *
188 osmtpd_mheader_skip_obs_fws(char *ptr, int optional)
190 char *start = ptr, *prev;
192 if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL)
193 return optional ? start : NULL;
194 prev = ptr;
195 while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
196 prev = ptr;
198 ptr = prev;
199 while (1) {
200 if ((ptr = osmtpd_mheader_skip_crlf(ptr, 0)) == NULL)
201 return prev;
202 if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL)
203 return prev;
204 prev = ptr;
205 while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
206 prev = ptr;
207 ptr = prev;
211 char *
212 osmtpd_mheader_skip_fws(char *ptr, int optional)
214 char *start = ptr, *prev = ptr;
216 while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
217 prev = ptr;
218 if ((ptr = osmtpd_mheader_skip_crlf(prev, 1)) == prev)
219 ptr = start;
220 if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL)
221 return osmtpd_mheader_skip_obs_fws(start, optional);
222 prev = ptr;
223 while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
224 prev = ptr;
225 return prev;
228 char *
229 osmtpd_mheader_skip_obs_no_ws_ctl(char *ptr, int optional)
231 if ((ptr[0] >= 1 && ptr[0] <= 8) || ptr[0] == 11 || ptr[0] == 12 ||
232 (ptr[0] >= 14 && ptr[0] <= 31) || ptr[0] == 127)
233 return ptr + 1;
234 return optional ? ptr : NULL;
237 char *
238 osmtpd_mheader_skip_obs_ctext(char *ptr, int optional)
240 return osmtpd_mheader_skip_obs_no_ws_ctl(ptr, optional);
243 char *
244 osmtpd_mheader_skip_ctext(char *ptr, int optional)
246 char *start = ptr;
248 if ((ptr[0] >= 33 && ptr[0] <= 39) || (ptr[0] >= 42 && ptr[0] <= 91) ||
249 (ptr[0] >= 93 && ptr[0] <= 126))
250 return ptr + 1;
251 if ((ptr = osmtpd_mheader_skip_obs_ctext(ptr, 0)) != NULL)
252 return ptr;
253 return optional ? start : NULL;
256 char *
257 osmtpd_mheader_skip_obs_qp(char *ptr, int optional)
259 char *start = ptr;
261 if (ptr[0] == '\\' && (
262 (ptr = osmtpd_mheader_skip_obs_no_ws_ctl(start + 1, 0)) != NULL ||
263 (ptr = osmtpd_mheader_skip_lf(start + 1, 0)) != NULL ||
264 (ptr = osmtpd_mheader_skip_cr(start + 1, 0)) != NULL))
265 return ptr;
266 return optional ? start : NULL;
269 char *
270 osmtpd_mheader_skip_quoted_pair(char *ptr, int optional)
272 char *start = ptr;
274 if (ptr[0] == '\\' && (
275 (ptr = osmtpd_mheader_skip_vchar(start + 1, 0)) != NULL ||
276 (ptr = osmtpd_mheader_skip_wsp(start + 1, 0)) != NULL))
277 return ptr;
278 return osmtpd_mheader_skip_obs_qp(start, optional);
281 char *
282 osmtpd_mheader_skip_ccontent(char *ptr, int optional)
284 char *start = ptr;
286 if ((ptr = osmtpd_mheader_skip_ctext(ptr, 0)) != NULL)
287 return ptr;
288 if ((ptr = osmtpd_mheader_skip_quoted_pair(start, 0)) != NULL)
289 return ptr;
290 if ((ptr = osmtpd_mheader_skip_comment(start, 0)) != NULL)
291 return ptr;
292 return optional ? start : NULL;
295 char *
296 osmtpd_mheader_skip_comment(char *ptr, int optional)
298 char *start = ptr;
300 if (ptr++[0] != '(')
301 return optional ? start : NULL;
302 while (1) {
303 ptr = osmtpd_mheader_skip_fws(ptr, 1);
304 if (ptr[0] == ')')
305 return ptr + 1;
306 if ((ptr = osmtpd_mheader_skip_ccontent(ptr, 0)) == NULL)
307 return optional ? start : NULL;
311 char *
312 osmtpd_mheader_skip_cfws(char *ptr, int optional)
314 char *start = ptr, *prev;
316 while (1) {
317 ptr = osmtpd_mheader_skip_fws(ptr, 1);
318 prev = ptr;
319 if ((ptr = osmtpd_mheader_skip_comment(ptr, 0)) == NULL) {
320 ptr = prev;
321 break;
324 return ptr == start && !optional ? NULL : ptr;
327 char *
328 osmtpd_mheader_skip_atext(char *ptr, int optional)
330 char *start = ptr;
332 if ((ptr = osmtpd_mheader_skip_alpha(start, 0)) != NULL ||
333 (ptr = osmtpd_mheader_skip_digit(start, 0)) != NULL)
334 return ptr;
335 ptr = start;
336 if (ptr[0] == '!' || ptr[0] == '#' || ptr[0] == '$' || ptr[0] == '%' ||
337 ptr[0] == '&' || ptr[0] == '\'' || ptr[0] == '*' || ptr[0] == '+' ||
338 ptr[0] == '-' || ptr[0] == '/' || ptr[0] == '=' || ptr[0] == '?' ||
339 ptr[0] == '^' || ptr[0] == '_' || ptr[0] == '`' || ptr[0] == '{' ||
340 ptr[0] == '|' || ptr[0] == '}' || ptr[0] == '~')
341 return ptr + 1;
342 return optional ? start : NULL;
345 char *
346 osmtpd_mheader_skip_atom(char *ptr, int optional)
348 char *start = ptr, *prev;
350 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
351 if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL)
352 return optional ? start : NULL;
353 do {
354 prev = ptr;
355 ptr = osmtpd_mheader_skip_atext(ptr, 1);
356 } while (prev != ptr);
357 return osmtpd_mheader_skip_cfws(ptr, 1);
360 char *
361 osmtpd_mheader_skip_dot_atom_text(char *ptr, int optional)
363 char *start = ptr, *prev;
365 if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL)
366 return optional ? start : NULL;
367 do {
368 prev = ptr;
369 ptr = osmtpd_mheader_skip_atext(ptr, 1);
370 } while (ptr != prev);
372 while (ptr[0] == '.') {
373 ptr++;
374 if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL)
375 return prev;
376 do {
377 prev = ptr;
378 ptr = osmtpd_mheader_skip_atext(ptr, 1);
379 } while (ptr != prev);
381 return ptr;
384 char *
385 osmtpd_mheader_skip_dot_atom(char *ptr, int optional)
387 char *start = ptr;
389 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
390 if ((ptr = osmtpd_mheader_skip_dot_atom_text(ptr, 0)) == NULL)
391 return optional ? start : NULL;
392 return osmtpd_mheader_skip_cfws(ptr, 1);
395 char *
396 osmtpd_mheader_skip_obs_qtext(char *ptr, int optional)
398 return osmtpd_mheader_skip_obs_no_ws_ctl(ptr, optional);
401 char *
402 osmtpd_mheader_skip_qtext(char *ptr, int optional)
404 char *start = ptr;
406 if (ptr[0] == 33 || (ptr[0] >= 35 && ptr[0] <= 91) ||
407 (ptr[0] >= 93 && ptr[0] <= 126))
408 return ptr + 1;
409 if ((ptr = osmtpd_mheader_skip_obs_qtext(ptr, 0)) != NULL)
410 return ptr;
411 return optional ? start : NULL;
414 char *
415 osmtpd_mheader_skip_qcontent(char *ptr, int optional)
417 char *start = ptr;
419 if ((ptr = osmtpd_mheader_skip_qtext(ptr, 0)) != NULL)
420 return ptr;
421 return osmtpd_mheader_skip_quoted_pair(start, optional);
424 char *
425 osmtpd_mheader_skip_quoted_string(char *ptr, int optional)
427 char *start = ptr, *prev;
429 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
430 if ((ptr = osmtpd_mheader_skip_dquote(ptr, 0)) == NULL)
431 return optional ? start : NULL;
432 prev = ptr;
433 while (1) {
434 ptr = osmtpd_mheader_skip_fws(ptr, 1);
435 if ((ptr = osmtpd_mheader_skip_qcontent(ptr, 0)) == NULL)
436 break;
437 prev = ptr;
439 if ((ptr = osmtpd_mheader_skip_dquote(prev, 0)) == NULL)
440 return optional ? start : NULL;
441 return osmtpd_mheader_skip_cfws(ptr, 1);
444 char *
445 osmtpd_mheader_skip_keyword(char *ptr, int optional)
447 return osmtpd_mheader_skip_ldhstring(ptr, optional);
450 char *
451 osmtpd_mheader_skip_word(char *ptr, int optional)
453 char *start = ptr;
455 if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) != NULL)
456 return ptr;
457 return osmtpd_mheader_skip_quoted_string(start, optional);
460 char *
461 osmtpd_mheader_skip_obs_phrase(char *ptr, int optional)
463 char *start = ptr, *prev;
465 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
466 return optional ? start : NULL;
467 while (1) {
468 prev = ptr;
469 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) != NULL)
470 continue;
471 ptr = prev;
472 if (ptr[0] == '.') {
473 ptr++;
474 continue;
476 if ((ptr = osmtpd_mheader_skip_cfws(ptr, 0)) != NULL)
477 continue;
478 return prev;
482 char *
483 osmtpd_mheader_skip_phrase(char *ptr, int optional)
485 /* obs-phrase is a superset of phrae */
486 return osmtpd_mheader_skip_obs_phrase(ptr, optional);
487 #if 0
488 char *start = ptr, *prev;
490 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
491 return optional ? start : NULL;
492 while (1) {
493 prev = ptr;
494 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
495 return prev;
497 #endif
500 char *
501 osmtpd_mheader_skip_obs_local_part(char *ptr, int optional)
503 char *start = ptr, *prev;
505 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
506 return optional ? start : NULL;
507 prev = ptr;
508 while (ptr[0] == '.') {
509 ptr++;
510 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
511 return prev;
512 prev = ptr;
514 return ptr;
517 char *
518 osmtpd_mheader_skip_local_part(char *ptr, int optional)
520 char *start = ptr;
522 if ((ptr = osmtpd_mheader_skip_dot_atom(ptr, 0)) != NULL)
523 return ptr;
524 ptr = start;
525 if ((ptr = osmtpd_mheader_skip_quoted_string(ptr, 0)) != NULL)
526 return ptr;
527 return osmtpd_mheader_skip_obs_local_part(start, optional);
530 char *
531 osmtpd_mheader_skip_subdomain(char *ptr, int optional)
533 char *start = ptr;
535 if ((ptr = osmtpd_mheader_skip_letdig(ptr, 0)) == NULL)
536 return optional ? start : NULL;
537 return osmtpd_mheader_skip_ldhstring(ptr, 1);
540 char *
541 osmtpd_mheader_skip_obs_dtext(char *ptr, int optional)
543 char *start = ptr;
545 if ((ptr = osmtpd_mheader_skip_obs_no_ws_ctl(ptr, 0)) != NULL)
546 return ptr;
547 return osmtpd_mheader_skip_quoted_pair(start, optional);
550 char *
551 osmtpd_mheader_skip_dtext(char *ptr, int optional)
553 if ((ptr[0] >= 33 && ptr[0] <= 90) || (ptr[0] >= 94 && ptr[0] <= 126))
554 return ptr + 1;
555 return osmtpd_mheader_skip_obs_dtext(ptr, optional);
559 char *
560 osmtpd_mheader_skip_domain_literal(char *ptr, int optional)
562 char *start = ptr, *prev;
564 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
565 if (ptr++[0] != '[')
566 return optional ? start : NULL;
567 while (1) {
568 ptr = osmtpd_mheader_skip_fws(ptr, 1);
569 prev = ptr;
570 if ((ptr = osmtpd_mheader_skip_dtext(ptr, 0)) == NULL) {
571 ptr = prev;
572 break;
575 if (ptr[0] != ']')
576 return optional ? start : NULL;
577 ptr++;
578 return osmtpd_mheader_skip_cfws(ptr, 1);
581 char *
582 osmtpd_mheader_skip_obs_domain(char *ptr, int optional)
584 char *start = ptr, *prev;
586 if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) == NULL)
587 return optional ? start : NULL;
588 prev = ptr;
589 while (1) {
590 if (ptr++[0] != '.')
591 return prev;
592 if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) == NULL)
593 return prev;
594 prev = ptr;
598 char *
599 osmtpd_mheader_skip_domain(char *ptr, int optional)
601 char *start = ptr;
603 if ((ptr = osmtpd_mheader_skip_dot_atom(start, 0)) != NULL)
604 return ptr;
605 if ((ptr = osmtpd_mheader_skip_domain_literal(start, 0)) != NULL)
606 return ptr;
607 return osmtpd_mheader_skip_obs_domain(start, optional);
610 char *
611 osmtpd_mheader_skip_display_name(char *ptr, int optional)
613 return osmtpd_mheader_skip_phrase(ptr, optional);
616 char *
617 osmtpd_mheader_skip_obs_domain_list(char *ptr, int optional)
619 char *start = ptr, *prev = ptr;
621 while (1) {
622 if (ptr[0] == ',') {
623 ptr++;
624 prev = ptr;
625 continue;
626 } else if ((ptr = osmtpd_mheader_skip_cfws(ptr, 0)) != NULL) {
627 prev = ptr;
628 continue;
630 break;
632 ptr = prev;
634 if (ptr++[0] != '@')
635 return optional ? start : NULL;
636 if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL)
637 return optional ? start : NULL;
638 while (1) {
639 if (ptr[0] != ',')
640 break;
641 ptr++;
642 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
643 if (ptr[0] != '@')
644 continue;
645 prev = ptr;
646 if ((ptr = osmtpd_mheader_skip_domain(ptr + 1, 0)) == NULL) {
647 ptr = prev;
648 break;
651 return ptr;
654 char *
655 osmtpd_mheader_skip_obs_route(char *ptr, int optional)
657 char *start = ptr;
659 if ((ptr = osmtpd_mheader_skip_obs_domain_list(ptr, 0)) == NULL)
660 return optional ? start : NULL;
661 if (ptr++[0] != ':')
662 return optional ? start : NULL;
663 return ptr;
666 char *
667 osmtpd_mheader_skip_addr_spec(char *ptr, int optional)
669 char *start = ptr;
671 if ((ptr = osmtpd_mheader_skip_local_part(ptr, 0)) == NULL)
672 return optional ? start : NULL;
673 if (ptr++[0] != '@')
674 return optional ? start : NULL;
675 if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL)
676 return optional ? start : NULL;
677 return ptr;
680 char *
681 osmtpd_mheader_skip_obs_angle_addr(char *ptr, int optional)
683 char *start = ptr;
685 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
686 if (ptr++[0] != '<')
687 return optional ? start : NULL;
688 if ((ptr = osmtpd_mheader_skip_obs_route(ptr, 0)) == NULL)
689 return optional ? start : NULL;
690 if ((ptr = osmtpd_mheader_skip_addr_spec(ptr, 0)) == NULL)
691 return optional ? start : NULL;
692 if (ptr++[0] != '>')
693 return optional ? start : NULL;
694 return osmtpd_mheader_skip_cfws(ptr, 1);
697 char *
698 osmtpd_mheader_skip_angle_addr(char *ptr, int optional)
700 char *start = ptr;
702 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
703 if (ptr++[0] != '<')
704 return osmtpd_mheader_skip_obs_angle_addr(start, optional);
705 if ((ptr = osmtpd_mheader_skip_addr_spec(ptr, 0)) == NULL)
706 return osmtpd_mheader_skip_obs_angle_addr(start, optional);
707 if (ptr++[0] != '>')
708 return osmtpd_mheader_skip_obs_angle_addr(start, optional);
709 return osmtpd_mheader_skip_cfws(ptr, 1);
712 char *
713 osmtpd_mheader_skip_name_addr(char *ptr, int optional)
715 char *start = ptr;
717 ptr = osmtpd_mheader_skip_display_name(ptr, 1);
718 if ((ptr = osmtpd_mheader_skip_angle_addr(ptr, 0)) == NULL)
719 return optional ? start : NULL;
720 return ptr;
723 char *
724 osmtpd_mheader_skip_alphadigitps(char *ptr, int optional)
726 char *end;
728 if ((end = osmtpd_mheader_skip_alpha(ptr, 0)) == NULL &&
729 (end = osmtpd_mheader_skip_digit(ptr, 0)) == NULL &&
730 ptr[0] != '+' && ptr[0] != '/')
731 return optional ? ptr : NULL;
732 return end == NULL ? ptr + 1 : end;
735 char *
736 osmtpd_mheader_skip_base64string(char *ptr, int optional)
738 char *start = ptr;
740 if ((ptr = osmtpd_mheader_skip_alphadigitps(ptr, 0)) == NULL)
741 return optional ? start : NULL;
742 while (1) {
743 start = ptr;
744 ptr = osmtpd_mheader_skip_fws(ptr, 1);
745 if ((ptr = osmtpd_mheader_skip_alphadigitps(ptr, 0)) == NULL)
746 break;
748 ptr = start;
749 ptr = osmtpd_mheader_skip_fws(ptr, 1);
750 if (ptr[0] == '=') {
751 ptr++;
752 start = ptr;
753 ptr = osmtpd_mheader_skip_fws(ptr, 1);
754 if (ptr[0] == '=')
755 ptr++;
756 else
757 ptr = start;
758 } else
759 ptr = start;
760 return ptr;
763 char *
764 osmtpd_mheader_skip_hyphenatedword(char *ptr, int optional)
766 char *start = ptr, *end, *hyphen;
768 if ((ptr = osmtpd_mheader_skip_alpha(ptr, 0)) == NULL)
769 return optional ? start : NULL;
771 end = ptr;
772 while (1) {
773 if (ptr[0] == '-') {
774 hyphen = hyphen == NULL ? ptr - 1 : hyphen;
775 ptr++;
776 continue;
778 start = ptr;
779 if ((ptr = osmtpd_mheader_skip_alpha(start, 0)) == NULL &&
780 (ptr = osmtpd_mheader_skip_digit(start, 0)) == NULL)
781 break;
782 hyphen = NULL;
783 end = ptr;
786 return hyphen == NULL ? end : hyphen;
789 char *
790 osmtpd_mheader_skip_ftext(char *ptr, int optional)
792 if ((ptr[0] >= 33 && ptr[0] <= 57) ||
793 (ptr[0] >= 59 && ptr[0] <= 126))
794 return ptr + 1;
795 return optional ? ptr : NULL;
798 char *
799 osmtpd_mheader_skip_fieldname(char *ptr, int optional)
801 char *start = ptr;
803 if ((ptr = osmtpd_mheader_skip_ftext(ptr, 0)) == NULL)
804 return optional ? start : NULL;
805 while (1) {
806 start = ptr;
807 if ((ptr = osmtpd_mheader_skip_ftext(ptr, 0)) == NULL)
808 return start;
812 char *
813 osmtpd_mheader_skip_hdrname(char *ptr, int optional)
815 return osmtpd_mheader_skip_fieldname(ptr, optional);
818 char *
819 osmtpd_mheader_skip_tspecials(char *ptr, int optional)
821 if (ptr[0] == '(' || ptr[0] == ')' || ptr[0] == '<' || ptr[0] == '>' ||
822 ptr[0] == '@' || ptr[0] == ',' || ptr[0] == ';' || ptr[0] == ':' ||
823 ptr[0] == '\\' || ptr[0] == '"' || ptr[0] == '/' || ptr[0] == '[' ||
824 ptr[0] == ']' || ptr[0] == '?' || ptr[0] == '=')
825 return ptr + 1;
826 return optional ? ptr : NULL;
829 char *
830 osmtpd_mheader_skip_token(char *ptr, int optional)
832 char *start;
833 int first = 1;
835 while (1) {
836 start = ptr;
837 if ((ptr = osmtpd_mheader_skip_char(start, 0)) != NULL &&
838 osmtpd_mheader_skip_sp(start, 0) == NULL &&
839 osmtpd_mheader_skip_ctl(start, 0) == NULL &&
840 osmtpd_mheader_skip_tspecials(start, 0) == NULL) {
841 first = 0;
842 continue;
844 return optional || !first ? start : NULL;
848 char *
849 osmtpd_mheader_skip_value(char *ptr, int optional)
851 char *start = ptr;
853 if ((ptr = osmtpd_mheader_skip_token(start, 0)) != NULL)
854 return ptr;
855 if ((ptr = osmtpd_mheader_skip_quoted_string(start, 0)) != NULL)
856 return ptr;
857 return optional ? start : NULL;
860 char *
861 osmtpd_mheader_skip_dkim_safe_char(char *ptr, int optional)
863 if ((ptr[0] >= 0x21 && ptr[0] <= 0x3a) || ptr[0] == 0x3c ||
864 (ptr[0] >= 0x3e && ptr[0] <= 0x7e))
865 return ptr + 1;
866 return optional ? ptr : NULL;
869 char *
870 osmtpd_mheader_skip_dkim_quoted_printable(char *ptr, int optional)
872 char *start;
874 while (1) {
875 start = ptr;
876 if ((ptr = osmtpd_mheader_skip_fws(start, 0)) != NULL)
877 continue;
878 if ((ptr = osmtpd_mheader_skip_hexoctet(start, 0)) != NULL)
879 continue;
880 ptr = osmtpd_mheader_skip_dkim_safe_char(start, 0);
881 if (ptr == NULL)
882 break;
884 return start;
887 char *
888 osmtpd_mheader_skip_dkim_qp_hdr_value(char *ptr, int optional)
890 return osmtpd_mheader_skip_dkim_quoted_printable(ptr, optional);
893 char *
894 osmtpd_mheader_skip_dkimsig_alnumpunc(char *ptr, int optional)
896 char *start = ptr;
898 if ((ptr = osmtpd_mheader_skip_alpha(start, 0)) != NULL)
899 return ptr;
900 if ((ptr = osmtpd_mheader_skip_digit(start, 0)) != NULL)
901 return ptr;
902 if (start[0] == '_')
903 return start + 1;
904 return optional ? start : NULL;
907 char *
908 osmtpd_mheader_skip_dkimsig_valchar(char *ptr, int optional)
910 if ((ptr[0] >= 0x21 && ptr[0] <= 0x3A) ||
911 (ptr[0] >= 0x3C && ptr[0] <= 0x7E))
912 return ptr + 1;
913 return optional ? ptr : NULL;
916 char *
917 osmtpd_mheader_skip_dkimsig_tval(char *ptr, int optional)
919 char *start = ptr, *prev;
921 if ((ptr = osmtpd_mheader_skip_dkimsig_valchar(ptr, 0)) == NULL)
922 return optional ? start : NULL;
923 prev = ptr;
924 while ((ptr = osmtpd_mheader_skip_dkimsig_valchar(ptr, 0)) != NULL)
925 prev = ptr;
926 return prev;
929 char *
930 osmtpd_mheader_skip_dkimsig_tagvalue(char *ptr, int optional)
932 char *start = ptr, *prev;
934 if ((ptr = osmtpd_mheader_skip_dkimsig_tval(ptr, 0)) == NULL)
935 return start;
937 while (1) {
938 start = ptr;
939 /* FWS contains WSP */
940 if ((ptr = osmtpd_mheader_skip_fws(ptr, 0)) == NULL)
941 return start;
942 prev = ptr;
943 while ((ptr = osmtpd_mheader_skip_fws(ptr, 0)) != NULL)
944 prev = ptr;
945 ptr = prev;
946 if ((ptr = osmtpd_mheader_skip_dkimsig_tval(ptr, 0)) == NULL)
947 return start;
951 char *
952 osmtpd_mheader_skip_dkimsig_tagname(char *ptr, int optional)
954 char *start = ptr, *prev;
956 if ((ptr = osmtpd_mheader_skip_alpha(ptr, 0)) == NULL)
957 return optional ? start : NULL;
958 prev = ptr;
959 while ((ptr = osmtpd_mheader_skip_dkimsig_alnumpunc(ptr, 0)) != NULL)
960 prev = ptr;
961 return prev;
964 char *
965 osmtpd_mheader_skip_dkimsig_tagspec(char *ptr, int optional)
967 char *start = ptr;
969 ptr = osmtpd_mheader_skip_fws(ptr, 1);
970 if ((ptr = osmtpd_mheader_skip_dkimsig_tagname(ptr, 0)) == NULL)
971 return optional ? start : NULL;
972 ptr = osmtpd_mheader_skip_fws(ptr, 1);
973 if (ptr[0] != '=')
974 return optional ? start : NULL;
975 ptr++;
976 ptr = osmtpd_mheader_skip_fws(ptr, 1);
977 if ((ptr = osmtpd_mheader_skip_dkimsig_tagvalue(ptr, 0)) == NULL)
978 return optional ? start : NULL;
979 return osmtpd_mheader_skip_fws(ptr, 1);
982 char *
983 osmtpd_mheader_skip_dkimsig_taglist(char *ptr, int optional)
985 char *start = ptr;
987 if ((ptr = osmtpd_mheader_skip_dkimsig_tagspec(ptr, 0)) == NULL)
988 return optional ? start : NULL;
989 while (1) {
990 /* Starting or trailing ';' */
991 if (ptr[0] != ';')
992 return ptr;
993 ptr++;
994 start = ptr;
995 if ((ptr = osmtpd_mheader_skip_dkimsig_tagspec(ptr, 0)) == NULL)
996 return start;
1000 char *
1001 osmtpd_mheader_skip_dkimsig_xsigatagh(char *ptr, int optional)
1003 char *start = ptr, *prev, *end;
1005 if ((ptr = osmtpd_mheader_skip_alpha(ptr, 0)) == NULL)
1006 return optional ? start : NULL;
1007 prev = ptr;
1008 while ((end = osmtpd_mheader_skip_alpha(ptr, 0)) != NULL ||
1009 (end = osmtpd_mheader_skip_digit(ptr, 0)) != NULL) {
1010 ptr = end;
1011 prev = end;
1013 return prev;
1016 char *
1017 osmtpd_mheader_skip_dkimsig_xsigatagk(char *ptr, int optional)
1019 char *start = ptr, *prev, *end;
1021 if ((ptr = osmtpd_mheader_skip_alpha(ptr, 0)) == NULL)
1022 return optional ? start : NULL;
1023 prev = ptr;
1024 while ((end = osmtpd_mheader_skip_alpha(ptr, 0)) != NULL ||
1025 (end = osmtpd_mheader_skip_digit(ptr, 0)) != NULL) {
1026 ptr = end;
1027 prev = end;
1029 return prev;
1032 char *
1033 osmtpd_mheader_skip_dkimsig_sigatagh(char *ptr, int optional)
1035 /* rsa / ed25519 covered by x-sig-a-tag-h */
1036 return osmtpd_mheader_skip_dkimsig_xsigatagh(ptr, optional);
1039 char *
1040 osmtpd_mheader_skip_dkimsig_sigatagk(char *ptr, int optional)
1042 /* sha1 / sha256 covered by x-sig-a-tag-k */
1043 return osmtpd_mheader_skip_dkimsig_xsigatagk(ptr, optional);
1046 char *
1047 osmtpd_mheader_skip_dkimsig_sigatagalg(char *ptr, int optional)
1049 char *start = ptr;
1051 if ((ptr = osmtpd_mheader_skip_dkimsig_sigatagk(ptr, 0)) == NULL)
1052 return optional ? start : NULL;
1053 if (ptr[0] != '-')
1054 return optional ? start : NULL;
1055 ptr++;
1056 if ((ptr = osmtpd_mheader_skip_dkimsig_sigatagh(ptr, 0)) == NULL)
1057 return optional ? start : NULL;
1058 return ptr;
1061 char *
1062 osmtpd_mheader_skip_dkimsig_xsigctagalg(char *ptr, int optional)
1064 return osmtpd_mheader_skip_hyphenatedword(ptr, optional);
1067 char *
1068 osmtpd_mheader_skip_dkimsig_sigctagalg(char *ptr, int optional)
1070 /* simple / relaxed covered by x-sig-c-tag-alga */
1071 return osmtpd_mheader_skip_dkimsig_xsigctagalg(ptr, optional);
1074 char *
1075 osmtpd_mheader_skip_dkimsig_xkeyhtagalg(char *ptr, int optional)
1077 return osmtpd_mheader_skip_hyphenatedword(ptr, optional);
1080 char *
1081 osmtpd_mheader_skip_dkimsig_keyhtagalg(char *ptr, int optional)
1083 /* sha1 / sha256 covered by x-key-h-tag-alg */
1084 return osmtpd_mheader_skip_dkimsig_xkeyhtagalg(ptr, optional);
1087 char *
1088 osmtpd_mheader_skip_dkimsig_keyhtagvalue(char *ptr, int optional)
1090 char *start = ptr;
1092 if ((ptr = osmtpd_mheader_skip_dkimsig_keyhtagalg(ptr, 0)) == NULL)
1093 return optional ? start : NULL;
1094 while (1) {
1095 start = ptr;
1096 ptr = osmtpd_mheader_skip_fws(ptr, 1);
1097 if (ptr[0] != ':')
1098 return start;
1099 ptr = osmtpd_mheader_skip_fws(ptr + 1, 1);
1100 ptr = osmtpd_mheader_skip_dkimsig_keyhtagalg(ptr, 0);
1101 if (ptr == NULL)
1102 return start;
1106 char *
1107 osmtpd_mheader_skip_dkimsig_xsigqtagargs(char *ptr, int optional)
1109 return osmtpd_mheader_skip_dkim_qp_hdr_value(ptr, optional);
1112 char *
1113 osmtpd_mheader_skip_dkimsig_xsigqtagtype(char *ptr, int optional)
1115 return osmtpd_mheader_skip_hyphenatedword(ptr, optional);
1118 char *
1119 osmtpd_mheader_skip_dkimsig_sigqtagmethod(char *ptr, int optional)
1121 char *start = ptr;
1123 /* dns/txt covered by x-sig-q-tag-type ["/" x-sig-q-tag-args] */
1124 if ((ptr = osmtpd_mheader_skip_dkimsig_xsigqtagtype(ptr, 0)) == NULL)
1125 return optional ? start : NULL;
1126 start = ptr;
1127 if (ptr[0] != '/')
1128 return ptr;
1129 if ((ptr = osmtpd_mheader_skip_dkimsig_xsigqtagargs(ptr, 0)) == NULL)
1130 return start;
1131 return ptr;
1134 char *
1135 osmtpd_mheader_skip_dkimsig_sigztagcopy(char *ptr, int optional)
1137 char *start = ptr;
1139 if ((ptr = osmtpd_mheader_skip_hdrname(ptr, 0)) == NULL)
1140 return optional ? start : NULL;
1141 ptr = osmtpd_mheader_skip_fws(ptr, 1);
1142 if (ptr[0] != ':')
1143 return optional ? start : NULL;
1144 if ((ptr = osmtpd_mheader_skip_dkim_qp_hdr_value(ptr, 0)) == NULL)
1145 return optional ? start : NULL;
1146 return ptr;
1149 char *
1150 osmtpd_mheader_skip_dkimsig_sigztagvalue(char *ptr, int optional)
1152 char *start = ptr;
1154 if ((ptr = osmtpd_mheader_skip_dkimsig_sigztagcopy(ptr, 0)) == NULL)
1155 return optional ? start : NULL;
1156 while (1) {
1157 start = ptr;
1158 if (ptr[0] != '|')
1159 return start;
1160 osmtpd_mheader_skip_fws(ptr + 1, 1);
1161 ptr = osmtpd_mheader_skip_dkimsig_sigztagcopy(ptr, 0);
1162 if (ptr == NULL)
1163 return start;
1167 char *
1168 osmtpd_mheader_skip_dkimsig_xkeystagtype(char *ptr, int optional)
1170 return osmtpd_mheader_skip_hyphenatedword(ptr, optional);
1173 char *
1174 osmtpd_mheader_skip_dkimsig_keystagtype(char *ptr, int optional)
1176 if (ptr[0] == '*')
1177 return ptr + 1;
1178 /* email covered by x-key-s-tag-type */
1179 return osmtpd_mheader_skip_dkimsig_xkeystagtype(ptr, optional);
1182 char *
1183 osmtpd_mheader_skip_dkimsig_keystagvalue(char *ptr, int optional)
1185 char *start = ptr;
1187 if ((ptr = osmtpd_mheader_skip_dkimsig_keystagtype(ptr, 0)) == NULL)
1188 return optional ? start : NULL;
1189 while (1) {
1190 start = ptr;
1191 ptr = osmtpd_mheader_skip_fws(ptr, 1);
1192 if (ptr[0] != ':')
1193 return start;
1194 ptr = osmtpd_mheader_skip_fws(ptr + 1, 1);
1195 ptr = osmtpd_mheader_skip_dkimsig_keystagtype(ptr, 0);
1196 if (ptr == NULL)
1197 return start;
1202 char *
1203 osmtpd_mheader_skip_dkimsig_xkeyttagflag(char *ptr, int optional)
1205 return osmtpd_mheader_skip_hyphenatedword(ptr, optional);
1208 char *
1209 osmtpd_mheader_skip_dkimsig_keyttagflag(char *ptr, int optional)
1211 /* y / s covered by x-key-t-tag-flag */
1212 return osmtpd_mheader_skip_dkimsig_xkeyttagflag(ptr, optional);
1215 char *
1216 osmtpd_mheader_skip_dkimsig_keyttagvalue(char *ptr, int optional)
1218 char *start = ptr;
1220 if ((ptr = osmtpd_mheader_skip_dkimsig_keyttagflag(ptr, 0)) == NULL)
1221 return optional ? start : NULL;
1222 while (1) {
1223 start = ptr;
1224 ptr = osmtpd_mheader_skip_fws(ptr, 1);
1225 if (ptr[0] != ':')
1226 return start;
1227 ptr = osmtpd_mheader_skip_fws(ptr + 1, 1);
1228 ptr = osmtpd_mheader_skip_dkimsig_keyttagflag(ptr, 0);
1229 if (ptr == NULL)
1230 return start;
1234 char *
1235 osmtpd_mheader_skip_dkimsig_selector(char *ptr, int optional)
1237 char *start = ptr;
1239 if ((ptr = osmtpd_mheader_skip_subdomain(ptr, 0)) == NULL)
1240 return optional ? start : NULL;
1241 while (1) {
1242 start = ptr;
1243 if (ptr[0] != '.')
1244 return start;
1245 ptr++;
1246 if ((ptr = osmtpd_mheader_skip_subdomain(ptr, 0)) == NULL)
1247 return start;
1251 char *
1252 osmtpd_mheader_skip_ar_pvalue(char *ptr, int optional)
1254 char *start = ptr, *tmp;
1256 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
1257 if ((tmp = osmtpd_mheader_skip_value(ptr, 0)) != NULL)
1258 return tmp;
1259 ptr = osmtpd_mheader_skip_local_part(ptr, 1);
1260 if (ptr[0] == '@')
1261 ptr++;
1262 if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL)
1263 return optional ? start : NULL;
1264 return ptr;
1267 char *
1268 osmtpd_mheader_domain_uncomment(char *ptr)
1270 char *domain0, *domain, *tmp, *end;
1272 if (osmtpd_mheader_skip_dot_atom(ptr, 0) != NULL) {
1273 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
1274 return strndup(ptr,
1275 osmtpd_mheader_skip_dot_atom_text(ptr, 0) - ptr);
1277 if ((tmp = osmtpd_mheader_skip_domain_literal(ptr, 0)) != NULL) {
1278 ptr = osmtpd_mheader_skip_cfws(ptr, 1) + 1;
1279 domain0 = domain = strndup(ptr, (size_t)(tmp - ptr));
1280 if (domain0 == NULL)
1281 return NULL;
1282 end = domain0 + (tmp - ptr) + 1;
1283 domain++;
1284 while (1) {
1285 tmp = osmtpd_mheader_skip_fws(domain, 1);
1286 if (tmp != domain) {
1287 memmove(domain, tmp, end - tmp);
1288 end -= (tmp - domain);
1290 tmp = osmtpd_mheader_skip_dtext(domain, 0);
1291 if (tmp == NULL)
1292 break;
1293 domain = tmp;
1295 /* domain[0] == ']' */
1296 domain[0] = '\0';
1297 return domain0;
1299 return strndup(ptr, osmtpd_mheader_skip_obs_domain(ptr, 1) - ptr);
1302 /* Return the domain component of the first mailbox */
1303 char *
1304 osmtpd_mheader_from_domain(char *ptr)
1306 char *tmp;
1308 /* from */
1309 if (strncasecmp(ptr, "from:", 5) == 0) {
1310 ptr += 5;
1311 /* obs-from */
1312 } else if (strncasecmp(ptr, "from", 4) == 0) {
1313 ptr += 4;
1314 do {
1315 tmp = ptr;
1316 } while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL);
1317 ptr = tmp;
1318 if (ptr++[0] != ':')
1319 return NULL;
1320 } else {
1321 errno = EINVAL;
1322 return NULL;
1325 /* Both from and obs-from use Mailbox-list CRLF */
1326 /* obs-mbox-list has just a prefix compared to mailbox-list */
1327 while (1) {
1328 tmp = ptr;
1329 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
1330 if (ptr++[0] != ',') {
1331 ptr = tmp;
1332 break;
1335 /* We're only interested in the first mailbox */
1336 if (osmtpd_mheader_skip_name_addr(ptr, 0) != NULL) {
1337 ptr = osmtpd_mheader_skip_display_name(ptr, 1);
1338 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
1339 /* < */
1340 ptr++;
1341 /* addr-spec */
1342 ptr = osmtpd_mheader_skip_local_part(ptr, 0);
1343 /* @ */
1344 ptr++;
1345 return osmtpd_mheader_domain_uncomment(ptr);
1347 if (osmtpd_mheader_skip_addr_spec(ptr, 0) != NULL) {
1348 ptr = osmtpd_mheader_skip_local_part(ptr, 0);
1349 /* @ */
1350 ptr++;
1351 return osmtpd_mheader_domain_uncomment(ptr);
1353 errno = EINVAL;
1354 return NULL;
1357 char *
1358 osmtpd_mheader_quoted_string_normalize(char *ptr)
1360 char *end;
1361 size_t d = 0, s;
1363 end = osmtpd_mheader_skip_cfws(ptr, 1);
1364 s = end - ptr;
1365 if (osmtpd_mheader_skip_dquote(end, 0) == NULL)
1366 return NULL;
1367 ptr[d++] = ptr[s++];
1368 while (ptr[s] != '\0') {
1369 if (osmtpd_mheader_skip_quoted_pair(ptr + s, 0) != NULL) {
1370 end = osmtpd_mheader_skip_qtext(ptr + s + 1, 0);
1371 if (end != NULL)
1372 s++;
1373 else
1374 ptr[d++] = ptr[s++];
1375 ptr[d++] = ptr[s++];
1376 continue;
1377 } else if (osmtpd_mheader_skip_qtext(ptr + s, 0) != NULL) {
1378 ptr[d++] = ptr[s++];
1379 } else if ((end = osmtpd_mheader_skip_fws(
1380 ptr + s, 0)) != NULL) {
1381 ptr[d++] = ' ';
1382 s = end - ptr;
1383 } else
1384 return NULL;
1386 if (osmtpd_mheader_skip_dquote(end, 0) == NULL)
1387 return NULL;
1388 ptr[d++] = ptr[s++];
1389 end = osmtpd_mheader_skip_cfws(ptr + s, 1);
1390 if (end[0] != '\0')
1391 return NULL;
1392 ptr[d] = '\0';
1393 return ptr;