Home | History | Annotate | Line # | Download | only in mail
support.c revision 1.15
      1 /*	$NetBSD: support.c,v 1.15 2006/10/21 21:37:21 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.15 2006/10/21 21:37:21 christos Exp $");
     38 #endif
     39 #endif /* not lint */
     40 
     41 #include "rcv.h"
     42 #include "extern.h"
     43 #include "mime.h"
     44 
     45 /*
     46  * Mail -- a mail program
     47  *
     48  * Auxiliary functions.
     49  */
     50 static char *save2str(char *, char *);
     51 static int gethfield(FILE *, char [], int, char **); /* don't call this outside hfield()
     52 							or decoding will not get done */
     53 /*
     54  * Return a pointer to a dynamic copy of the argument.
     55  */
     56 char *
     57 savestr(const char *str)
     58 {
     59 	char *new;
     60 	size_t size = strlen(str) + 1;
     61 
     62 	if ((new = salloc(size)) != NULL)
     63 		(void)memmove(new, str, size);
     64 	return new;
     65 }
     66 
     67 /*
     68  * Make a copy of new argument incorporating old one.
     69  */
     70 static char *
     71 save2str(char *str, char *old)
     72 {
     73 	char *new;
     74 	size_t newsize = strlen(str) + 1;
     75 	size_t oldsize = old ? strlen(old) + 1 : 0;
     76 
     77 	if ((new = salloc(newsize + oldsize)) != NULL) {
     78 		if (oldsize) {
     79 			(void)memmove(new, old, oldsize);
     80 			new[oldsize - 1] = ' ';
     81 		}
     82 		(void)memmove(new + oldsize, str, newsize);
     83 	}
     84 	return new;
     85 }
     86 
     87 /*
     88  * Touch the named message by setting its MTOUCH flag.
     89  * Touched messages have the effect of not being sent
     90  * back to the system mailbox on exit.
     91  */
     92 void
     93 touch(struct message *mp)
     94 {
     95 
     96 	mp->m_flag |= MTOUCH;
     97 	if ((mp->m_flag & MREAD) == 0)
     98 		mp->m_flag |= MREAD|MSTATUS;
     99 }
    100 
    101 /*
    102  * Test to see if the passed file name is a directory.
    103  * Return true if it is.
    104  */
    105 int
    106 isdir(const char name[])
    107 {
    108 	struct stat sbuf;
    109 
    110 	if (stat(name, &sbuf) < 0)
    111 		return(0);
    112 	return (S_ISDIR(sbuf.st_mode));
    113 }
    114 
    115 /*
    116  * Count the number of arguments in the given string raw list.
    117  */
    118 int
    119 argcount(char **argv)
    120 {
    121 	char **ap;
    122 
    123 	for (ap = argv; *ap++ != NULL;)
    124 		;
    125 	return ap - argv - 1;
    126 }
    127 
    128 /*
    129  * Return the desired header line from the passed message
    130  * pointer (or NULL if the desired header field is not available).
    131  */
    132 char *
    133 hfield(const char field[], const struct message *mp)
    134 {
    135 	FILE *ibuf;
    136 	char linebuf[LINESIZE];
    137 	int lc;
    138 	char *headerfield;
    139 	char *colon, *oldhfield = NULL;
    140 #ifdef MIME_SUPPORT
    141 	int decode;
    142 
    143 	decode = value(ENAME_MIME_DECODE_MSG) &&
    144 	    value(ENAME_MIME_DECODE_HDR);
    145 #endif
    146 
    147 	ibuf = setinput(mp);
    148 	if ((lc = mp->m_lines - 1) < 0)
    149 		return NULL;
    150 	if (mail_readline(ibuf, linebuf, LINESIZE) < 0)
    151 		return NULL;
    152 	while (lc > 0) {
    153 		if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
    154 			return oldhfield;
    155 #ifdef MIME_SUPPORT
    156 		if ((headerfield = ishfield(linebuf, colon, field)) != NULL) {
    157 			char linebuf2[LINESIZE];
    158 			if (decode && colon)
    159 				headerfield = mime_decode_hfield(linebuf2, sizeof(linebuf2), headerfield);
    160 			oldhfield = save2str(headerfield, oldhfield);
    161 		}
    162 #else
    163 		if ((headerfield = ishfield(linebuf, colon, field)) != NULL)
    164 			oldhfield = save2str(headerfield, oldhfield);
    165 #endif
    166 	}
    167 	return oldhfield;
    168 }
    169 
    170 /*
    171  * Return the next header field found in the given message.
    172  * Return >= 0 if something found, < 0 elsewise.
    173  * "colon" is set to point to the colon in the header.
    174  * Must deal with \ continuations & other such fraud.
    175  */
    176 static int
    177 gethfield(FILE *f, char linebuf[], int rem, char **colon)
    178 {
    179 	char line2[LINESIZE];
    180 	char *cp, *cp2;
    181 	int c;
    182 
    183 	for (;;) {
    184 		if (--rem < 0)
    185 			return -1;
    186 		if ((c = mail_readline(f, linebuf, LINESIZE)) <= 0)
    187 			return -1;
    188 		for (cp = linebuf; isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':';
    189 		     cp++)
    190 			;
    191 		if (*cp != ':' || cp == linebuf)
    192 			continue;
    193 		/*
    194 		 * I guess we got a headline.
    195 		 * Handle wraparounding
    196 		 */
    197 		*colon = cp;
    198 		cp = linebuf + c;
    199 		for (;;) {
    200 			while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
    201 				;
    202 			cp++;
    203 			if (rem <= 0)
    204 				break;
    205 			(void)ungetc(c = getc(f), f);
    206 			if (c != ' ' && c != '\t')
    207 				break;
    208 			if ((c = mail_readline(f, line2, LINESIZE)) < 0)
    209 				break;
    210 			rem--;
    211 			for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
    212 				;
    213 			c -= cp2 - line2;
    214 			if (cp + c >= linebuf + LINESIZE - 2)
    215 				break;
    216 			*cp++ = ' ';
    217 			(void)memmove(cp, cp2, (size_t)c);
    218 			cp += c;
    219 		}
    220 		*cp = 0;
    221 		return rem;
    222 	}
    223 	/* NOTREACHED */
    224 }
    225 
    226 /*
    227  * Check whether the passed line is a header line of
    228  * the desired breed.  Return the field body, or 0.
    229  */
    230 
    231 char*
    232 ishfield(const char linebuf[], char *colon, const char field[])
    233 {
    234 	char *cp = colon;
    235 
    236 	*cp = 0;
    237 	if (strcasecmp(linebuf, field) != 0) {
    238 		*cp = ':';
    239 		return 0;
    240 	}
    241 	*cp = ':';
    242 	for (cp++; *cp == ' ' || *cp == '\t'; cp++)
    243 		;
    244 	return cp;
    245 }
    246 
    247 /*
    248  * Copy a string, lowercasing it as we go.
    249  */
    250 void
    251 istrcpy(char *dest, const char *src)
    252 {
    253 
    254 	do {
    255 		*dest++ = tolower((unsigned char)*src);
    256 	} while (*src++ != 0);
    257 }
    258 
    259 /*
    260  * The following code deals with input stacking to do source
    261  * commands.  All but the current file pointer are saved on
    262  * the stack.
    263  */
    264 
    265 static	int	ssp;			/* Top of file stack */
    266 struct sstack {
    267 	FILE	*s_file;		/* File we were in. */
    268 	int	s_cond;			/* Saved state of conditionals */
    269 	int	s_loading;		/* Loading .mailrc, etc. */
    270 } sstack[NOFILE];
    271 
    272 /*
    273  * Pushdown current input file and switch to a new one.
    274  * Set the global flag "sourcing" so that others will realize
    275  * that they are no longer reading from a tty (in all probability).
    276  */
    277 int
    278 source(void *v)
    279 {
    280 	char **arglist = v;
    281 	FILE *fi;
    282 	const char *cp;
    283 
    284 	if ((cp = expand(*arglist)) == NULL)
    285 		return(1);
    286 	if ((fi = Fopen(cp, "r")) == NULL) {
    287 		warn("%s", cp);
    288 		return(1);
    289 	}
    290 	if (ssp >= NOFILE - 1) {
    291 		(void)printf("Too much \"sourcing\" going on.\n");
    292 		(void)Fclose(fi);
    293 		return(1);
    294 	}
    295 	sstack[ssp].s_file = input;
    296 	sstack[ssp].s_cond = cond;
    297 	sstack[ssp].s_loading = loading;
    298 	ssp++;
    299 	loading = 0;
    300 	cond = CANY;
    301 	input = fi;
    302 	sourcing++;
    303 	return(0);
    304 }
    305 
    306 /*
    307  * Pop the current input back to the previous level.
    308  * Update the "sourcing" flag as appropriate.
    309  */
    310 int
    311 unstack(void)
    312 {
    313 	if (ssp <= 0) {
    314 		(void)printf("\"Source\" stack over-pop.\n");
    315 		sourcing = 0;
    316 		return(1);
    317 	}
    318 	(void)Fclose(input);
    319 	if (cond != CANY)
    320 		(void)printf("Unmatched \"if\"\n");
    321 	ssp--;
    322 	cond = sstack[ssp].s_cond;
    323 	loading = sstack[ssp].s_loading;
    324 	input = sstack[ssp].s_file;
    325 	if (ssp == 0)
    326 		sourcing = loading;
    327 	return(0);
    328 }
    329 
    330 /*
    331  * Touch the indicated file.
    332  * This is nifty for the shell.
    333  */
    334 void
    335 alter(char *name)
    336 {
    337 	struct stat sb;
    338 	struct timeval tv[2];
    339 
    340 	if (stat(name, &sb))
    341 		return;
    342 	(void)gettimeofday(&tv[0], NULL);
    343 	tv[0].tv_sec++;
    344 	TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
    345 	(void)utimes(name, tv);
    346 }
    347 
    348 /*
    349  * Examine the passed line buffer and
    350  * return true if it is all blanks and tabs.
    351  */
    352 int
    353 blankline(char linebuf[])
    354 {
    355 	char *cp;
    356 
    357 	for (cp = linebuf; *cp; cp++)
    358 		if (*cp != ' ' && *cp != '\t')
    359 			return(0);
    360 	return(1);
    361 }
    362 
    363 /*
    364  * Get sender's name from this message.  If the message has
    365  * a bunch of arpanet stuff in it, we may have to skin the name
    366  * before returning it.
    367  */
    368 char *
    369 nameof(struct message *mp, int reptype)
    370 {
    371 	char *cp, *cp2;
    372 
    373 	cp = skin(name1(mp, reptype));
    374 	if (reptype != 0 || charcount(cp, '!') < 2)
    375 		return(cp);
    376 	cp2 = strrchr(cp, '!');
    377 	cp2--;
    378 	while (cp2 > cp && *cp2 != '!')
    379 		cp2--;
    380 	if (*cp2 == '!')
    381 		return(cp2 + 1);
    382 	return(cp);
    383 }
    384 
    385 /*
    386  * Start of a "comment".
    387  * Ignore it.
    388  */
    389 char *
    390 skip_comment(char *cp)
    391 {
    392 	int nesting = 1;
    393 
    394 	for (; nesting > 0 && *cp; cp++) {
    395 		switch (*cp) {
    396 		case '\\':
    397 			if (cp[1])
    398 				cp++;
    399 			break;
    400 		case '(':
    401 			nesting++;
    402 			break;
    403 		case ')':
    404 			nesting--;
    405 			break;
    406 		}
    407 	}
    408 	return cp;
    409 }
    410 
    411 /*
    412  * Skin an arpa net address according to the RFC 822 interpretation
    413  * of "host-phrase."
    414  */
    415 char *
    416 skin(char *name)
    417 {
    418 	int c;
    419 	char *cp, *cp2;
    420 	char *bufend;
    421 	int gotlt, lastsp;
    422 	char nbuf[BUFSIZ];
    423 
    424 	if (name == NULL)
    425 		return(NULL);
    426 	if (strchr(name, '(') == NULL && strchr(name, '<') == NULL
    427 	    && strchr(name, ' ') == NULL)
    428 		return(name);
    429 	gotlt = 0;
    430 	lastsp = 0;
    431 	bufend = nbuf;
    432 	for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; /* EMPTY */) {
    433 		switch (c) {
    434 		case '(':
    435 			cp = skip_comment(cp);
    436 			lastsp = 0;
    437 			break;
    438 
    439 		case '"':
    440 			/*
    441 			 * Start of a "quoted-string".
    442 			 * Copy it in its entirety.
    443 			 */
    444 			while ((c = *cp) != '\0') {
    445 				cp++;
    446 				if (c == '"')
    447 					break;
    448 				if (c != '\\')
    449 					*cp2++ = c;
    450 				else if ((c = *cp) != '\0') {
    451 					*cp2++ = c;
    452 					cp++;
    453 				}
    454 			}
    455 			lastsp = 0;
    456 			break;
    457 
    458 		case ' ':
    459 			if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
    460 				cp += 3, *cp2++ = '@';
    461 			else
    462 			if (cp[0] == '@' && cp[1] == ' ')
    463 				cp += 2, *cp2++ = '@';
    464 			else
    465 				lastsp = 1;
    466 			break;
    467 
    468 		case '<':
    469 			cp2 = bufend;
    470 			gotlt++;
    471 			lastsp = 0;
    472 			break;
    473 
    474 		case '>':
    475 			if (gotlt) {
    476 				gotlt = 0;
    477 				while ((c = *cp) && c != ',') {
    478 					cp++;
    479 					if (c == '(')
    480 						cp = skip_comment(cp);
    481 					else if (c == '"')
    482 						while ((c = *cp) != '\0') {
    483 							cp++;
    484 							if (c == '"')
    485 								break;
    486 							if (c == '\\' && *cp)
    487 								cp++;
    488 						}
    489 				}
    490 				lastsp = 0;
    491 				break;
    492 			}
    493 			/* FALLTHROUGH */
    494 
    495 		default:
    496 			if (lastsp) {
    497 				lastsp = 0;
    498 				*cp2++ = ' ';
    499 			}
    500 			*cp2++ = c;
    501 			if (c == ',' && !gotlt) {
    502 				*cp2++ = ' ';
    503 				for (; *cp == ' '; cp++)
    504 					;
    505 				lastsp = 0;
    506 				bufend = cp2;
    507 			}
    508 		}
    509 	}
    510 	*cp2 = 0;
    511 
    512 	return(savestr(nbuf));
    513 }
    514 
    515 /*
    516  * Fetch the sender's name from the passed message.
    517  * Reptype can be
    518  *	0 -- get sender's name for display purposes
    519  *	1 -- get sender's name for reply
    520  *	2 -- get sender's name for Reply
    521  */
    522 char *
    523 name1(struct message *mp, int reptype)
    524 {
    525 	char namebuf[LINESIZE];
    526 	char linebuf[LINESIZE];
    527 	char *cp, *cp2;
    528 	FILE *ibuf;
    529 	int firstrun = 1;
    530 
    531 	if ((cp = hfield("from", mp)) != NULL)
    532 		return cp;
    533 	if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
    534 		return cp;
    535 	ibuf = setinput(mp);
    536 	namebuf[0] = '\0';
    537 	if (mail_readline(ibuf, linebuf, LINESIZE) < 0)
    538 		return(savestr(namebuf));
    539 newname:
    540 	for (cp = linebuf; *cp && *cp != ' '; cp++)
    541 		;
    542 	for (; *cp == ' ' || *cp == '\t'; cp++)
    543 		;
    544 	for (cp2 = &namebuf[strlen(namebuf)];
    545 	     *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
    546 		*cp2++ = *cp++;
    547 	*cp2 = '\0';
    548 	if (mail_readline(ibuf, linebuf, LINESIZE) < 0)
    549 		return(savestr(namebuf));
    550 	if ((cp = strchr(linebuf, 'F')) == NULL)
    551 		return(savestr(namebuf));
    552 	if (strncmp(cp, "From", 4) != 0)
    553 		return(savestr(namebuf));
    554 	while ((cp = strchr(cp, 'r')) != NULL) {
    555 		if (strncmp(cp, "remote", 6) == 0) {
    556 			if ((cp = strchr(cp, 'f')) == NULL)
    557 				break;
    558 			if (strncmp(cp, "from", 4) != 0)
    559 				break;
    560 			if ((cp = strchr(cp, ' ')) == NULL)
    561 				break;
    562 			cp++;
    563 			if (firstrun) {
    564 				cp2 = namebuf;
    565 				firstrun = 0;
    566 			} else
    567 				cp2 = strrchr(namebuf, '!') + 1;
    568 			while (*cp && cp2 < namebuf + LINESIZE - 1)
    569 				*cp2++ = *cp++;
    570 			if (cp2 < namebuf + LINESIZE - 1)
    571 				*cp2++ = '!';
    572 			*cp2 = '\0';
    573 			if (cp2 < namebuf + LINESIZE - 1)
    574 				goto newname;
    575 			else
    576 				break;
    577 		}
    578 		cp++;
    579 	}
    580 	return(savestr(namebuf));
    581 }
    582 
    583 /*
    584  * Count the occurrences of c in str
    585  */
    586 int
    587 charcount(char *str, int c)
    588 {
    589 	char *cp;
    590 	int i;
    591 
    592 	for (i = 0, cp = str; *cp; cp++)
    593 		if (*cp == c)
    594 			i++;
    595 	return(i);
    596 }
    597 
    598 /*
    599  * Convert c to upper case
    600  */
    601 int
    602 upcase(int c)
    603 {
    604 
    605 	if (islower(c))
    606 		return toupper(c);
    607 	return c;
    608 }
    609 
    610 /*
    611  * Copy s1 to s2, return pointer to null in s2.
    612  */
    613 char *
    614 copy(char *s1, char *s2)
    615 {
    616 
    617 	while ((*s2++ = *s1++) != '\0')
    618 		;
    619 	return s2 - 1;
    620 }
    621 
    622 /*
    623  * See if the given header field is supposed to be ignored.
    624  */
    625 int
    626 isign(const char *field, struct ignoretab ignoretabs[2])
    627 {
    628 	char realfld[LINESIZE];
    629 
    630 	if (ignoretabs == ignoreall)
    631 		return 1;
    632 	/*
    633 	 * Lower-case the string, so that "Status" and "status"
    634 	 * will hash to the same place.
    635 	 */
    636 	istrcpy(realfld, field);
    637 	if (ignoretabs[1].i_count > 0)
    638 		return (!member(realfld, ignoretabs + 1));
    639 	else
    640 		return (member(realfld, ignoretabs));
    641 }
    642 
    643 int
    644 member(char *realfield, struct ignoretab *table)
    645 {
    646 	struct ignore *igp;
    647 
    648 	for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
    649 		if (*igp->i_field == *realfield &&
    650 		    equal(igp->i_field, realfield))
    651 			return (1);
    652 	return (0);
    653 }
    654