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