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 char *
26 osmtpd_mheader_skip_sp(char *ptr, int optional)
27 {
28 if (ptr[0] == 0x20)
29 return ptr + 1;
30 return optional ? ptr : NULL;
31 }
33 char *
34 osmtpd_mheader_skip_htab(char *ptr, int optional)
35 {
36 if (ptr[0] == 0x9)
37 return ptr + 1;
38 return optional ? ptr : NULL;
39 }
41 char *
42 osmtpd_mheader_skip_wsp(char *ptr, int optional)
43 {
44 char *start = ptr;
46 if ((ptr = osmtpd_mheader_skip_sp(start, 0)) != NULL ||
47 (ptr = osmtpd_mheader_skip_htab(start, 0)) != NULL)
48 return ptr;
49 return optional ? ptr : NULL;
50 }
52 char *
53 osmtpd_mheader_skip_crlf(char *ptr, int optional)
54 {
55 if (ptr[0] == 13 && ptr[1] == 10)
56 return ptr + 2;
57 return optional ? ptr : NULL;
58 }
60 char *
61 osmtpd_mheader_skip_vchar(char *ptr, int optional)
62 {
63 if (ptr[0] >= 0x21 && ptr[0] <= 0x7e)
64 return ptr + 1;
65 return optional ? ptr : NULL;
66 }
68 char *
69 osmtpd_mheader_skip_lf(char *ptr, int optional)
70 {
71 if (ptr[0] == 0xa)
72 return ptr + 1;
73 return optional ? ptr : NULL;
74 }
76 char *
77 osmtpd_mheader_skip_cr(char *ptr, int optional)
78 {
79 if (ptr[0] == 0xd)
80 return ptr + 1;
81 return optional ? ptr : NULL;
82 }
84 char *
85 osmtpd_mheader_skip_alpha(char *ptr, int optional)
86 {
87 if ((ptr[0] >= 0x41 && ptr[0] <= 0x5a) ||
88 (ptr[0] >= 0x61 && ptr[0] <= 0x7a))
89 return ptr + 1;
90 return optional ? ptr : NULL;
91 }
93 char *
94 osmtpd_mheader_skip_digit(char *ptr, int optional)
95 {
96 if (ptr[0] >= 0x30 && ptr[0] <= 0x39)
97 return ptr + 1;
98 return optional ? ptr : NULL;
99 }
101 char *
102 osmtpd_mheader_skip_dquote(char *ptr, int optional)
104 if (ptr[0] == 0x22)
105 return ptr + 1;
106 return optional ? ptr : NULL;
109 char *
110 osmtpd_mheader_skip_obs_fws(char *ptr, int optional)
112 char *start = ptr, *prev;
114 if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL)
115 return optional ? start : NULL;
116 prev = ptr;
117 while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
118 prev = ptr;
120 ptr = prev;
121 while (1) {
122 if ((ptr = osmtpd_mheader_skip_crlf(ptr, 0)) == NULL)
123 return prev;
124 if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL)
125 return prev;
126 prev = ptr;
127 while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
128 prev = ptr;
129 ptr = prev;
133 char *
134 osmtpd_mheader_skip_fws(char *ptr, int optional)
136 char *start = ptr, *prev = ptr;
138 while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
139 prev = ptr;
140 if ((ptr = osmtpd_mheader_skip_crlf(prev, 1)) == prev)
141 ptr = start;
142 if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL)
143 return osmtpd_mheader_skip_obs_fws(start, optional);
144 prev = ptr;
145 while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
146 prev = ptr;
147 return prev;
150 char *
151 osmtpd_mheader_skip_obs_no_ws_ctl(char *ptr, int optional)
153 if ((ptr[0] >= 1 && ptr[0] <= 8) || ptr[0] == 11 || ptr[0] == 12 ||
154 (ptr[0] >= 14 && ptr[0] <= 31) || ptr[0] == 127)
155 return ptr + 1;
156 return optional ? ptr : NULL;
159 char *
160 osmtpd_mheader_skip_obs_ctext(char *ptr, int optional)
162 return osmtpd_mheader_skip_obs_no_ws_ctl(ptr, optional);
165 char *
166 osmtpd_mheader_skip_ctext(char *ptr, int optional)
168 char *start = ptr;
170 if ((ptr[0] >= 33 && ptr[0] <= 39) || (ptr[0] >= 42 && ptr[0] <= 91) ||
171 (ptr[0] >= 93 && ptr[0] <= 126))
172 return ptr + 1;
173 if ((ptr = osmtpd_mheader_skip_obs_ctext(ptr, 0)) != NULL)
174 return ptr;
175 return optional ? start : NULL;
178 char *
179 osmtpd_mheader_skip_obs_qp(char *ptr, int optional)
181 char *start = ptr;
183 if (ptr[0] == '\\' && (
184 (ptr = osmtpd_mheader_skip_obs_no_ws_ctl(start + 1, 0)) != NULL ||
185 (ptr = osmtpd_mheader_skip_lf(start + 1, 0)) != NULL ||
186 (ptr = osmtpd_mheader_skip_cr(start + 1, 0)) != NULL))
187 return ptr;
188 return optional ? start : NULL;
191 char *
192 osmtpd_mheader_skip_quoted_pair(char *ptr, int optional)
194 char *start = ptr;
196 if (ptr[0] == '\\' && (
197 (ptr = osmtpd_mheader_skip_vchar(start + 1, 0)) != NULL ||
198 (ptr = osmtpd_mheader_skip_wsp(start + 1, 0)) != NULL))
199 return ptr;
200 return osmtpd_mheader_skip_obs_qp(start, optional);
203 char *
204 osmtpd_mheader_skip_ccontent(char *ptr, int optional)
206 char *start = ptr;
208 if ((ptr = osmtpd_mheader_skip_ctext(ptr, 0)) != NULL)
209 return ptr;
210 if ((ptr = osmtpd_mheader_skip_quoted_pair(start, 0)) != NULL)
211 return ptr;
212 if ((ptr = osmtpd_mheader_skip_comment(start, 0)) != NULL)
213 return ptr;
214 return optional ? start : NULL;
217 char *
218 osmtpd_mheader_skip_comment(char *ptr, int optional)
220 char *start = ptr;
222 if (ptr++[0] != '(')
223 return optional ? start : NULL;
224 while (1) {
225 ptr = osmtpd_mheader_skip_fws(ptr, 1);
226 if (ptr[0] == ')')
227 return ptr + 1;
228 if ((ptr = osmtpd_mheader_skip_ccontent(ptr, 0)) == NULL)
229 return optional ? start : NULL;
233 char *
234 osmtpd_mheader_skip_cfws(char *ptr, int optional)
236 char *start = ptr, *prev;
238 while (1) {
239 ptr = osmtpd_mheader_skip_fws(ptr, 1);
240 prev = ptr;
241 if ((ptr = osmtpd_mheader_skip_comment(ptr, 0)) == NULL) {
242 ptr = prev;
243 break;
246 return ptr == start && !optional ? NULL : ptr;
249 char *
250 osmtpd_mheader_skip_atext(char *ptr, int optional)
252 char *start = ptr;
254 if ((ptr = osmtpd_mheader_skip_alpha(start, 0)) != NULL ||
255 (ptr = osmtpd_mheader_skip_digit(start, 0)) != NULL)
256 return ptr;
257 ptr = start;
258 if (ptr[0] == '!' || ptr[0] == '#' || ptr[0] == '$' || ptr[0] == '%' ||
259 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] == '~')
263 return ptr + 1;
264 return optional ? start : NULL;
267 char *
268 osmtpd_mheader_skip_atom(char *ptr, int optional)
270 char *start = ptr, *prev;
272 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
273 if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL)
274 return optional ? start : NULL;
275 do {
276 prev = ptr;
277 ptr = osmtpd_mheader_skip_atext(ptr, 1);
278 } while (prev != ptr);
279 return osmtpd_mheader_skip_cfws(ptr, 1);
282 char *
283 osmtpd_mheader_skip_dot_atom_text(char *ptr, int optional)
285 char *start = ptr, *prev;
287 if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL)
288 return optional ? start : NULL;
289 do {
290 prev = ptr;
291 ptr = osmtpd_mheader_skip_atext(ptr, 1);
292 } while (ptr != prev);
294 while (ptr[0] == '.') {
295 ptr++;
296 if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL)
297 return prev;
298 do {
299 prev = ptr;
300 ptr = osmtpd_mheader_skip_atext(ptr, 1);
301 } while (ptr != prev);
303 return ptr;
306 char *
307 osmtpd_mheader_skip_dot_atom(char *ptr, int optional)
309 char *start = ptr;
311 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
312 if ((ptr = osmtpd_mheader_skip_dot_atom_text(ptr, 0)) == NULL)
313 return optional ? start : NULL;
314 return osmtpd_mheader_skip_cfws(ptr, 1);
318 char *
319 osmtpd_mheader_skip_obs_qtext(char *ptr, int optional)
321 return osmtpd_mheader_skip_obs_no_ws_ctl(ptr, optional);
324 char *
325 osmtpd_mheader_skip_qtext(char *ptr, int optional)
327 char *start = ptr;
329 if (ptr[0] == 33 || (ptr[0] >= 35 && ptr[0] <= 91) ||
330 (ptr[0] >= 93 && ptr[0] <= 126))
331 return ptr + 1;
332 if ((ptr = osmtpd_mheader_skip_obs_qtext(ptr, 0)) != NULL)
333 return ptr;
334 return optional ? start : NULL;
337 char *
338 osmtpd_mheader_skip_qcontent(char *ptr, int optional)
340 char *start = ptr;
342 if ((ptr = osmtpd_mheader_skip_qtext(ptr, 0)) != NULL)
343 return ptr;
344 return osmtpd_mheader_skip_quoted_pair(start, optional);
347 char *
348 osmtpd_mheader_skip_quoted_string(char *ptr, int optional)
350 char *start = ptr, *prev;
352 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
353 if ((ptr = osmtpd_mheader_skip_dquote(ptr, 0)) == NULL)
354 return optional ? start : NULL;
355 prev = ptr;
356 while (1) {
357 ptr = osmtpd_mheader_skip_fws(ptr, 1);
358 if ((ptr = osmtpd_mheader_skip_qcontent(ptr, 0)) == NULL)
359 break;
360 prev = ptr;
362 if ((ptr = osmtpd_mheader_skip_dquote(prev, 0)) == NULL)
363 return optional ? start : NULL;
364 return osmtpd_mheader_skip_cfws(ptr, 1);
367 char *
368 osmtpd_mheader_skip_word(char *ptr, int optional)
370 char *start = ptr;
372 if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) != NULL)
373 return ptr;
374 return osmtpd_mheader_skip_quoted_string(start, optional);
377 char *
378 osmtpd_mheader_skip_obs_phrase(char *ptr, int optional)
380 char *start = ptr, *prev;
382 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
383 return optional ? start : NULL;
384 while (1) {
385 prev = ptr;
386 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) != NULL)
387 continue;
388 ptr = prev;
389 if (ptr[0] == '.')
390 continue;
391 if ((ptr = osmtpd_mheader_skip_cfws(ptr, 0)) != NULL)
392 continue;
393 return prev;
397 char *
398 osmtpd_mheader_skip_phrase(char *ptr, int optional)
400 /* obs-phrase is a superset of phrae */
401 return osmtpd_mheader_skip_obs_phrase(ptr, optional);
402 #if 0
403 char *start = ptr, *prev;
405 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
406 return optional ? start : NULL;
407 while (1) {
408 prev = ptr;
409 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
410 return prev;
412 #endif
415 char *
416 osmtpd_mheader_skip_obs_local_part(char *ptr, int optional)
418 char *start = ptr, *prev;
420 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
421 return optional ? start : NULL;
422 prev = ptr;
423 while (ptr[0] == '.') {
424 ptr++;
425 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
426 return prev;
427 prev = ptr;
429 return ptr;
432 char *
433 osmtpd_mheader_skip_local_part(char *ptr, int optional)
435 char *start = ptr;
437 if ((ptr = osmtpd_mheader_skip_dot_atom(ptr, 0)) != NULL)
438 return ptr;
439 ptr = start;
440 if ((ptr = osmtpd_mheader_skip_quoted_string(ptr, 0)) != NULL)
441 return ptr;
442 return osmtpd_mheader_skip_obs_local_part(start, optional);
445 char *
446 osmtpd_mheader_skip_obs_dtext(char *ptr, int optional)
448 char *start = ptr;
450 if ((ptr = osmtpd_mheader_skip_obs_no_ws_ctl(ptr, 0)) != NULL)
451 return ptr;
452 return osmtpd_mheader_skip_quoted_pair(start, optional);
455 char *
456 osmtpd_mheader_skip_dtext(char *ptr, int optional)
458 if ((ptr[0] >= 33 && ptr[0] <= 90) || (ptr[0] >= 94 && ptr[0] <= 126))
459 return ptr + 1;
460 return osmtpd_mheader_skip_obs_dtext(ptr, optional);
464 char *
465 osmtpd_mheader_skip_domain_literal(char *ptr, int optional)
467 char *start = ptr, *prev;
469 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
470 if (ptr++[0] != '[')
471 return optional ? start : NULL;
472 while (1) {
473 ptr = osmtpd_mheader_skip_fws(ptr, 1);
474 prev = ptr;
475 if ((ptr = osmtpd_mheader_skip_dtext(ptr, 0)) == NULL) {
476 ptr = prev;
477 break;
480 if (ptr[0] != ']')
481 return optional ? start : NULL;
482 return osmtpd_mheader_skip_cfws(ptr, 1);
485 char *
486 osmtpd_mheader_skip_obs_domain(char *ptr, int optional)
488 char *start = ptr, *prev;
490 if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) == NULL)
491 return optional ? start : NULL;
492 prev = ptr;
493 while (1) {
494 if (ptr++[0] != '.')
495 return prev;
496 if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) == NULL)
497 return prev;
498 prev = ptr;
502 char *
503 osmtpd_mheader_skip_domain(char *ptr, int optional)
505 char *start = ptr;
507 if ((ptr = osmtpd_mheader_skip_dot_atom(start, 0)) != NULL)
508 return ptr;
509 if ((ptr = osmtpd_mheader_skip_domain_literal(start, 0)) != NULL)
510 return ptr;
511 return osmtpd_mheader_skip_obs_domain(start, optional);
514 char *
515 osmtpd_mheader_skip_display_name(char *ptr, int optional)
517 return osmtpd_mheader_skip_phrase(ptr, optional);
520 char *
521 osmtpd_mheader_skip_obs_domain_list(char *ptr, int optional)
523 char *start = ptr, *prev;
525 while (1) {
526 if (ptr[0] == ',') {
527 ptr++;
528 prev = ptr;
529 continue;
530 } else if ((ptr = osmtpd_mheader_skip_cfws(ptr, 0)) != NULL) {
531 prev = ptr;
532 continue;
534 break;
536 ptr = prev;
538 if (ptr++[0] != '@')
539 return optional ? start : NULL;
540 if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL)
541 return optional ? start : NULL;
542 while (1) {
543 if (ptr[0] != ',')
544 break;
545 ptr++;
546 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
547 if (ptr[0] != '@')
548 continue;
549 prev = ptr;
550 if ((ptr = osmtpd_mheader_skip_domain(ptr + 1, 0)) == NULL) {
551 ptr = prev;
552 break;
555 return ptr;
558 char *
559 osmtpd_mheader_skip_obs_route(char *ptr, int optional)
561 char *start = ptr;
563 if ((ptr = osmtpd_mheader_skip_obs_domain_list(ptr, 0)) == NULL)
564 return optional ? start : NULL;
565 if (ptr++[0] != ':')
566 return optional ? start : NULL;
567 return ptr;
570 char *
571 osmtpd_mheader_skip_addr_spec(char *ptr, int optional)
573 char *start = ptr;
575 if ((ptr = osmtpd_mheader_skip_local_part(ptr, 0)) == NULL)
576 return optional ? start : NULL;
577 if (ptr++[0] != '@')
578 return optional ? start : NULL;
579 if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL)
580 return optional ? start : NULL;
581 return ptr;
584 char *
585 osmtpd_mheader_skip_obs_angle_addr(char *ptr, int optional)
587 char *start = ptr;
589 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
590 if (ptr++[0] != '<')
591 return optional ? start : NULL;
592 if ((ptr = osmtpd_mheader_skip_obs_route(ptr, 0)) == NULL)
593 return optional ? start : NULL;
594 if ((ptr = osmtpd_mheader_skip_addr_spec(ptr, 0)) == NULL)
595 return optional ? start : NULL;
596 if (ptr++[0] != '>')
597 return optional ? start : NULL;
598 return osmtpd_mheader_skip_cfws(ptr, 1);
601 char *
602 osmtpd_mheader_skip_angle_addr(char *ptr, int optional)
604 char *start = ptr;
606 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
607 if (ptr++[0] != '<')
608 return osmtpd_mheader_skip_obs_angle_addr(start, optional);
609 if ((ptr = osmtpd_mheader_skip_addr_spec(ptr, 0)) == NULL)
610 return osmtpd_mheader_skip_obs_angle_addr(start, optional);
611 if (ptr++[0] != '>')
612 return osmtpd_mheader_skip_obs_angle_addr(start, optional);
613 return osmtpd_mheader_skip_cfws(ptr, 1);
616 char *
617 osmtpd_mheader_skip_name_addr(char *ptr, int optional)
619 char *start = ptr;
621 ptr = osmtpd_mheader_skip_display_name(ptr, 1);
622 if ((ptr = osmtpd_mheader_skip_angle_addr(ptr, 0)) == NULL)
623 return optional ? start : NULL;
624 return ptr;
627 /* Return the domain component of the first mailbox */
628 char *
629 osmtpd_mheader_from_domain(char *ptr)
631 char *tmp;
633 /* from */
634 if (strncasecmp(ptr, "from:", 5) == 0) {
635 ptr += 5;
636 /* obs-from */
637 } else if (strncasecmp(ptr, "from", 4) == 0) {
638 ptr += 4;
639 do {
640 tmp = ptr;
641 } while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL);
642 ptr = tmp;
643 if (ptr++[0] != ':')
644 return NULL;
645 } else {
646 errno = EINVAL;
647 return NULL;
650 /* Both from and obs-from use Mailbox-list CRLF */
651 /* obs-mbox-list has just a prefix compared to mailbox-list */
652 while (1) {
653 tmp = ptr;
654 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
655 if (ptr++[0] != ',') {
656 ptr = tmp;
657 break;
660 /* We're only interested in the first mailbox */
661 if (osmtpd_mheader_skip_name_addr(ptr, 0) != NULL) {
662 ptr = osmtpd_mheader_skip_display_name(ptr, 1);
663 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
664 /* < */
665 ptr++;
666 /* addr-spec */
667 ptr = osmtpd_mheader_skip_local_part(ptr, 0);
668 /* @ */
669 ptr++;
670 tmp = osmtpd_mheader_skip_domain(ptr, 0);
671 return strndup(ptr, tmp - ptr);
673 if (osmtpd_mheader_skip_addr_spec(ptr, 0) != NULL) {
674 ptr = osmtpd_mheader_skip_local_part(ptr, 0);
675 /* @ */
676 ptr++;
677 tmp = osmtpd_mheader_skip_domain(ptr, 0);
678 return strndup(ptr, tmp - ptr);
680 errno = EINVAL;
681 return NULL;