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 ptr++;
391 continue;
393 if ((ptr = osmtpd_mheader_skip_cfws(ptr, 0)) != NULL)
394 continue;
395 return prev;
399 char *
400 osmtpd_mheader_skip_phrase(char *ptr, int optional)
402 /* obs-phrase is a superset of phrae */
403 return osmtpd_mheader_skip_obs_phrase(ptr, optional);
404 #if 0
405 char *start = ptr, *prev;
407 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
408 return optional ? start : NULL;
409 while (1) {
410 prev = ptr;
411 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
412 return prev;
414 #endif
417 char *
418 osmtpd_mheader_skip_obs_local_part(char *ptr, int optional)
420 char *start = ptr, *prev;
422 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
423 return optional ? start : NULL;
424 prev = ptr;
425 while (ptr[0] == '.') {
426 ptr++;
427 if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
428 return prev;
429 prev = ptr;
431 return ptr;
434 char *
435 osmtpd_mheader_skip_local_part(char *ptr, int optional)
437 char *start = ptr;
439 if ((ptr = osmtpd_mheader_skip_dot_atom(ptr, 0)) != NULL)
440 return ptr;
441 ptr = start;
442 if ((ptr = osmtpd_mheader_skip_quoted_string(ptr, 0)) != NULL)
443 return ptr;
444 return osmtpd_mheader_skip_obs_local_part(start, optional);
447 char *
448 osmtpd_mheader_skip_obs_dtext(char *ptr, int optional)
450 char *start = ptr;
452 if ((ptr = osmtpd_mheader_skip_obs_no_ws_ctl(ptr, 0)) != NULL)
453 return ptr;
454 return osmtpd_mheader_skip_quoted_pair(start, optional);
457 char *
458 osmtpd_mheader_skip_dtext(char *ptr, int optional)
460 if ((ptr[0] >= 33 && ptr[0] <= 90) || (ptr[0] >= 94 && ptr[0] <= 126))
461 return ptr + 1;
462 return osmtpd_mheader_skip_obs_dtext(ptr, optional);
466 char *
467 osmtpd_mheader_skip_domain_literal(char *ptr, int optional)
469 char *start = ptr, *prev;
471 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
472 if (ptr++[0] != '[')
473 return optional ? start : NULL;
474 while (1) {
475 ptr = osmtpd_mheader_skip_fws(ptr, 1);
476 prev = ptr;
477 if ((ptr = osmtpd_mheader_skip_dtext(ptr, 0)) == NULL) {
478 ptr = prev;
479 break;
482 if (ptr[0] != ']')
483 return optional ? start : NULL;
484 return osmtpd_mheader_skip_cfws(ptr, 1);
487 char *
488 osmtpd_mheader_skip_obs_domain(char *ptr, int optional)
490 char *start = ptr, *prev;
492 if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) == NULL)
493 return optional ? start : NULL;
494 prev = ptr;
495 while (1) {
496 if (ptr++[0] != '.')
497 return prev;
498 if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) == NULL)
499 return prev;
500 prev = ptr;
504 char *
505 osmtpd_mheader_skip_domain(char *ptr, int optional)
507 char *start = ptr;
509 if ((ptr = osmtpd_mheader_skip_dot_atom(start, 0)) != NULL)
510 return ptr;
511 if ((ptr = osmtpd_mheader_skip_domain_literal(start, 0)) != NULL)
512 return ptr;
513 return osmtpd_mheader_skip_obs_domain(start, optional);
516 char *
517 osmtpd_mheader_skip_display_name(char *ptr, int optional)
519 return osmtpd_mheader_skip_phrase(ptr, optional);
522 char *
523 osmtpd_mheader_skip_obs_domain_list(char *ptr, int optional)
525 char *start = ptr, *prev = ptr;
527 while (1) {
528 if (ptr[0] == ',') {
529 ptr++;
530 prev = ptr;
531 continue;
532 } else if ((ptr = osmtpd_mheader_skip_cfws(ptr, 0)) != NULL) {
533 prev = ptr;
534 continue;
536 break;
538 ptr = prev;
540 if (ptr++[0] != '@')
541 return optional ? start : NULL;
542 if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL)
543 return optional ? start : NULL;
544 while (1) {
545 if (ptr[0] != ',')
546 break;
547 ptr++;
548 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
549 if (ptr[0] != '@')
550 continue;
551 prev = ptr;
552 if ((ptr = osmtpd_mheader_skip_domain(ptr + 1, 0)) == NULL) {
553 ptr = prev;
554 break;
557 return ptr;
560 char *
561 osmtpd_mheader_skip_obs_route(char *ptr, int optional)
563 char *start = ptr;
565 if ((ptr = osmtpd_mheader_skip_obs_domain_list(ptr, 0)) == NULL)
566 return optional ? start : NULL;
567 if (ptr++[0] != ':')
568 return optional ? start : NULL;
569 return ptr;
572 char *
573 osmtpd_mheader_skip_addr_spec(char *ptr, int optional)
575 char *start = ptr;
577 if ((ptr = osmtpd_mheader_skip_local_part(ptr, 0)) == NULL)
578 return optional ? start : NULL;
579 if (ptr++[0] != '@')
580 return optional ? start : NULL;
581 if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL)
582 return optional ? start : NULL;
583 return ptr;
586 char *
587 osmtpd_mheader_skip_obs_angle_addr(char *ptr, int optional)
589 char *start = ptr;
591 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
592 if (ptr++[0] != '<')
593 return optional ? start : NULL;
594 if ((ptr = osmtpd_mheader_skip_obs_route(ptr, 0)) == NULL)
595 return optional ? start : NULL;
596 if ((ptr = osmtpd_mheader_skip_addr_spec(ptr, 0)) == NULL)
597 return optional ? start : NULL;
598 if (ptr++[0] != '>')
599 return optional ? start : NULL;
600 return osmtpd_mheader_skip_cfws(ptr, 1);
603 char *
604 osmtpd_mheader_skip_angle_addr(char *ptr, int optional)
606 char *start = ptr;
608 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
609 if (ptr++[0] != '<')
610 return osmtpd_mheader_skip_obs_angle_addr(start, optional);
611 if ((ptr = osmtpd_mheader_skip_addr_spec(ptr, 0)) == NULL)
612 return osmtpd_mheader_skip_obs_angle_addr(start, optional);
613 if (ptr++[0] != '>')
614 return osmtpd_mheader_skip_obs_angle_addr(start, optional);
615 return osmtpd_mheader_skip_cfws(ptr, 1);
618 char *
619 osmtpd_mheader_skip_name_addr(char *ptr, int optional)
621 char *start = ptr;
623 ptr = osmtpd_mheader_skip_display_name(ptr, 1);
624 if ((ptr = osmtpd_mheader_skip_angle_addr(ptr, 0)) == NULL)
625 return optional ? start : NULL;
626 return ptr;
629 /* Return the domain component of the first mailbox */
630 char *
631 osmtpd_mheader_from_domain(char *ptr)
633 char *tmp;
635 /* from */
636 if (strncasecmp(ptr, "from:", 5) == 0) {
637 ptr += 5;
638 /* obs-from */
639 } else if (strncasecmp(ptr, "from", 4) == 0) {
640 ptr += 4;
641 do {
642 tmp = ptr;
643 } while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL);
644 ptr = tmp;
645 if (ptr++[0] != ':')
646 return NULL;
647 } else {
648 errno = EINVAL;
649 return NULL;
652 /* Both from and obs-from use Mailbox-list CRLF */
653 /* obs-mbox-list has just a prefix compared to mailbox-list */
654 while (1) {
655 tmp = ptr;
656 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
657 if (ptr++[0] != ',') {
658 ptr = tmp;
659 break;
662 /* We're only interested in the first mailbox */
663 if (osmtpd_mheader_skip_name_addr(ptr, 0) != NULL) {
664 ptr = osmtpd_mheader_skip_display_name(ptr, 1);
665 ptr = osmtpd_mheader_skip_cfws(ptr, 1);
666 /* < */
667 ptr++;
668 /* addr-spec */
669 ptr = osmtpd_mheader_skip_local_part(ptr, 0);
670 /* @ */
671 ptr++;
672 tmp = osmtpd_mheader_skip_domain(ptr, 0);
673 return strndup(ptr, tmp - ptr);
675 if (osmtpd_mheader_skip_addr_spec(ptr, 0) != NULL) {
676 ptr = osmtpd_mheader_skip_local_part(ptr, 0);
677 /* @ */
678 ptr++;
679 tmp = osmtpd_mheader_skip_domain(ptr, 0);
680 return strndup(ptr, tmp - ptr);
682 errno = EINVAL;
683 return NULL;