Home | History | Annotate | Line # | Download | only in mail
support.c revision 1.3
      1 /*	$NetBSD: support.c,v 1.3 2002/03/02 15:27:52 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.3 2002/03/02 15:27:52 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)) != NOSTR)
     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)) != NOSTR) {
     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++ != NOSTR;)
    126 		;
    127 	return ap - argv - 1;
    128 }
    129 
    130 /*
    131  * Return the desired header line from the passed message
    132  * pointer (or NOSTR 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 = NOSTR;
    142 
    143 	ibuf = setinput(mp);
    144 	if ((lc = mp->m_lines - 1) < 0)
    145 		return NOSTR;
    146 	if (readline(ibuf, linebuf, LINESIZE) < 0)
    147 		return NOSTR;
    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(linebuf, colon, field)
    220 	char linebuf[], field[];
    221 	char *colon;
    222 {
    223 	char *cp = colon;
    224 
    225 	*cp = 0;
    226 	if (strcasecmp(linebuf, field) != 0) {
    227 		*cp = ':';
    228 		return 0;
    229 	}
    230 	*cp = ':';
    231 	for (cp++; *cp == ' ' || *cp == '\t'; cp++)
    232 		;
    233 	return cp;
    234 }
    235 
    236 /*
    237  * Copy a string, lowercasing it as we go.
    238  */
    239 void
    240 istrcpy(char *dest, char *src)
    241 {
    242 
    243 	do {
    244 		if (isupper((unsigned char)*src))
    245 			*dest++ = tolower(*src);
    246 		else
    247 			*dest++ = *src;
    248 	} while (*src++ != 0);
    249 }
    250 
    251 /*
    252  * The following code deals with input stacking to do source
    253  * commands.  All but the current file pointer are saved on
    254  * the stack.
    255  */
    256 
    257 static	int	ssp;			/* Top of file stack */
    258 struct sstack {
    259 	FILE	*s_file;		/* File we were in. */
    260 	int	s_cond;			/* Saved state of conditionals */
    261 	int	s_loading;		/* Loading .mailrc, etc. */
    262 } sstack[NOFILE];
    263 
    264 /*
    265  * Pushdown current input file and switch to a new one.
    266  * Set the global flag "sourcing" so that others will realize
    267  * that they are no longer reading from a tty (in all probability).
    268  */
    269 int
    270 source(void *v)
    271 {
    272 	char **arglist = v;
    273 	FILE *fi;
    274 	char *cp;
    275 
    276 	if ((cp = expand(*arglist)) == NOSTR)
    277 		return(1);
    278 	if ((fi = Fopen(cp, "r")) == NULL) {
    279 		perror(cp);
    280 		return(1);
    281 	}
    282 	if (ssp >= NOFILE - 1) {
    283 		printf("Too much \"sourcing\" going on.\n");
    284 		Fclose(fi);
    285 		return(1);
    286 	}
    287 	sstack[ssp].s_file = input;
    288 	sstack[ssp].s_cond = cond;
    289 	sstack[ssp].s_loading = loading;
    290 	ssp++;
    291 	loading = 0;
    292 	cond = CANY;
    293 	input = fi;
    294 	sourcing++;
    295 	return(0);
    296 }
    297 
    298 /*
    299  * Pop the current input back to the previous level.
    300  * Update the "sourcing" flag as appropriate.
    301  */
    302 int
    303 unstack(void)
    304 {
    305 	if (ssp <= 0) {
    306 		printf("\"Source\" stack over-pop.\n");
    307 		sourcing = 0;
    308 		return(1);
    309 	}
    310 	Fclose(input);
    311 	if (cond != CANY)
    312 		printf("Unmatched \"if\"\n");
    313 	ssp--;
    314 	cond = sstack[ssp].s_cond;
    315 	loading = sstack[ssp].s_loading;
    316 	input = sstack[ssp].s_file;
    317 	if (ssp == 0)
    318 		sourcing = loading;
    319 	return(0);
    320 }
    321 
    322 /*
    323  * Touch the indicated file.
    324  * This is nifty for the shell.
    325  */
    326 void
    327 alter(char *name)
    328 {
    329 	struct stat sb;
    330 	struct timeval tv[2];
    331 
    332 	if (stat(name, &sb))
    333 		return;
    334 	(void) gettimeofday(&tv[0], (struct timezone *)0);
    335 	tv[0].tv_sec++;
    336 	TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
    337 	(void) utimes(name, tv);
    338 }
    339 
    340 /*
    341  * Examine the passed line buffer and
    342  * return true if it is all blanks and tabs.
    343  */
    344 int
    345 blankline(char linebuf[])
    346 {
    347 	char *cp;
    348 
    349 	for (cp = linebuf; *cp; cp++)
    350 		if (*cp != ' ' && *cp != '\t')
    351 			return(0);
    352 	return(1);
    353 }
    354 
    355 /*
    356  * Get sender's name from this message.  If the message has
    357  * a bunch of arpanet stuff in it, we may have to skin the name
    358  * before returning it.
    359  */
    360 char *
    361 nameof(struct message *mp, int reptype)
    362 {
    363 	char *cp, *cp2;
    364 
    365 	cp = skin(name1(mp, reptype));
    366 	if (reptype != 0 || charcount(cp, '!') < 2)
    367 		return(cp);
    368 	cp2 = strrchr(cp, '!');
    369 	cp2--;
    370 	while (cp2 > cp && *cp2 != '!')
    371 		cp2--;
    372 	if (*cp2 == '!')
    373 		return(cp2 + 1);
    374 	return(cp);
    375 }
    376 
    377 /*
    378  * Start of a "comment".
    379  * Ignore it.
    380  */
    381 char *
    382 skip_comment(char *cp)
    383 {
    384 	int nesting = 1;
    385 
    386 	for (; nesting > 0 && *cp; cp++) {
    387 		switch (*cp) {
    388 		case '\\':
    389 			if (cp[1])
    390 				cp++;
    391 			break;
    392 		case '(':
    393 			nesting++;
    394 			break;
    395 		case ')':
    396 			nesting--;
    397 			break;
    398 		}
    399 	}
    400 	return cp;
    401 }
    402 
    403 /*
    404  * Skin an arpa net address according to the RFC 822 interpretation
    405  * of "host-phrase."
    406  */
    407 char *
    408 skin(char *name)
    409 {
    410 	int c;
    411 	char *cp, *cp2;
    412 	char *bufend;
    413 	int gotlt, lastsp;
    414 	char nbuf[BUFSIZ];
    415 
    416 	if (name == NOSTR)
    417 		return(NOSTR);
    418 	if (strchr(name, '(') == NOSTR && strchr(name, '<') == NOSTR
    419 	    && strchr(name, ' ') == NOSTR)
    420 		return(name);
    421 	gotlt = 0;
    422 	lastsp = 0;
    423 	bufend = nbuf;
    424 	for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) {
    425 		switch (c) {
    426 		case '(':
    427 			cp = skip_comment(cp);
    428 			lastsp = 0;
    429 			break;
    430 
    431 		case '"':
    432 			/*
    433 			 * Start of a "quoted-string".
    434 			 * Copy it in its entirety.
    435 			 */
    436 			while ((c = *cp) != '\0') {
    437 				cp++;
    438 				if (c == '"')
    439 					break;
    440 				if (c != '\\')
    441 					*cp2++ = c;
    442 				else if ((c = *cp) != '\0') {
    443 					*cp2++ = c;
    444 					cp++;
    445 				}
    446 			}
    447 			lastsp = 0;
    448 			break;
    449 
    450 		case ' ':
    451 			if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
    452 				cp += 3, *cp2++ = '@';
    453 			else
    454 			if (cp[0] == '@' && cp[1] == ' ')
    455 				cp += 2, *cp2++ = '@';
    456 			else
    457 				lastsp = 1;
    458 			break;
    459 
    460 		case '<':
    461 			cp2 = bufend;
    462 			gotlt++;
    463 			lastsp = 0;
    464 			break;
    465 
    466 		case '>':
    467 			if (gotlt) {
    468 				gotlt = 0;
    469 				while ((c = *cp) && c != ',') {
    470 					cp++;
    471 					if (c == '(')
    472 						cp = skip_comment(cp);
    473 					else if (c == '"')
    474 						while ((c = *cp) != '\0') {
    475 							cp++;
    476 							if (c == '"')
    477 								break;
    478 							if (c == '\\' && *cp)
    479 								cp++;
    480 						}
    481 				}
    482 				lastsp = 0;
    483 				break;
    484 			}
    485 			/* Fall into . . . */
    486 
    487 		default:
    488 			if (lastsp) {
    489 				lastsp = 0;
    490 				*cp2++ = ' ';
    491 			}
    492 			*cp2++ = c;
    493 			if (c == ',' && !gotlt) {
    494 				*cp2++ = ' ';
    495 				for (; *cp == ' '; cp++)
    496 					;
    497 				lastsp = 0;
    498 				bufend = cp2;
    499 			}
    500 		}
    501 	}
    502 	*cp2 = 0;
    503 
    504 	return(savestr(nbuf));
    505 }
    506 
    507 /*
    508  * Fetch the sender's name from the passed message.
    509  * Reptype can be
    510  *	0 -- get sender's name for display purposes
    511  *	1 -- get sender's name for reply
    512  *	2 -- get sender's name for Reply
    513  */
    514 char *
    515 name1(struct message *mp, int reptype)
    516 {
    517 	char namebuf[LINESIZE];
    518 	char linebuf[LINESIZE];
    519 	char *cp, *cp2;
    520 	FILE *ibuf;
    521 	int firstrun = 1;
    522 
    523 	if ((cp = hfield("from", mp)) != NOSTR)
    524 		return cp;
    525 	if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
    526 		return cp;
    527 	ibuf = setinput(mp);
    528 	namebuf[0] = '\0';
    529 	if (readline(ibuf, linebuf, LINESIZE) < 0)
    530 		return(savestr(namebuf));
    531 newname:
    532 	for (cp = linebuf; *cp && *cp != ' '; cp++)
    533 		;
    534 	for (; *cp == ' ' || *cp == '\t'; cp++)
    535 		;
    536 	for (cp2 = &namebuf[strlen(namebuf)];
    537 	     *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
    538 		*cp2++ = *cp++;
    539 	*cp2 = '\0';
    540 	if (readline(ibuf, linebuf, LINESIZE) < 0)
    541 		return(savestr(namebuf));
    542 	if ((cp = strchr(linebuf, 'F')) == NULL)
    543 		return(savestr(namebuf));
    544 	if (strncmp(cp, "From", 4) != 0)
    545 		return(savestr(namebuf));
    546 	while ((cp = strchr(cp, 'r')) != NULL) {
    547 		if (strncmp(cp, "remote", 6) == 0) {
    548 			if ((cp = strchr(cp, 'f')) == NULL)
    549 				break;
    550 			if (strncmp(cp, "from", 4) != 0)
    551 				break;
    552 			if ((cp = strchr(cp, ' ')) == NULL)
    553 				break;
    554 			cp++;
    555 			if (firstrun) {
    556 				cp2 = namebuf;
    557 				firstrun = 0;
    558 			} else
    559 				cp2 = strrchr(namebuf, '!') + 1;
    560 			while (*cp && cp2 < namebuf + LINESIZE - 1)
    561 				*cp2++ = *cp++;
    562 			if (cp2 < namebuf + LINESIZE - 1)
    563 				*cp2++ = '!';
    564 			*cp2 = '\0';
    565 			if (cp2 < namebuf + LINESIZE - 1)
    566 				goto newname;
    567 			else
    568 				break;
    569 		}
    570 		cp++;
    571 	}
    572 	return(savestr(namebuf));
    573 }
    574 
    575 /*
    576  * Count the occurances of c in str
    577  */
    578 int
    579 charcount(char *str, int c)
    580 {
    581 	char *cp;
    582 	int i;
    583 
    584 	for (i = 0, cp = str; *cp; cp++)
    585 		if (*cp == c)
    586 			i++;
    587 	return(i);
    588 }
    589 
    590 /*
    591  * Are any of the characters in the two strings the same?
    592  */
    593 int
    594 anyof(char *s1, char *s2)
    595 {
    596 
    597 	while (*s1)
    598 		if (strchr(s2, *s1++))
    599 			return 1;
    600 	return 0;
    601 }
    602 
    603 /*
    604  * Convert c to upper case
    605  */
    606 int
    607 upcase(int c)
    608 {
    609 
    610 	if (islower(c))
    611 		return toupper(c);
    612 	return c;
    613 }
    614 
    615 /*
    616  * Copy s1 to s2, return pointer to null in s2.
    617  */
    618 char *
    619 copy(char *s1, char *s2)
    620 {
    621 
    622 	while ((*s2++ = *s1++) != '\0')
    623 		;
    624 	return s2 - 1;
    625 }
    626 
    627 /*
    628  * See if the given header field is supposed to be ignored.
    629  */
    630 int
    631 isign(char *field, struct ignoretab ignoretabs[2])
    632 {
    633 	char realfld[LINESIZE];
    634 
    635 	if (ignoretabs == ignoreall)
    636 		return 1;
    637 	/*
    638 	 * Lower-case the string, so that "Status" and "status"
    639 	 * will hash to the same place.
    640 	 */
    641 	istrcpy(realfld, field);
    642 	if (ignoretabs[1].i_count > 0)
    643 		return (!member(realfld, ignoretabs + 1));
    644 	else
    645 		return (member(realfld, ignoretabs));
    646 }
    647 
    648 int
    649 member(char *realfield, struct ignoretab *table)
    650 {
    651 	struct ignore *igp;
    652 
    653 	for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
    654 		if (*igp->i_field == *realfield &&
    655 		    equal(igp->i_field, realfield))
    656 			return (1);
    657 	return (0);
    658 }
    659