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