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