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