Home | History | Annotate | Line # | Download | only in mail
support.c revision 1.18
      1 /*	$NetBSD: support.c,v 1.18 2007/06/05 17:50:23 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1980, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 #if 0
     35 static char sccsid[] = "@(#)aux.c	8.1 (Berkeley) 6/6/93";
     36 #else
     37 __RCSID("$NetBSD: support.c,v 1.18 2007/06/05 17:50:23 christos Exp $");
     38 #endif
     39 #endif /* not lint */
     40 
     41 #include <stdarg.h>
     42 #include <util.h>
     43 
     44 #include "rcv.h"
     45 #include "extern.h"
     46 #include "mime.h"
     47 #include "thread.h"
     48 
     49 /*
     50  * Mail -- a mail program
     51  *
     52  * Auxiliary functions.
     53  */
     54 
     55 
     56 /*
     57  * Return a pointer to a dynamic copy of the argument.
     58  */
     59 PUBLIC char *
     60 savestr(const char *str)
     61 {
     62 	char *new;
     63 	size_t size = strlen(str) + 1;
     64 
     65 	if ((new = salloc(size)) != NULL)
     66 		(void)memmove(new, str, size);
     67 	return new;
     68 }
     69 
     70 /*
     71  * Make a copy of new argument incorporating old one.
     72  */
     73 static char *
     74 save2str(char *str, char *old)
     75 {
     76 	char *new;
     77 	size_t newsize = strlen(str) + 1;
     78 	size_t oldsize = old ? strlen(old) + 1 : 0;
     79 
     80 	if ((new = salloc(newsize + oldsize)) != NULL) {
     81 		if (oldsize) {
     82 			(void)memmove(new, old, oldsize);
     83 			new[oldsize - 1] = ' ';
     84 		}
     85 		(void)memmove(new + oldsize, str, newsize);
     86 	}
     87 	return new;
     88 }
     89 
     90 /*
     91  * Like asprintf(), but with result salloc-ed rather than malloc-ed.
     92  */
     93 PUBLIC int
     94 sasprintf(char **ret, const char *format, ...)
     95 {
     96 	int rval;
     97 	char *p;
     98 	va_list args;
     99 
    100 	va_start(args, format);
    101 	rval = evasprintf(&p, format, args);
    102 	*ret = savestr(p);
    103 	free(p);
    104 	va_end(args);
    105 	return rval;
    106 }
    107 
    108 struct set_m_flag_args_s {
    109 	int and_bits;
    110 	int xor_bits;
    111 };
    112 static int
    113 set_m_flag_core(struct message *mp, void *v)
    114 {
    115 	struct set_m_flag_args_s *args;
    116 	args = v;
    117 	mp->m_flag &= args->and_bits;
    118 	mp->m_flag ^= args->xor_bits;
    119 	return 0;
    120 }
    121 
    122 PUBLIC struct message *
    123 set_m_flag(int msgnum, int and_bits, int xor_bits)
    124 {
    125 	struct message *mp;
    126 	struct set_m_flag_args_s args;
    127 	mp = get_message(msgnum);
    128 	args.and_bits = and_bits;
    129 	args.xor_bits = xor_bits;
    130 	(void)thread_recursion(mp, set_m_flag_core, &args);
    131 	return mp;
    132 }
    133 
    134 /*
    135  * Touch the named message by setting its MTOUCH flag.
    136  * Touched messages have the effect of not being sent
    137  * back to the system mailbox on exit.
    138  */
    139 PUBLIC void
    140 touch(struct message *mp)
    141 {
    142 
    143 	mp->m_flag |= MTOUCH;
    144 	if ((mp->m_flag & MREAD) == 0)
    145 		mp->m_flag |= MREAD|MSTATUS;
    146 }
    147 
    148 /*
    149  * Test to see if the passed file name is a directory.
    150  * Return true if it is.
    151  */
    152 PUBLIC int
    153 isdir(const char name[])
    154 {
    155 	struct stat sbuf;
    156 
    157 	if (stat(name, &sbuf) < 0)
    158 		return 0;
    159 	return S_ISDIR(sbuf.st_mode);
    160 }
    161 
    162 /*
    163  * Count the number of arguments in the given string raw list.
    164  */
    165 PUBLIC int
    166 argcount(char **argv)
    167 {
    168 	char **ap;
    169 
    170 	for (ap = argv; *ap++ != NULL;)
    171 		continue;
    172 	return ap - argv - 1;
    173 }
    174 
    175 /*
    176  * Check whether the passed line is a header line of
    177  * the desired breed.  Return the field body, or 0.
    178  */
    179 static char*
    180 ishfield(const char linebuf[], char *colon, const char field[])
    181 {
    182 	char *cp = colon;
    183 
    184 	*cp = 0;
    185 	if (strcasecmp(linebuf, field) != 0) {
    186 		*cp = ':';
    187 		return 0;
    188 	}
    189 	*cp = ':';
    190 	for (cp++; *cp == ' ' || *cp == '\t'; cp++)
    191 		continue;
    192 	return cp;
    193 }
    194 
    195 /*
    196  * Return the next header field found in the given message.
    197  * Return >= 0 if something found, < 0 elsewise.
    198  * "colon" is set to point to the colon in the header.
    199  * Must deal with \ continuations & other such fraud.
    200  *
    201  * WARNING - do not call this outside hfield() or decoding will not
    202  *           get done.
    203  */
    204 static int
    205 gethfield(FILE *f, char linebuf[], int rem, char **colon)
    206 {
    207 	char line2[LINESIZE];
    208 	char *cp, *cp2;
    209 	int c;
    210 
    211 	for (;;) {
    212 		if (--rem < 0)
    213 			return -1;
    214 		if ((c = mail_readline(f, linebuf, LINESIZE)) <= 0)
    215 			return -1;
    216 		for (cp = linebuf; isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':';
    217 		     cp++)
    218 			continue;
    219 		if (*cp != ':' || cp == linebuf)
    220 			continue;
    221 		/*
    222 		 * I guess we got a headline.
    223 		 * Handle wraparounding
    224 		 */
    225 		*colon = cp;
    226 		cp = linebuf + c;
    227 		for (;;) {
    228 			while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
    229 				continue;
    230 			cp++;
    231 			if (rem <= 0)
    232 				break;
    233 			(void)ungetc(c = getc(f), f);
    234 			if (c != ' ' && c != '\t')
    235 				break;
    236 			if ((c = mail_readline(f, line2, LINESIZE)) < 0)
    237 				break;
    238 			rem--;
    239 			for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
    240 				continue;
    241 			c -= cp2 - line2;
    242 			if (cp + c >= linebuf + LINESIZE - 2)
    243 				break;
    244 			*cp++ = ' ';
    245 			(void)memmove(cp, cp2, (size_t)c);
    246 			cp += c;
    247 		}
    248 		*cp = 0;
    249 		return rem;
    250 	}
    251 	/* NOTREACHED */
    252 }
    253 
    254 /*
    255  * Return the desired header line from the passed message
    256  * pointer (or NULL if the desired header field is not available).
    257  */
    258 PUBLIC char *
    259 hfield(const char field[], const struct message *mp)
    260 {
    261 	FILE *ibuf;
    262 	char linebuf[LINESIZE];
    263 	int lc;
    264 	char *headerfield;
    265 	char *colon, *oldhfield = NULL;
    266 #ifdef MIME_SUPPORT
    267 	int decode;
    268 
    269 	decode = value(ENAME_MIME_DECODE_MSG) &&
    270 	    value(ENAME_MIME_DECODE_HDR);
    271 #endif
    272 
    273 	ibuf = setinput(mp);
    274 	if ((lc = mp->m_lines - 1) < 0)
    275 		return NULL;
    276 	if (mail_readline(ibuf, linebuf, LINESIZE) < 0)
    277 		return NULL;
    278 	while (lc > 0) {
    279 		if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
    280 			return oldhfield;
    281 #ifdef MIME_SUPPORT
    282 		if ((headerfield = ishfield(linebuf, colon, field)) != NULL) {
    283 			char linebuf2[LINESIZE];
    284 			if (decode && colon)
    285 				headerfield = mime_decode_hfield(linebuf2, sizeof(linebuf2), headerfield);
    286 			oldhfield = save2str(headerfield, oldhfield);
    287 		}
    288 #else
    289 		if ((headerfield = ishfield(linebuf, colon, field)) != NULL)
    290 			oldhfield = save2str(headerfield, oldhfield);
    291 #endif
    292 	}
    293 	return oldhfield;
    294 }
    295 
    296 /*
    297  * Copy a string, lowercasing it as we go.
    298  */
    299 PUBLIC void
    300 istrcpy(char *dest, const char *src)
    301 {
    302 
    303 	do {
    304 		*dest++ = tolower((unsigned char)*src);
    305 	} while (*src++ != 0);
    306 }
    307 
    308 /*
    309  * The following code deals with input stacking to do source
    310  * commands.  All but the current file pointer are saved on
    311  * the stack.
    312  */
    313 
    314 static	int	ssp;			/* Top of file stack */
    315 static struct sstack {
    316 	FILE	*s_file;		/* File we were in. */
    317 	int	s_cond;			/* Saved state of conditionals */
    318 #ifdef NEW_CONDITIONAL
    319 	struct cond_stack_s *s_cond_stack; /* Saved conditional stack */
    320 #endif
    321 	int	s_loading;		/* Loading .mailrc, etc. */
    322 } sstack[NOFILE];
    323 
    324 /*
    325  * Pushdown current input file and switch to a new one.
    326  * Set the global flag "sourcing" so that others will realize
    327  * that they are no longer reading from a tty (in all probability).
    328  */
    329 PUBLIC int
    330 source(void *v)
    331 {
    332 	char **arglist = v;
    333 	FILE *fi;
    334 	const char *cp;
    335 
    336 	if ((cp = expand(*arglist)) == NULL)
    337 		return 1;
    338 	if ((fi = Fopen(cp, "r")) == NULL) {
    339 		warn("%s", cp);
    340 		return 1;
    341 	}
    342 	if (ssp >= NOFILE - 1) {
    343 		(void)printf("Too much \"sourcing\" going on.\n");
    344 		(void)Fclose(fi);
    345 		return 1;
    346 	}
    347 	sstack[ssp].s_file = input;
    348 	sstack[ssp].s_cond = cond;
    349 #ifdef NEW_CONDITIONAL
    350 	sstack[ssp].s_cond_stack = cond_stack;
    351 #endif
    352 	sstack[ssp].s_loading = loading;
    353 	ssp++;
    354 	loading = 0;
    355 	cond = CNONE;
    356 	input = fi;
    357 	sourcing++;
    358 	return 0;
    359 }
    360 
    361 /*
    362  * Pop the current input back to the previous level.
    363  * Update the "sourcing" flag as appropriate.
    364  */
    365 PUBLIC int
    366 unstack(void)
    367 {
    368 	if (ssp <= 0) {
    369 		(void)printf("\"Source\" stack over-pop.\n");
    370 		sourcing = 0;
    371 		return 1;
    372 	}
    373 	(void)Fclose(input);
    374 	if (cond != CNONE || cond_stack != NULL)
    375 		(void)printf("Unmatched \"if\"\n");
    376 	ssp--;
    377 	input = sstack[ssp].s_file;
    378 	cond = sstack[ssp].s_cond;
    379 #ifdef NEW_CONDITIONAL
    380 	cond_stack = sstack[ssp].s_cond_stack;
    381 #endif
    382 	loading = sstack[ssp].s_loading;
    383 	if (ssp == 0)
    384 		sourcing = loading;
    385 	return 0;
    386 }
    387 
    388 /*
    389  * Touch the indicated file.
    390  * This is nifty for the shell.
    391  */
    392 PUBLIC void
    393 alter(char *name)
    394 {
    395 	struct stat sb;
    396 	struct timeval tv[2];
    397 
    398 	if (stat(name, &sb))
    399 		return;
    400 	(void)gettimeofday(&tv[0], NULL);
    401 	tv[0].tv_sec++;
    402 	TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
    403 	(void)utimes(name, tv);
    404 }
    405 
    406 /*
    407  * Examine the passed line buffer and
    408  * return true if it is all blanks and tabs.
    409  */
    410 PUBLIC int
    411 blankline(char linebuf[])
    412 {
    413 	char *cp;
    414 
    415 	for (cp = linebuf; *cp; cp++)
    416 		if (*cp != ' ' && *cp != '\t')
    417 			return 0;
    418 	return 1;
    419 }
    420 
    421 /*
    422  * Start of a "comment".
    423  * Ignore it.
    424  */
    425 static char *
    426 skip_comment(char *cp)
    427 {
    428 	int nesting = 1;
    429 
    430 	for (/*EMPTY*/; nesting > 0 && *cp; cp++) {
    431 		switch (*cp) {
    432 		case '\\':
    433 			if (cp[1])
    434 				cp++;
    435 			break;
    436 		case '(':
    437 			nesting++;
    438 			break;
    439 		case ')':
    440 			nesting--;
    441 			break;
    442 		}
    443 	}
    444 	return cp;
    445 }
    446 
    447 /*
    448  * Skin an arpa net address according to the RFC 822 interpretation
    449  * of "host-phrase."
    450  */
    451 PUBLIC char *
    452 skin(char *name)
    453 {
    454 	int c;
    455 	char *cp, *cp2;
    456 	char *bufend;
    457 	int gotlt, lastsp;
    458 	char nbuf[LINESIZE];
    459 
    460 	if (name == NULL)
    461 		return NULL;
    462 	if (strchr(name, '(') == NULL && strchr(name, '<') == NULL
    463 	    && strchr(name, ' ') == NULL)
    464 		return name;
    465 	gotlt = 0;
    466 	lastsp = 0;
    467 	bufend = nbuf;
    468 	for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; /* EMPTY */) {
    469 		switch (c) {
    470 		case '(':
    471 			cp = skip_comment(cp);
    472 			lastsp = 0;
    473 			break;
    474 
    475 		case '"':
    476 			/*
    477 			 * Start of a "quoted-string".
    478 			 * Copy it in its entirety.
    479 			 */
    480 			while ((c = *cp) != '\0') {
    481 				cp++;
    482 				if (c == '"')
    483 					break;
    484 				if (c != '\\')
    485 					*cp2++ = c;
    486 				else if ((c = *cp) != '\0') {
    487 					*cp2++ = c;
    488 					cp++;
    489 				}
    490 			}
    491 			lastsp = 0;
    492 			break;
    493 
    494 		case ' ':
    495 			if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
    496 				cp += 3, *cp2++ = '@';
    497 			else
    498 			if (cp[0] == '@' && cp[1] == ' ')
    499 				cp += 2, *cp2++ = '@';
    500 			else
    501 				lastsp = 1;
    502 			break;
    503 
    504 		case '<':
    505 			cp2 = bufend;
    506 			gotlt++;
    507 			lastsp = 0;
    508 			break;
    509 
    510 		case '>':
    511 			if (gotlt) {
    512 				gotlt = 0;
    513 				while ((c = *cp) && c != ',') {
    514 					cp++;
    515 					if (c == '(')
    516 						cp = skip_comment(cp);
    517 					else if (c == '"')
    518 						while ((c = *cp) != '\0') {
    519 							cp++;
    520 							if (c == '"')
    521 								break;
    522 							if (c == '\\' && *cp)
    523 								cp++;
    524 						}
    525 				}
    526 				lastsp = 0;
    527 				break;
    528 			}
    529 			/* FALLTHROUGH */
    530 
    531 		default:
    532 			if (lastsp) {
    533 				lastsp = 0;
    534 				*cp2++ = ' ';
    535 			}
    536 			*cp2++ = c;
    537 			if (c == ',' && !gotlt) {
    538 				*cp2++ = ' ';
    539 				for (/*EMPTY*/; *cp == ' '; cp++)
    540 					continue;
    541 				lastsp = 0;
    542 				bufend = cp2;
    543 			}
    544 		}
    545 	}
    546 	*cp2 = 0;
    547 
    548 	return savestr(nbuf);
    549 }
    550 
    551 /*
    552  * Fetch the sender's name from the passed message.
    553  * Reptype can be
    554  *	0 -- get sender's name for display purposes
    555  *	1 -- get sender's name for reply
    556  *	2 -- get sender's name for Reply
    557  */
    558 static char *
    559 name1(struct message *mp, int reptype)
    560 {
    561 	char namebuf[LINESIZE];
    562 	char linebuf[LINESIZE];
    563 	char *cp, *cp2;
    564 	FILE *ibuf;
    565 	int firstrun = 1;
    566 
    567 	if ((cp = hfield("from", mp)) != NULL)
    568 		return cp;
    569 	if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
    570 		return cp;
    571 	ibuf = setinput(mp);
    572 	namebuf[0] = '\0';
    573 	if (mail_readline(ibuf, linebuf, LINESIZE) < 0)
    574 		return savestr(namebuf);
    575  newname:
    576 	for (cp = linebuf; *cp && *cp != ' '; cp++)
    577 		continue;
    578 	for (/*EMPTY*/; *cp == ' ' || *cp == '\t'; cp++)
    579 		continue;
    580 	for (cp2 = &namebuf[strlen(namebuf)];
    581 	     *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
    582 		*cp2++ = *cp++;
    583 	*cp2 = '\0';
    584 	if (mail_readline(ibuf, linebuf, LINESIZE) < 0)
    585 		return savestr(namebuf);
    586 	if ((cp = strchr(linebuf, 'F')) == NULL)
    587 		return savestr(namebuf);
    588 	if (strncmp(cp, "From", 4) != 0)
    589 		return savestr(namebuf);
    590 	while ((cp = strchr(cp, 'r')) != NULL) {
    591 		if (strncmp(cp, "remote", 6) == 0) {
    592 			if ((cp = strchr(cp, 'f')) == NULL)
    593 				break;
    594 			if (strncmp(cp, "from", 4) != 0)
    595 				break;
    596 			if ((cp = strchr(cp, ' ')) == NULL)
    597 				break;
    598 			cp++;
    599 			if (firstrun) {
    600 				cp2 = namebuf;
    601 				firstrun = 0;
    602 			} else
    603 				cp2 = strrchr(namebuf, '!') + 1;
    604 			while (*cp && cp2 < namebuf + LINESIZE - 1)
    605 				*cp2++ = *cp++;
    606 			if (cp2 < namebuf + LINESIZE - 1)
    607 				*cp2++ = '!';
    608 			*cp2 = '\0';
    609 			if (cp2 < namebuf + LINESIZE - 1)
    610 				goto newname;
    611 			else
    612 				break;
    613 		}
    614 		cp++;
    615 	}
    616 	return savestr(namebuf);
    617 }
    618 
    619 /*
    620  * Count the occurrences of c in str
    621  */
    622 static int
    623 charcount(char *str, int c)
    624 {
    625 	char *cp;
    626 	int i;
    627 
    628 	for (i = 0, cp = str; *cp; cp++)
    629 		if (*cp == c)
    630 			i++;
    631 	return i;
    632 }
    633 
    634 /*
    635  * Get sender's name from this message.  If the message has
    636  * a bunch of arpanet stuff in it, we may have to skin the name
    637  * before returning it.
    638  */
    639 PUBLIC char *
    640 nameof(struct message *mp, int reptype)
    641 {
    642 	char *cp, *cp2;
    643 
    644 	cp = skin(name1(mp, reptype));
    645 	if (reptype != 0 || charcount(cp, '!') < 2)
    646 		return cp;
    647 	cp2 = strrchr(cp, '!');
    648 	cp2--;
    649 	while (cp2 > cp && *cp2 != '!')
    650 		cp2--;
    651 	if (*cp2 == '!')
    652 		return cp2 + 1;
    653 	return cp;
    654 }
    655 
    656 /*
    657  * Copy s1 to s2, return pointer to null in s2.
    658  */
    659 PUBLIC char *
    660 copy(char *s1, char *s2)
    661 {
    662 
    663 	while ((*s2++ = *s1++) != '\0')
    664 		continue;
    665 	return s2 - 1;
    666 }
    667 
    668 /*
    669  * The core routine to check the ignore table for a field.
    670  * Note: realfield should be lower-cased!
    671  */
    672 PUBLIC int
    673 member(char *realfield, struct ignoretab *table)
    674 {
    675 	struct ignore *igp;
    676 
    677 	for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
    678 		if (*igp->i_field == *realfield &&
    679 		    equal(igp->i_field, realfield))
    680 			return 1;
    681 	return 0;
    682 }
    683 
    684 /*
    685  * See if the given header field is supposed to be ignored.
    686  */
    687 PUBLIC int
    688 isign(const char *field, struct ignoretab ignoretabs[2])
    689 {
    690 	char realfld[LINESIZE];
    691 
    692 	if (ignoretabs == ignoreall)
    693 		return 1;
    694 	/*
    695 	 * Lower-case the string, so that "Status" and "status"
    696 	 * will hash to the same place.
    697 	 */
    698 	istrcpy(realfld, field);
    699 	if (ignoretabs[1].i_count > 0)
    700 		return !member(realfld, ignoretabs + 1);
    701 	else
    702 		return member(realfld, ignoretabs);
    703 }
    704 
    705 /*
    706  * Write a file to stdout, skipping comment lines.
    707  */
    708 PUBLIC void
    709 cathelp(const char *fname)
    710 {
    711 	char *line;
    712 	FILE *f;
    713 	size_t len;
    714 
    715 	if ((f = Fopen(fname, "r")) == NULL) {
    716 		warn(fname);
    717 		return;
    718 	}
    719 	while ((line = fgetln(f, &len)) != NULL) {
    720 		if (*line == '#')
    721 			continue;
    722 		if (fwrite(line, 1, len, stdout) != len)
    723 			break;
    724 	}
    725 	(void)Fclose(f);
    726 	return;
    727 }
    728