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