mblaze

Unix utilities to deal with Maildir - my mirror
git clone https://pi.duncano.de/git/mblaze.git

blaze822.c (11485B)


      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
     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
     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 = tm_to_secs(&tm);
    125 	return r;
    126 
    127 fail:
    128 	return -1;
    129 }
    130 
    131 char *
    132 blaze822_addr(char *s, char **dispo, char **addro)
    133 {
    134 	static char disp[1024];
    135 	static char addr[1024];
    136 	char *c, *e;
    137 
    138 	while (iswsp(*s))
    139 		s++;
    140 
    141 	if (!*s) {
    142 		if (dispo) *dispo = 0;
    143 		if (addro) *addro = 0;
    144 		return 0;
    145 	}
    146 
    147 	c = disp;
    148 	e = disp + sizeof disp - 1;
    149 
    150 	*disp = 0;
    151 	*addr = 0;
    152 
    153 	while (*s && c < e) {
    154 startover:
    155 		if (*s == '<') {
    156 			char *c = addr;
    157 			char *e = addr + sizeof addr;
    158 
    159 			s++;
    160 			while (*s && c < e && *s != '>') {
    161 				if (*s == '<') {
    162 					goto startover;
    163 				} else if (*s == '"') {
    164 					// local part may be quoted, allow all
    165 					s++;
    166 					while (*s && c < e && *s != '"')
    167 						*c++ = *s++;
    168 					if (*s == '"')
    169 						s++;
    170 				} else {
    171 					*c++ = *s++;
    172 				}
    173 			}
    174 			if (*s == '>')
    175 				s++;
    176 			while (iswsp(*s))
    177 				s++;
    178 			*c = 0;
    179 		} else if (*s == '"') {
    180 			s++;
    181 			while (*s && c < e && *s != '"') {
    182 				if (*s == '\\' && *(s+1))
    183 					s++;
    184 				*c++ = *s++;
    185 			}
    186 			if (*s == '"')
    187 				s++;
    188 		} else if (*s == '(') {   // XXX recurse to conform?
    189 			s++;
    190 
    191 			if (!*addr) {   // assume: user@host (name)
    192 				*c-- = 0;
    193 				while (c > disp && iswsp(*c))
    194 					*c-- = 0;
    195 				c++;
    196 				memcpy(addr, disp, (c - disp) + 1);
    197 				c = disp;
    198 				*c = 0;
    199 			}
    200 
    201 			while (*s && c < e && *s != ')')
    202 				*c++ = *s++;
    203 			if (*s == ')')
    204 				s++;
    205 		} else if (*s == '\\') {
    206 			s++;
    207 			if (*s)
    208 				*c++ = *s++;
    209 		} else if (*s == ':') {
    210 			s++;
    211 			while (iswsp(*s))
    212 				s++;
    213 			c = disp;  // forget already read group name
    214 		} else if (*s == ',' || *s == ';') {
    215 			s++;
    216 			break;
    217 		} else {
    218 			*c++ = *s++;
    219 		}
    220 	}
    221 
    222 	*c-- = 0;
    223 	// strip trailing ws
    224 	while (c > disp && iswsp(*c))
    225 		*c-- = 0;
    226 
    227 	if (*disp && !*addr && strchr(disp, '@')) {
    228 		// just mail address was given
    229 		c++;
    230 		memcpy(addr, disp, (c - disp) + 1);
    231 		*disp = 0;
    232 	}
    233 
    234 	char *host = strrchr(addr, '@');
    235 	char *u;
    236 	if (host && (u = strpbrk(addr, "()<>[]:;@\\,\"")) && u < host) {
    237 		// need to "-quote local-part
    238 
    239 		ssize_t hlen = strlen(host);
    240 		char addr2[sizeof addr];
    241 		char *e = addr2 + sizeof addr2 - 1;
    242 		char *t;
    243 
    244 		u = addr;
    245 		t = addr2;
    246 		*t++ = '"';
    247 		while (u < host && e - t > 2) {
    248 			if (*u == '"' || *u == '\\')
    249 				*t++ = '\\';
    250 			*t++ = *u++;
    251 		}
    252 		*t++ = '"';
    253 		if (e - t > hlen + 1) {
    254 			memcpy(t, host, hlen);
    255 			*(t + hlen) = 0;
    256 			memcpy(addr, addr2, sizeof addr);
    257 		}
    258 	}
    259 
    260 	if (dispo) *dispo = *disp ? disp : 0;
    261 	if (addro) *addro = *addr ? addr : 0;
    262 
    263 	return s;
    264 }
    265 
    266 static void
    267 compress_hdr(char *s, char *end)
    268 {
    269 	char *t, *h;
    270 
    271 	if ((t = h = strchr(s, '\n'))) {
    272 		while (h < end && *h) {
    273 			if (*h == '\n') {
    274 				*t++ = ' ';
    275 				while (*h && isfws(*h))
    276 					h++;
    277 			}
    278 			*t++ = *h++;
    279 		}
    280 		// remove trailing whitespace
    281 		while (s < t && isfws(t[-1]))
    282 			*--t = 0;
    283 		// zero fill gap
    284 		while (t < h)
    285 			*t++ = 0;
    286 	}
    287 }
    288 
    289 
    290 static void
    291 unfold_hdr(char *buf, char *end)
    292 {
    293 	char *s, *l;
    294 	*end = 0;
    295 
    296 	// sanitize all nul in message headers, srsly
    297 	if (memchr(buf, 0, end-buf))
    298 		for (s = buf; s < end; s++)
    299 			if (*s == 0)
    300 				*s = ' ';
    301 
    302 	// normalize crlf
    303 	if (memchr(buf, '\r', end-buf))
    304 		for (s = buf; s < end; s++)
    305 			if (*s == '\r') {
    306 				if (*(s+1) == '\n')
    307 					*s = '\n';
    308 				else
    309 					*s = ' ';
    310 			}
    311 
    312 	l = buf;
    313 	s = buf;
    314 
    315 	while (s < end && *s != ':' && *s != '\n') {
    316 		*s = lc(*s);
    317 		s++;
    318 	}
    319 
    320 	while (s < end) {
    321 		s = memchr(s+1, '\n', end-s);
    322 		if (!s)
    323 			break;
    324 
    325 		while (s < end && *s == '\n')
    326 			s++;
    327 		if (!iswsp(*s)) {
    328 			*(s-1) = 0;
    329 			compress_hdr(l, s-1);
    330 			l = s;
    331 			while (s < end && *s != ':' && *s != '\n') {
    332 				*s = lc(*s);
    333 				s++;
    334 			}
    335 		}
    336 	}
    337 	compress_hdr(l, end);
    338 }
    339 
    340 struct message *
    341 blaze822(char *file)
    342 {
    343 	int fd;
    344 	ssize_t rd;
    345 	char *buf;
    346 	ssize_t bufalloc;
    347 	ssize_t used;
    348 	char *end;
    349 
    350 	struct message *mesg = malloc(sizeof (struct message));
    351 	if (!mesg)
    352 		return 0;
    353 
    354 	fd = open(file, O_RDONLY);
    355 	if (fd < 0) {
    356 		free(mesg);
    357 		return 0;
    358 	}
    359 
    360 	buf = 0;
    361 	bufalloc = 0;
    362 	used = 0;
    363 
    364 	while (1) {
    365 		int overlap = used > 3 ? 3 : 0;
    366 
    367 		bufalloc += bufsiz;
    368 		buf = realloc(buf, bufalloc);
    369 		if (!buf) {
    370 			free(mesg);
    371 			close(fd);
    372 			return 0;
    373 		}
    374 
    375 		rd = read(fd, buf+used, bufalloc-used);
    376 		if (rd == 0) {
    377 			end = buf+used;
    378 			break;
    379 		}
    380 		if (rd < 0) {
    381 			free(mesg);
    382 			free(buf);
    383 			close(fd);
    384 			return 0;
    385 		}
    386 
    387 		if ((end = mymemmem(buf-overlap+used, rd+overlap, "\n\n", 2))) {
    388 			end++;
    389 			break;
    390 		}
    391 		if ((end = mymemmem(buf-overlap+used, rd+overlap, "\r\n\r\n", 4))) {
    392 			end++;
    393 			end++;
    394 			break;
    395 		}
    396 
    397 		used += rd;
    398 	}
    399 	close(fd);
    400 
    401 	*end = 0;   // dereferencing *end is safe
    402 
    403 	unfold_hdr(buf, end);
    404 
    405 	mesg->msg = buf;
    406 	mesg->end = end;
    407 	mesg->body = mesg->bodyend = mesg->bodychunk = mesg->orig_header = 0;
    408 
    409 	return mesg;
    410 }
    411 
    412 struct message *
    413 blaze822_mem(char *src, size_t len)
    414 {
    415 	char *buf;
    416 	char *end;
    417 
    418 	struct message *mesg = malloc(sizeof (struct message));
    419 	if (!mesg)
    420 		return 0;
    421 
    422 	if ((end = mymemmem(src, len, "\n\n", 2))) {
    423 		mesg->body = end+2;
    424 	} else if ((end = mymemmem(src, len, "\r\n\r\n", 4))) {
    425 		mesg->body = end+4;
    426 	} else {
    427 		end = src + len;
    428 		mesg->body = end;
    429 		mesg->bodyend = end;
    430 	}
    431 	if (mesg->body)
    432 		mesg->bodyend = src + len;
    433 
    434 	size_t hlen = end - src;
    435 
    436 	buf = malloc(hlen+1);
    437 	if (!buf)
    438 		return 0;
    439 	memcpy(buf, src, hlen);
    440 
    441 	end = buf+hlen;
    442 	*end = 0;   // dereferencing *end is safe
    443 
    444 	unfold_hdr(buf, end);
    445 
    446 	mesg->msg = buf;
    447 	mesg->end = end;
    448 	mesg->bodychunk = 0;   // src is not ours
    449 	mesg->orig_header = src;
    450 
    451 	return mesg;
    452 }
    453 
    454 void
    455 blaze822_free(struct message *mesg)
    456 {
    457 	if (!mesg)
    458 		return;
    459 	if (mesg->bodychunk == mesg->msg) {
    460 		munmap(mesg->bodychunk, mesg->bodyend - mesg->msg);
    461 	} else {
    462 		free(mesg->msg);
    463 		free(mesg->bodychunk);
    464 	}
    465 	free(mesg);
    466 }
    467 
    468 char *
    469 blaze822_hdr_(struct message *mesg, const char *hdr, size_t hdrlen)
    470 {
    471 	char *v;
    472 
    473 	if (hdrlen == 0 || hdrlen-1 >= (size_t)(mesg->end - mesg->msg))
    474 		return 0;  // header too small for the key, probably empty
    475 
    476 	// special case: first header, no leading nul
    477 	if (memcmp(mesg->msg, hdr+1, hdrlen-1) == 0) {
    478 		v = mesg->msg;
    479 		hdrlen--;
    480 	} else {
    481 		v = mymemmem(mesg->msg, mesg->end - mesg->msg, hdr, hdrlen);
    482 	}
    483 	if (!v)
    484 		return 0;
    485 	v += hdrlen;
    486 	while (*v && iswsp(*v))
    487 		v++;
    488 	return v;
    489 }
    490 
    491 char *
    492 blaze822_chdr(struct message *mesg, const char *chdr)
    493 {
    494 	char hdr[256];
    495 	char *c;
    496 
    497 	size_t l = snprintf(hdr, sizeof hdr, "%c%s:", 0, chdr);
    498 	for (c = hdr+1; *c; c++)
    499 		*c = lc(*c);
    500 
    501 	return blaze822_hdr_(mesg, hdr, l);
    502 }
    503 
    504 struct message *
    505 blaze822_file(char *file)
    506 {
    507 	char *buf = 0;
    508 	ssize_t rd = 0, n;
    509 
    510 	int fd = open(file, O_RDONLY);
    511 	if (fd < 0)
    512 		return 0;
    513 
    514 	struct stat st;
    515 	if (fstat(fd, &st) < 0)
    516 		goto error;
    517 
    518 	if (S_ISFIFO(st.st_mode)) {  // unbounded read, grow buffer
    519 		const ssize_t bufblk = 16384;
    520 		ssize_t bufalloc = bufblk;
    521 		buf = malloc(bufalloc);
    522 		if (!buf)
    523 			goto error;
    524 
    525 		do {
    526 			if (bufalloc < rd + bufblk) {
    527 				bufalloc *= 2;
    528 				buf = realloc(buf, bufalloc);
    529 				if (!buf)
    530 					goto error;
    531 			}
    532 			if ((n = read(fd, buf + rd, bufblk)) < 0) {
    533 				if (errno == EINTR) {
    534 					continue;
    535 				} else {
    536 					perror("read");
    537 					goto error;
    538 				}
    539 			}
    540 			rd += n;
    541 		} while (n > 0);
    542 	} else {  // file size known
    543 		ssize_t s = st.st_size;
    544 
    545 		buf = malloc(s+1);
    546 		if (!buf)
    547 			goto error;
    548 
    549 		do {
    550 			if ((n = read(fd, buf + rd, s - rd)) < 0) {
    551 				if (errno == EINTR) {
    552 					continue;
    553 				} else {
    554 					perror("read");
    555 					goto error;
    556 				}
    557 			}
    558 			rd += n;
    559 		} while (rd < s && n > 0);
    560 	}
    561 
    562 	close(fd);
    563 
    564 	buf[rd] = 0;
    565 
    566 	// XXX duplicate header in ram...
    567 	struct message *mesg = blaze822_mem(buf, rd);
    568 	if (mesg)
    569 		mesg->bodychunk = buf;
    570 	return mesg;
    571 
    572 error:
    573 	close(fd);
    574 	free(buf);
    575 	return 0;
    576 }
    577 
    578 struct message *
    579 blaze822_mmap(char *file)
    580 {
    581 	int fd = open(file, O_RDONLY);
    582 	if (fd < 0)
    583 		return 0;
    584 
    585 	struct stat st;
    586 	if (fstat(fd, &st) < 0)
    587 		goto error;
    588 
    589 	size_t len = st.st_size;
    590 
    591 	struct message *mesg = malloc(sizeof (struct message));
    592 	if (!mesg)
    593 		goto error;
    594 
    595 	char *buf = mmap(0, len+1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    596 	if (buf == MAP_FAILED) {
    597 		perror("mmap");
    598 		goto error;
    599 	}
    600 	close(fd);
    601 
    602 	char *end;
    603 	if ((end = mymemmem(buf, len, "\n\n", 2))) {
    604 		mesg->body = end+2;
    605 	} else if ((end = mymemmem(buf, len, "\r\n\r\n", 4))) {
    606 		mesg->body = end+4;
    607 	} else {
    608 		end = buf + len;
    609 		mesg->body = end;
    610 	}
    611 
    612 	unfold_hdr(buf, end);
    613 
    614 	mesg->msg = mesg->bodychunk = buf;
    615 	mesg->end = end;
    616 	mesg->bodyend = buf + len;
    617 	mesg->orig_header = 0;
    618 
    619 	return mesg;
    620 
    621 error:
    622 	close(fd);
    623 	return 0;
    624 }
    625 
    626 size_t
    627 blaze822_headerlen(struct message *mesg)
    628 {
    629 	return mesg->end - mesg->msg;
    630 }
    631 
    632 char *
    633 blaze822_body(struct message *mesg)
    634 {
    635 	return mesg->body;
    636 }
    637 
    638 char *
    639 blaze822_orig_header(struct message *mesg)
    640 {
    641 	return mesg->orig_header;
    642 }
    643 
    644 size_t
    645 blaze822_bodylen(struct message *mesg)
    646 {
    647 	if (!mesg->body || !mesg->bodyend)
    648 		return 0;
    649 	return mesg->bodyend - mesg->body;
    650 }
    651 
    652 char *
    653 blaze822_next_header(struct message *mesg, char *prev)
    654 {
    655 	if (!prev) {
    656 		prev = mesg->msg;
    657 	} else {
    658 		if (prev >= mesg->end)
    659 			return 0;
    660 		prev = prev + strlen(prev);
    661 	}
    662 	while (prev < mesg->end && *prev == 0)
    663 		prev++;
    664 	if (prev >= mesg->end)
    665 		return 0;
    666 	return prev;
    667 }