1 #include <sys/mman.h>
2 #include <sys/stat.h>
3 #include <sys/types.h>
4
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <time.h>
12 #include <unistd.h>
13
14 #include "blaze822.h"
15 #include "blaze822_priv.h"
16
17 #define bufsiz 4096
18
19 static long
parse_posint(char ** s,size_t minn,size_t maxn)20 parse_posint(char **s, size_t minn, size_t maxn)
21 {
22 long n;
23 char *end;
24
25 errno = 0;
26 n = strtol(*s, &end, 10);
27 if (errno)
28 return -1;
29 if (n < (long)minn || n > (long)maxn) {
30 errno = ERANGE;
31 return -1;
32 }
33 *s = end;
34 return n;
35 }
36
37 time_t
blaze822_date(char * s)38 blaze822_date(char *s) {
39 struct tm tm;
40 int c;
41
42 #define i4(m) (((uint32_t) m[0]<<24 | m[1]<<16 | m[2]<<8 | m[3]) == \
43 ((uint32_t) s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3] | 0x20202020) \
44 && (s += 4))
45
46 #define i3(m) (((uint32_t) m[0]<<24 | m[1]<<16 | m[2]<<8) == \
47 ((uint32_t) s[0]<<24 | s[1]<<16 | s[2]<<8 | 0x20202000) \
48 && (s += 3))
49
50 while (iswsp(*s))
51 s++;
52 if (i4("mon,") || i4("tue,") || i4("wed,") || i4("thu,") ||
53 i4("fri,") || i4("sat,") || i4("sun,"))
54 while (iswsp(*s))
55 s++;
56
57 if ((c = parse_posint(&s, 1, 31)) < 0) goto fail;
58 tm.tm_mday = c;
59
60 while (iswsp(*s))
61 s++;
62
63 if (i3("jan")) tm.tm_mon = 0;
64 else if (i3("feb")) tm.tm_mon = 1;
65 else if (i3("mar")) tm.tm_mon = 2;
66 else if (i3("apr")) tm.tm_mon = 3;
67 else if (i3("may")) tm.tm_mon = 4;
68 else if (i3("jun")) tm.tm_mon = 5;
69 else if (i3("jul")) tm.tm_mon = 6;
70 else if (i3("aug")) tm.tm_mon = 7;
71 else if (i3("sep")) tm.tm_mon = 8;
72 else if (i3("oct")) tm.tm_mon = 9;
73 else if (i3("nov")) tm.tm_mon = 10;
74 else if (i3("dec")) tm.tm_mon = 11;
75 else goto fail;
76
77 #undef i3
78 #undef i4
79
80 while (iswsp(*s))
81 s++;
82
83 if ((c = parse_posint(&s, 1000, 9999)) > 0) {
84 tm.tm_year = c - 1900;
85 } else if ((c = parse_posint(&s, 0, 49)) > 0) {
86 tm.tm_year = c + 100;
87 } else if ((c = parse_posint(&s, 50, 99)) > 0) {
88 tm.tm_year = c;
89 } else goto fail;
90
91 while (iswsp(*s))
92 s++;
93
94 if ((c = parse_posint(&s, 0, 24)) < 0) goto fail;
95 tm.tm_hour = c;
96 if (*s++ != ':') goto fail;
97 if ((c = parse_posint(&s, 0, 59)) < 0) goto fail;
98 tm.tm_min = c;
99 if (*s++ == ':') {
100 if ((c = parse_posint(&s, 0, 61)) < 0) goto fail;
101 tm.tm_sec = c;
102 } else {
103 tm.tm_sec = 0;
104 }
105
106 while (iswsp(*s))
107 s++;
108
109 if (*s == '+' || *s == '-') {
110 int neg = (*s == '-');
111 s++;
112 if ((c = parse_posint(&s, 0, 10000)) < 0) goto fail;
113 if (neg) {
114 tm.tm_hour += c / 100;
115 tm.tm_min += c % 100;
116 } else {
117 tm.tm_hour -= c / 100;
118 tm.tm_min -= c % 100;
119 }
120 }
121
122 tm.tm_isdst = -1;
123
124 time_t r = mytimegm(&tm);
125 return r;
126
127 fail:
128 return -1;
129 }
130
131 static char *
skip_comment(char * s)132 skip_comment(char *s)
133 {
134 if (*s != '(')
135 return s;
136
137 long d = 0;
138 do {
139 if (!*s)
140 break;
141 else if (*s == '(')
142 d++;
143 else if (*s == ')')
144 d--;
145 s++;
146 } while (d > 0);
147
148 return s;
149 }
150
151 // always 0 terminates
152 // never writes more than dstmax to dst
153 // returns how many bytes were appended
154 static size_t
safe_append(char * dst,size_t dstmax,char * srcbeg,char * srcend)155 safe_append(char *dst, size_t dstmax, char *srcbeg, char *srcend)
156 {
157 size_t srclen = srcend - srcbeg;
158 size_t dstlen = strnlen(dst, dstmax);
159
160 if (dstlen == dstmax)
161 return 0;
162
163 if (dstmax - dstlen < srclen + 1)
164 srclen = dstmax - dstlen - 1;
165 memcpy(dst + dstlen, srcbeg, srclen);
166 dst[dstlen + srclen] = 0;
167
168 return srclen;
169 }
170
171 static size_t
safe_append_space(char * dst,size_t dstmax)172 safe_append_space(char *dst, size_t dstmax)
173 {
174 char sp[] = " ";
175 char *se = sp + 1;
176
177 return safe_append(dst, dstmax, sp, se);
178 }
179
180 char *
blaze822_addr(char * s,char ** dispo,char ** addro)181 blaze822_addr(char *s, char **dispo, char **addro)
182 {
183 static char disp[1024];
184 static char addr[1024];
185
186 memset(addr, 0, sizeof addr);
187 memset(disp, 0, sizeof disp);
188
189 char ttok[1024] = { 0 };
190 char *tc = ttok;
191 char *te = ttok + sizeof ttok;
192
193 int not_addr = 0;
194
195 while (1) {
196 if (!*s || iswsp(*s) || *s == ',' || *s == ';') {
197 if (tc != ttok) {
198 if (!*addr && !not_addr && memchr(ttok, '@', tc - ttok)) {
199 safe_append(addr, sizeof addr, ttok, tc);
200 } else {
201 if (*disp)
202 safe_append_space(disp, sizeof disp);
203 safe_append(disp, sizeof disp,
204 ttok, tc);
205 }
206 tc = ttok;
207 *ttok = 0;
208 not_addr = 0;
209 }
210 if (!*s) {
211 if (!*addr && !*disp) {
212 if (dispo) *dispo = 0;
213 if (addro) *addro = 0;
214 return 0;
215 }
216 break;
217 }
218 if (*s == ',' || *s == ';') {
219 s++;
220 if (*addr || *disp)
221 break;
222 } else {
223 s++;
224 }
225 } else if (*s == '<') {
226 char tok[1024] = { 0 };
227 char *c = tok;
228 char *e = tok + sizeof tok;
229 s++;
230 while (*s && c < e && *s != '>') {
231 s = skip_comment(s);
232 if (!*s) {
233 break;
234 } else if (*s == '"') {
235 // local part may be quoted, allow all
236 s++;
237 while (*s && c < e && *s != '"') {
238 if (*s == '\\')
239 s++;
240 *c++ = *s++;
241 }
242 if (*s == '"')
243 s++;
244 } else if (*s == '<' || *s == ':') {
245 c = tok;
246 s++;
247 } else {
248 if (iswsp(*s))
249 s++;
250 else
251 *c++ = *s++;
252 }
253 }
254 if (*s == '>')
255 s++;
256 if (*addr) {
257 if (*disp)
258 safe_append_space(disp, sizeof disp);
259 safe_append(disp, sizeof disp,
260 addr, addr + strlen(addr));
261 }
262 *addr = 0;
263 safe_append(addr, sizeof addr, tok, c);
264 } else if (*s == '"') {
265 char tok[1024] = { 0 };
266 char *c = tok;
267 char *e = tok + sizeof tok;
268 s++;
269 while (*s && c < e && *s != '"') {
270 if (*s == '\\' && *(s+1))
271 s++;
272 *c++ = *s++;
273 }
274 if (*s == '"')
275 s++;
276
277 if (memchr(tok, '@', c - tok))
278 not_addr = 1; // @ inside "" is never an addr
279
280 *tc = 0;
281 tc += safe_append(ttok, sizeof ttok, tok, c);
282 } else if (*s == '(') {
283 char *z = skip_comment(s);
284 if (!*disp && *addr) // user@host (name)
285 safe_append(disp, sizeof disp, s + 1,
286 *z ? z-1 : (*(z-1) == ')' ? z-1 : z));
287 else if (*disp) { // copy comment
288 safe_append_space(disp, sizeof disp);
289 safe_append(disp, sizeof disp, s, z);
290 }
291 s = z;
292 } else if (*s == ':') {
293 if (memchr(ttok, '[', tc - ttok)) {
294 // in ipv6 address
295 if (tc < te)
296 *tc++ = *s++;
297 else
298 s++;
299 } else { // ignore group name and start over
300 s++;
301 tc = ttok;
302 memset(addr, 0, sizeof addr);
303 memset(disp, 0, sizeof disp);
304 *ttok = 0;
305 not_addr = 0;
306 }
307 } else {
308 if (*s == '\\' && *(s+1))
309 s++;
310 if (tc < te)
311 *tc++ = *s++;
312 else
313 s++;
314 }
315 }
316
317 char *host = strrchr(addr, '@');
318 char *u;
319 if (host && (u = strpbrk(addr, "()<>[]:;@\\,\" \t")) && u < host) {
320 // need to "-quote local-part
321
322 ssize_t hlen = strlen(host);
323 char addr2[sizeof addr];
324 char *e = addr2 + sizeof addr2 - 1;
325 char *t;
326
327 u = addr;
328 t = addr2;
329 *t++ = '"';
330 while (u < host && e - t > 2) {
331 if (*u == '"' || *u == '\\')
332 *t++ = '\\';
333 *t++ = *u++;
334 }
335 *t++ = '"';
336 if (e - t > hlen + 1) {
337 memcpy(t, host, hlen);
338 *(t + hlen) = 0;
339 memcpy(addr, addr2, sizeof addr);
340 }
341 }
342
343 if (dispo) *dispo = *disp ? disp : 0;
344 if (addro) *addro = *addr ? addr : 0;
345
346 return s;
347 }
348
349 static void
compress_hdr(char * s,char * end)350 compress_hdr(char *s, char *end)
351 {
352 char *t, *h;
353
354 if ((t = h = strchr(s, '\n'))) {
355 while (h < end && *h) {
356 if (*h == '\n') {
357 *t++ = ' ';
358 while (*h && isfws(*h) && *(h+1))
359 h++;
360 }
361 *t++ = *h++;
362 }
363 // remove trailing whitespace
364 while (s < t && isfws(t[-1]))
365 *--t = 0;
366 // zero fill gap
367 while (t < h)
368 *t++ = 0;
369 }
370 }
371
372
373 static void
unfold_hdr(char * buf,char * end)374 unfold_hdr(char *buf, char *end)
375 {
376 char *s, *l;
377 *end = 0;
378
379 // sanitize all nul in message headers, srsly
380 if (memchr(buf, 0, end-buf))
381 for (s = buf; s < end; s++)
382 if (*s == 0)
383 *s = ' ';
384
385 // normalize crlf
386 if (memchr(buf, '\r', end-buf))
387 for (s = buf; s < end; s++)
388 if (*s == '\r') {
389 if (*(s+1) == '\n')
390 *s = '\n';
391 else
392 *s = ' ';
393 }
394
395 l = buf;
396 s = buf;
397
398 while (s < end && *s != ':' && *s != '\n') {
399 *s = lc(*s);
400 s++;
401 }
402
403 while (s < end) {
404 s = memchr(s+1, '\n', end-s);
405 if (!s)
406 break;
407
408 while (s < end && *s == '\n')
409 s++;
410 if (!iswsp(*s)) {
411 *(s-1) = 0;
412 compress_hdr(l, s-1);
413 l = s;
414 while (s < end && *s != ':' && *s != '\n') {
415 *s = lc(*s);
416 s++;
417 }
418 }
419 }
420 compress_hdr(l, end);
421 }
422
423 struct message *
blaze822(char * file)424 blaze822(char *file)
425 {
426 int fd;
427 ssize_t rd;
428 char *buf;
429 ssize_t bufalloc;
430 ssize_t used;
431 char *end;
432
433 struct message *mesg = malloc(sizeof (struct message));
434 if (!mesg)
435 return 0;
436
437 fd = open(file, O_RDONLY);
438 if (fd < 0) {
439 free(mesg);
440 return 0;
441 }
442
443 buf = 0;
444 bufalloc = 0;
445 used = 0;
446
447 while (1) {
448 int overlap = used > 3 ? 3 : 0;
449
450 bufalloc += bufsiz;
451 buf = realloc(buf, bufalloc);
452 if (!buf) {
453 free(mesg);
454 close(fd);
455 return 0;
456 }
457
458 rd = read(fd, buf+used, bufalloc-used);
459 if (rd == 0) {
460 end = buf+used;
461 break;
462 }
463 if (rd < 0) {
464 free(mesg);
465 free(buf);
466 close(fd);
467 return 0;
468 }
469
470 if ((end = mymemmem(buf-overlap+used, rd+overlap, "\n\n", 2))) {
471 end++;
472 break;
473 }
474 if ((end = mymemmem(buf-overlap+used, rd+overlap, "\r\n\r\n", 4))) {
475 end++;
476 end++;
477 break;
478 }
479
480 used += rd;
481 }
482 close(fd);
483
484 *end = 0; // dereferencing *end is safe
485
486 unfold_hdr(buf, end);
487
488 mesg->msg = buf;
489 mesg->end = end;
490 mesg->body = mesg->bodyend = mesg->bodychunk = mesg->orig_header = 0;
491
492 return mesg;
493 }
494
495 struct message *
blaze822_mem(char * src,size_t len)496 blaze822_mem(char *src, size_t len)
497 {
498 char *buf;
499 char *end;
500
501 struct message *mesg = malloc(sizeof (struct message));
502 if (!mesg)
503 return 0;
504
505 if ((end = mymemmem(src, len, "\n\n", 2))) {
506 mesg->body = end+2;
507 } else if ((end = mymemmem(src, len, "\r\n\r\n", 4))) {
508 mesg->body = end+4;
509 } else {
510 end = src + len;
511 mesg->body = end;
512 mesg->bodyend = end;
513 }
514 if (mesg->body)
515 mesg->bodyend = src + len;
516
517 size_t hlen = end - src;
518
519 buf = malloc(hlen+1);
520 if (!buf) {
521 free(mesg);
522 return 0;
523 }
524 memcpy(buf, src, hlen);
525
526 end = buf+hlen;
527 *end = 0; // dereferencing *end is safe
528
529 unfold_hdr(buf, end);
530
531 mesg->msg = buf;
532 mesg->end = end;
533 mesg->bodychunk = 0; // src is not ours
534 mesg->orig_header = src;
535
536 return mesg;
537 }
538
539 void
blaze822_free(struct message * mesg)540 blaze822_free(struct message *mesg)
541 {
542 if (!mesg)
543 return;
544 if (mesg->bodychunk == mesg->msg) {
545 munmap(mesg->bodychunk, mesg->bodyend - mesg->msg);
546 } else {
547 free(mesg->msg);
548 free(mesg->bodychunk);
549 }
550 free(mesg);
551 }
552
553 char *
blaze822_hdr_(struct message * mesg,const char * hdr,size_t hdrlen)554 blaze822_hdr_(struct message *mesg, const char *hdr, size_t hdrlen)
555 {
556 char *v;
557
558 if (hdrlen == 0 || hdrlen-1 >= (size_t)(mesg->end - mesg->msg))
559 return 0; // header too small for the key, probably empty
560
561 // special case: first header, no leading nul
562 if (memcmp(mesg->msg, hdr+1, hdrlen-1) == 0) {
563 v = mesg->msg;
564 hdrlen--;
565 } else {
566 v = mymemmem(mesg->msg, mesg->end - mesg->msg, hdr, hdrlen);
567 }
568 if (!v)
569 return 0;
570 v += hdrlen;
571 while (*v && iswsp(*v))
572 v++;
573 return v;
574 }
575
576 char *
blaze822_chdr(struct message * mesg,const char * chdr)577 blaze822_chdr(struct message *mesg, const char *chdr)
578 {
579 char hdr[256];
580 char *c;
581
582 size_t l = snprintf(hdr, sizeof hdr, "%c%s:", 0, chdr);
583 for (c = hdr+1; *c; c++)
584 *c = lc(*c);
585
586 return blaze822_hdr_(mesg, hdr, l);
587 }
588
589 struct message *
blaze822_file(char * file)590 blaze822_file(char *file)
591 {
592 char *buf = 0;
593 ssize_t rd = 0, n;
594
595 int fd;
596 if (strcmp(file, "/dev/stdin") == 0)
597 fd = dup(0);
598 else
599 fd = open(file, O_RDONLY);
600 if (fd < 0)
601 return 0;
602
603 struct stat st;
604 if (fstat(fd, &st) < 0)
605 goto error;
606
607 if (S_ISFIFO(st.st_mode)) { // unbounded read, grow buffer
608 const ssize_t bufblk = 16384;
609 ssize_t bufalloc = bufblk;
610 buf = malloc(bufalloc);
611 if (!buf)
612 goto error;
613
614 do {
615 if (bufalloc < rd + bufblk) {
616 bufalloc *= 2;
617 buf = realloc(buf, bufalloc);
618 if (!buf)
619 goto error;
620 }
621 if ((n = read(fd, buf + rd, bufblk)) < 0) {
622 if (errno == EINTR) {
623 continue;
624 } else {
625 perror("read");
626 goto error;
627 }
628 }
629 rd += n;
630 } while (n > 0);
631 } else { // file size known
632 ssize_t s = st.st_size;
633
634 buf = malloc(s+1);
635 if (!buf)
636 goto error;
637
638 do {
639 if ((n = read(fd, buf + rd, s - rd)) < 0) {
640 if (errno == EINTR) {
641 continue;
642 } else {
643 perror("read");
644 goto error;
645 }
646 }
647 rd += n;
648 } while (rd < s && n > 0);
649 }
650
651 close(fd);
652
653 buf[rd] = 0;
654
655 // XXX duplicate header in ram...
656 struct message *mesg = blaze822_mem(buf, rd);
657 if (mesg)
658 mesg->bodychunk = buf;
659 return mesg;
660
661 error:
662 close(fd);
663 free(buf);
664 return 0;
665 }
666
667 struct message *
blaze822_mmap(char * file)668 blaze822_mmap(char *file)
669 {
670 int fd = open(file, O_RDONLY);
671 if (fd < 0)
672 return 0;
673
674 struct stat st;
675 if (fstat(fd, &st) < 0)
676 goto error;
677
678 size_t len = st.st_size;
679
680 struct message *mesg = malloc(sizeof (struct message));
681 if (!mesg)
682 goto error;
683
684 char *buf = mmap(0, len+1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
685 if (buf == MAP_FAILED) {
686 free(mesg);
687 perror("mmap");
688 goto error;
689 }
690 close(fd);
691
692 char *end;
693 if ((end = mymemmem(buf, len, "\n\n", 2))) {
694 mesg->body = end+2;
695 } else if ((end = mymemmem(buf, len, "\r\n\r\n", 4))) {
696 mesg->body = end+4;
697 } else {
698 end = buf + len;
699 mesg->body = end;
700 }
701
702 unfold_hdr(buf, end);
703
704 mesg->msg = mesg->bodychunk = buf;
705 mesg->end = end;
706 mesg->bodyend = buf + len;
707 mesg->orig_header = 0;
708
709 return mesg;
710
711 error:
712 close(fd);
713 return 0;
714 }
715
716 size_t
blaze822_headerlen(struct message * mesg)717 blaze822_headerlen(struct message *mesg)
718 {
719 return mesg->end - mesg->msg;
720 }
721
722 char *
blaze822_body(struct message * mesg)723 blaze822_body(struct message *mesg)
724 {
725 return mesg->body;
726 }
727
728 char *
blaze822_orig_header(struct message * mesg)729 blaze822_orig_header(struct message *mesg)
730 {
731 return mesg->orig_header;
732 }
733
734 size_t
blaze822_bodylen(struct message * mesg)735 blaze822_bodylen(struct message *mesg)
736 {
737 if (!mesg->body || !mesg->bodyend)
738 return 0;
739 return mesg->bodyend - mesg->body;
740 }
741
742 char *
blaze822_next_header(struct message * mesg,char * prev)743 blaze822_next_header(struct message *mesg, char *prev)
744 {
745 if (!prev) {
746 prev = mesg->msg;
747 } else {
748 if (prev >= mesg->end)
749 return 0;
750 prev = prev + strlen(prev);
751 }
752 while (prev < mesg->end && *prev == 0)
753 prev++;
754 if (prev >= mesg->end)
755 return 0;
756 return prev;
757 }
758