Home | History | Annotate | Download | only in mailx
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     23 /*	  All Rights Reserved  	*/
     24 
     25 
     26 /*
     27  * Copyright 1985-2002 Sun Microsystems, Inc. All rights reserved.
     28  * Use is subject to license terms.
     29  */
     30 
     31 /*
     32  * University Copyright- Copyright (c) 1982, 1986, 1988
     33  * The Regents of the University of California
     34  * All Rights Reserved
     35  *
     36  * University Acknowledgment- Portions of this document are derived from
     37  * software developed by the University of California, Berkeley, and its
     38  * contributors.
     39  */
     40 
     41 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     42 
     43 #include "rcv.h"
     44 #include <locale.h>
     45 
     46 /*
     47  * mailx -- a modified version of a University of California at Berkeley
     48  *	mail program
     49  *
     50  * Auxiliary functions.
     51  */
     52 
     53 static char	*phrase(char *name, int token, int comma);
     54 static char	*ripoff(register char *buf);
     55 
     56 /*
     57  * Return a pointer to a dynamic copy of the argument.
     58  */
     59 
     60 char *
     61 savestr(char *str)
     62 {
     63 	register char *cp, *cp2, *top;
     64 
     65 	for (cp = str; *cp; cp++)
     66 		;
     67 	top = (char *)salloc((unsigned)(cp-str + 1));
     68 	if (top == NOSTR)
     69 		return(NOSTR);
     70 	for (cp = str, cp2 = top; *cp; cp++)
     71 		*cp2++ = *cp;
     72 	*cp2 = 0;
     73 	return(top);
     74 }
     75 
     76 /*
     77  * Announce a fatal error and die.
     78  */
     79 
     80 void
     81 panic(char *str)
     82 {
     83 	fprintf(stderr, gettext("mailx: Panic - %s\n"), str);
     84 	exit(1);
     85 	/* NOTREACHED */
     86 }
     87 
     88 /*
     89  * Touch the named message by setting its MTOUCH flag.
     90  * Touched messages have the effect of not being sent
     91  * back to the system mailbox on exit.
     92  */
     93 
     94 void
     95 touch(int mesg)
     96 {
     97 	register struct message *mp;
     98 
     99 	if (mesg < 1 || mesg > msgCount)
    100 		return;
    101 	mp = &message[mesg-1];
    102 	mp->m_flag |= MTOUCH;
    103 	if ((mp->m_flag & MREAD) == 0)
    104 		mp->m_flag |= MREAD|MSTATUS;
    105 }
    106 
    107 /*
    108  * Test to see if the passed file name is a directory.
    109  * Return true if it is.
    110  */
    111 
    112 int
    113 isdir(char name[])
    114 {
    115 	struct stat sbuf;
    116 
    117 	if (stat(name, &sbuf) < 0)
    118 		return(0);
    119 	return((sbuf.st_mode & S_IFMT) == S_IFDIR);
    120 }
    121 
    122 /*
    123  * Count the number of arguments in the given string raw list.
    124  */
    125 
    126 int
    127 argcount(char **argv)
    128 {
    129 	register char **ap;
    130 
    131 	for (ap = argv; *ap != NOSTR; ap++)
    132 		;
    133 	return(ap-argv);
    134 }
    135 
    136 /*
    137  * Return the desired header line from the passed message
    138  * pointer (or NOSTR if the desired header field is not available).
    139  * Read all the header lines and concatenate multiple instances of
    140  * the requested header.
    141  */
    142 
    143 char *
    144 hfield(char field[], struct message *mp, char *(*add)(char *, char *))
    145 {
    146 	register FILE *ibuf;
    147 	char linebuf[LINESIZE];
    148 	register long lc;
    149 	char *r = NOSTR;
    150 
    151 	ibuf = setinput(mp);
    152 	if ((lc = mp->m_lines) <= 0)
    153 		return(NOSTR);
    154 	if (readline(ibuf, linebuf) < 0)
    155 		return(NOSTR);
    156 	lc--;
    157 	while ((lc = gethfield(ibuf, linebuf, lc)) >= 0)
    158 		if (ishfield(linebuf, field))
    159 			r = (*add)(r, hcontents(linebuf));
    160 	return r;
    161 }
    162 
    163 /*
    164  * Return the next header field found in the given message.
    165  * Return > 0 if something found, <= 0 elsewise.
    166  * Must deal with \ continuations & other such fraud.
    167  */
    168 
    169 int
    170 gethfield(
    171 	register FILE *f,
    172 	char linebuf[],
    173 	register long rem)
    174 {
    175 	char line2[LINESIZE];
    176 	register char *cp, *cp2;
    177 	register int c;
    178 
    179 	for (;;) {
    180 		if (rem <= 0)
    181 			return(-1);
    182 		if (readline(f, linebuf) < 0)
    183 			return(-1);
    184 		rem--;
    185 		if (strlen(linebuf) == 0)
    186 			return(-1);
    187 		if (isspace(linebuf[0]))
    188 			continue;
    189 		if (!headerp(linebuf))
    190 			return(-1);
    191 
    192 		/*
    193 		 * I guess we got a headline.
    194 		 * Handle wraparounding
    195 		 */
    196 
    197 		for (;;) {
    198 			if (rem <= 0)
    199 				break;
    200 			c = getc(f);
    201 			ungetc(c, f);
    202 			if (!isspace(c) || c == '\n')
    203 				break;
    204 			if (readline(f, line2) < 0)
    205 				break;
    206 			rem--;
    207 			cp2 = line2;
    208 			for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
    209 				;
    210 			if (strlen(linebuf) + strlen(cp2) >=
    211 			    (unsigned)LINESIZE-2)
    212 				break;
    213 			cp = &linebuf[strlen(linebuf)];
    214 			while (cp > linebuf &&
    215 			    (isspace(cp[-1]) || cp[-1] == '\\'))
    216 				cp--;
    217 			*cp++ = ' ';
    218 			for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
    219 				;
    220 			nstrcpy(cp, LINESIZE - (cp - linebuf), cp2);
    221 		}
    222 		if ((c = strlen(linebuf)) > 0) {
    223 			cp = &linebuf[c-1];
    224 			while (cp > linebuf && isspace(*cp))
    225 				cp--;
    226 			*++cp = 0;
    227 		}
    228 		return(rem);
    229 	}
    230 	/* NOTREACHED */
    231 }
    232 
    233 /*
    234  * Check whether the passed line is a header line of
    235  * the desired breed.
    236  */
    237 
    238 int
    239 ishfield(char linebuf[], char field[])
    240 {
    241 	register char *cp;
    242 
    243 	if ((cp = strchr(linebuf, ':')) == NOSTR)
    244 		return(0);
    245 	if (cp == linebuf)
    246 		return(0);
    247 	*cp = 0;
    248 	if (icequal(linebuf, field)) {
    249 		*cp = ':';
    250 		return(1);
    251 	}
    252 	*cp = ':';
    253 	return(0);
    254 }
    255 
    256 /*
    257  * Extract the non label information from the given header field
    258  * and return it.
    259  */
    260 
    261 char *
    262 hcontents(char hfield[])
    263 {
    264 	register char *cp;
    265 
    266 	if ((cp = strchr(hfield, ':')) == NOSTR)
    267 		return(NOSTR);
    268 	cp++;
    269 	while (*cp && isspace(*cp))
    270 		cp++;
    271 	return(cp);
    272 }
    273 
    274 /*
    275  * Compare two strings, ignoring case.
    276  */
    277 
    278 int
    279 icequal(register char *s1, register char *s2)
    280 {
    281 
    282 	while (toupper(*s1++) == toupper(*s2))
    283 		if (*s2++ == 0)
    284 			return(1);
    285 	return(0);
    286 }
    287 
    288 /*
    289  * Copy a string, lowercasing it as we go. Here dstsize is the size of
    290  * the destination buffer dst.
    291  */
    292 void
    293 istrcpy(char *dst, int dstsize, char *src)
    294 {
    295 	register char *cp, *cp2;
    296 
    297 	cp2 = dst;
    298 	cp = src;
    299 
    300 	while (--dstsize > 0 && *cp != '\0')
    301 		*cp2++ = tolower(*cp++);
    302 	*cp2 = '\0';
    303 }
    304 
    305 /*
    306  * The following code deals with input stacking to do source
    307  * commands.  All but the current file pointer are saved on
    308  * the stack.
    309  */
    310 
    311 static	int	ssp = -1;		/* Top of file stack */
    312 static struct sstack {
    313 	FILE	*s_file;		/* File we were in. */
    314 	int	s_cond;			/* Saved state of conditionals */
    315 	int	s_loading;		/* Loading .mailrc, etc. */
    316 } *sstack;
    317 
    318 /*
    319  * Pushdown current input file and switch to a new one.
    320  * Set the global flag "sourcing" so that others will realize
    321  * that they are no longer reading from a tty (in all probability).
    322  */
    323 
    324 int
    325 source(char name[])
    326 {
    327 	register FILE *fi;
    328 	register char *cp;
    329 
    330 	if ((cp = expand(name)) == NOSTR)
    331 		return(1);
    332 	if ((fi = fopen(cp, "r")) == NULL) {
    333 		printf(gettext("Unable to open %s\n"), cp);
    334 		return(1);
    335 	}
    336 
    337 	if (!maxfiles) {
    338 		if ((maxfiles = (int)ulimit(4, 0)) < 0)
    339 #ifndef _NFILE
    340 # define _NFILE 20
    341 #endif
    342 			maxfiles = _NFILE;
    343 		sstack = (struct sstack *)calloc(maxfiles, sizeof(struct sstack));
    344 		if (sstack == NULL) {
    345 			printf(gettext(
    346 			    "Couldn't allocate memory for sourcing stack\n"));
    347 			fclose(fi);
    348 			return(1);
    349 		}
    350 	}
    351 
    352 	sstack[++ssp].s_file = input;
    353 	sstack[ssp].s_cond = cond;
    354 	sstack[ssp].s_loading = loading;
    355 	loading = 0;
    356 	cond = CANY;
    357 	input = fi;
    358 	sourcing++;
    359 	return(0);
    360 }
    361 
    362 /*
    363  * Pop the current input back to the previous level.
    364  * Update the "sourcing" flag as appropriate.
    365  */
    366 
    367 int
    368 unstack(void)
    369 {
    370 	if (ssp < 0) {
    371 		printf(gettext("\"Source\" stack over-pop.\n"));
    372 		sourcing = 0;
    373 		return(1);
    374 	}
    375 	fclose(input);
    376 	if (cond != CANY)
    377 		printf(gettext("Unmatched \"if\"\n"));
    378 	cond = sstack[ssp].s_cond;
    379 	loading = sstack[ssp].s_loading;
    380 	input = sstack[ssp--].s_file;
    381 	if (ssp < 0)
    382 		sourcing = loading;
    383 	return(0);
    384 }
    385 
    386 /*
    387  * Touch the indicated file.
    388  * This is nifty for the shell.
    389  * If we have the utime() system call, this is better served
    390  * by using that, since it will work for empty files.
    391  * On non-utime systems, we must sleep a second, then read.
    392  */
    393 
    394 void
    395 alter(char name[])
    396 {
    397 	int rc = utime(name, utimep);
    398 	extern int errno;
    399 
    400 	if (rc != 0) {
    401 		fprintf(stderr, gettext("Cannot utime %s in aux:alter\n"),
    402 name);
    403 		fprintf(stderr, gettext("Errno: %d\n"), errno);
    404 	}
    405 }
    406 
    407 /*
    408  * Examine the passed line buffer and
    409  * return true if it is all blanks and tabs.
    410  */
    411 
    412 int
    413 blankline(const char linebuf[])
    414 {
    415 	register const char *cp;
    416 
    417 	for (cp = linebuf; *cp; cp++)
    418 		if (!any(*cp, " \t"))
    419 			return(0);
    420 	return(1);
    421 }
    422 
    423 /*
    424  * Skin an arpa net address according to the RFC 822 interpretation
    425  * of "host-phrase."
    426  */
    427 static char *
    428 phrase(char *name, int token, int comma)
    429 {
    430 	register char c;
    431 	register char *cp, *cp2;
    432 	char *bufend, *nbufp;
    433 	int gotlt, lastsp, didq;
    434 	char nbuf[LINESIZE];
    435 	int nesting;
    436 
    437 	if (name == NOSTR)
    438 		return(NOSTR);
    439 	if (strlen(name) >= (unsigned)LINESIZE)
    440 		nbufp = (char *)salloc(strlen(name));
    441 	else
    442 		nbufp = nbuf;
    443 	gotlt = 0;
    444 	lastsp = 0;
    445 	bufend = nbufp;
    446 	for (cp = name, cp2 = bufend; (c = *cp++) != 0;) {
    447 		switch (c) {
    448 		case '(':
    449 			/*
    450 				Start of a comment, ignore it.
    451 			*/
    452 			nesting = 1;
    453 			while ((c = *cp) != 0) {
    454 				cp++;
    455 				switch(c) {
    456 				case '\\':
    457 					if (*cp == 0) goto outcm;
    458 					cp++;
    459 					break;
    460 				case '(':
    461 					nesting++;
    462 					break;
    463 				case ')':
    464 					--nesting;
    465 					break;
    466 				}
    467 				if (nesting <= 0) break;
    468 			}
    469 		outcm:
    470 			lastsp = 0;
    471 			break;
    472 		case '"':
    473 			/*
    474 				Start a quoted string.
    475 				Copy it in its entirety.
    476 			*/
    477 			didq = 0;
    478 			while ((c = *cp) != 0) {
    479 				cp++;
    480 				switch (c) {
    481 				case '\\':
    482 					if ((c = *cp) == 0) goto outqs;
    483 					cp++;
    484 					break;
    485 				case '"':
    486 					goto outqs;
    487 				}
    488 				if (gotlt == 0 || gotlt == '<') {
    489 					if (lastsp) {
    490 						lastsp = 0;
    491 						*cp2++ = ' ';
    492 					}
    493 					if (!didq) {
    494 						*cp2++ = '"';
    495 						didq++;
    496 					}
    497 					*cp2++ = c;
    498 				}
    499 			}
    500 		outqs:
    501 			if (didq)
    502 				*cp2++ = '"';
    503 			lastsp = 0;
    504 			break;
    505 
    506 		case ' ':
    507 		case '\t':
    508 		case '\n':
    509 			if (token && (!comma || c == '\n')) {
    510 			done:
    511 				cp[-1] = 0;
    512 				return cp;
    513 			}
    514 			lastsp = 1;
    515 			break;
    516 
    517 		case ',':
    518 			*cp2++ = c;
    519 			if (gotlt != '<') {
    520 				if (token)
    521 					goto done;
    522 				bufend = cp2;
    523 				gotlt = 0;
    524 			}
    525 			break;
    526 
    527 		case '<':
    528 			cp2 = bufend;
    529 			gotlt = c;
    530 			lastsp = 0;
    531 			break;
    532 
    533 		case '>':
    534 			if (gotlt == '<') {
    535 				gotlt = c;
    536 				break;
    537 			}
    538 
    539 			/* FALLTHROUGH . . . */
    540 
    541 		default:
    542 			if (gotlt == 0 || gotlt == '<') {
    543 				if (lastsp) {
    544 					lastsp = 0;
    545 					*cp2++ = ' ';
    546 				}
    547 				*cp2++ = c;
    548 			}
    549 			break;
    550 		}
    551 	}
    552 	*cp2 = 0;
    553 	return (token ? --cp : equal(name, nbufp) ? name :
    554 	    nbufp == nbuf ? savestr(nbuf) : nbufp);
    555 }
    556 
    557 char *
    558 skin(char *name)
    559 {
    560 	return phrase(name, 0, 0);
    561 }
    562 
    563 /*
    564  * Here sz is the buffer size of word.
    565  */
    566 char *
    567 yankword(char *name, char *word, int sz, int comma)
    568 {
    569 	char *cp;
    570 
    571 	if (name == 0)
    572 		return 0;
    573 	while (isspace(*name))
    574 		name++;
    575 	if (*name == 0)
    576 		return 0;
    577 	cp = phrase(name, 1, comma);
    578 	nstrcpy(word, sz, name);
    579 	return cp;
    580 }
    581 
    582 int
    583 docomma(char *s)
    584 {
    585 	return s && strpbrk(s, "(<,");
    586 }
    587 
    588 /*
    589  * Fetch the sender's name from the passed message.
    590  */
    591 
    592 char *
    593 nameof(register struct message *mp)
    594 {
    595 	char namebuf[LINESIZE];
    596 	char linebuf[LINESIZE];
    597 	register char *cp, *cp2;
    598 	register FILE *ibuf;
    599 	int first = 1, wint = 0;
    600 	char	*tmp;
    601 
    602 	if (value("from") && (cp = hfield("from", mp, addto)) != NOSTR)
    603 		return ripoff(cp);
    604 	ibuf = setinput(mp);
    605 	copy("", namebuf);
    606 	if (readline(ibuf, linebuf) <= 0)
    607 		return(savestr(namebuf));
    608 newname:
    609 	for (cp = linebuf; *cp != ' '; cp++)
    610 		;
    611 	while (any(*cp, " \t"))
    612 		cp++;
    613 	for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") &&
    614 	    cp2-namebuf < LINESIZE-1; *cp2++ = *cp++)
    615 		;
    616 	*cp2 = '\0';
    617 	for (;;) {
    618 		if (readline(ibuf, linebuf) <= 0)
    619 			break;
    620 		if (substr(linebuf,"forwarded by ") != -1)
    621 			continue;
    622 		if (linebuf[0] == 'F')
    623 			cp = linebuf;
    624 		else if (linebuf[0] == '>')
    625 			cp = linebuf + 1;
    626 		else
    627 			break;
    628 		if (strncmp(cp, "From ", 5) != 0)
    629 			break;
    630 		if ((wint = substr(cp, "remote from ")) != -1) {
    631 			cp += wint + 12;
    632 			if (first) {
    633 				copy(cp, namebuf);
    634 				first = 0;
    635 			} else {
    636 				tmp = strrchr(namebuf, '!') + 1;
    637 				nstrcpy(tmp,
    638 					sizeof (namebuf) - (tmp  - namebuf),
    639 					cp);
    640 			}
    641 			nstrcat(namebuf, sizeof (namebuf), "!");
    642 			goto newname;
    643 		} else
    644 			break;
    645 	}
    646 	for (cp = namebuf; *cp == '!'; cp++);
    647 	while (ishost(host, cp))
    648 		cp = strchr(cp, '!') + 1;
    649 	if (value("mustbang") && !strchr(cp, '!')) {
    650 		snprintf(linebuf, sizeof (linebuf), "%s!%s",
    651 			host, cp);
    652 		cp = linebuf;
    653 	}
    654 	if (cp2 = hfield("from", mp, addto))
    655 		return(splice(cp, cp2));
    656 	else
    657 		return(savestr(cp));
    658 }
    659 
    660 /*
    661  * Splice an address into a commented recipient header.
    662  */
    663 char *
    664 splice(char *addr, char *hdr)
    665 {
    666 	char buf[LINESIZE];
    667 	char *cp, *cp2;
    668 
    669 	if (cp = strchr(hdr, '<')) {
    670 		cp2 = strchr(cp, '>');
    671 		if (cp2 == NULL) {
    672 			nstrcpy(buf, sizeof (buf), addr);
    673 		} else {
    674 			snprintf(buf, sizeof (buf), "%.*s%s%s",
    675 				cp - hdr + 1, hdr, addr, cp2);
    676 		}
    677 	} else if (cp = strchr(hdr, '(')) {
    678 		snprintf(buf, sizeof (buf), "%s %s",
    679 			addr, cp);
    680 	} else
    681 		nstrcpy(buf, sizeof (buf), addr);
    682 	return savestr(ripoff(buf));
    683 }
    684 
    685 static char *
    686 ripoff(register char *buf)
    687 {
    688 	register char *cp;
    689 
    690 	cp = buf + strlen(buf);
    691 	while (--cp >= buf && isspace(*cp));
    692 	if (cp >= buf && *cp == ',')
    693 		cp--;
    694 	*++cp = 0;
    695 	return buf;
    696 }
    697 
    698 /*
    699  * Are any of the characters in the two strings the same?
    700  */
    701 
    702 int
    703 anyof(register char *s1, register char *s2)
    704 {
    705 	register int c;
    706 
    707 	while ((c = *s1++) != 0)
    708 		if (any(c, s2))
    709 			return(1);
    710 	return(0);
    711 }
    712 
    713 /*
    714  * See if the given header field is supposed to be ignored.
    715  * Fields of the form "Content-*" can't be ignored when saving.
    716  */
    717 int
    718 isign(char *field, int saving)
    719 {
    720 	char realfld[BUFSIZ];
    721 
    722 	/*
    723 	 * Lower-case the string, so that "Status" and "status"
    724 	 * will hash to the same place.
    725 	 */
    726 	istrcpy(realfld, sizeof (realfld), field);
    727 
    728 	if (saving && strncmp(realfld, "content-", 8) == 0)
    729 		return (0);
    730 
    731 	if (nretained > 0)
    732 		return (!member(realfld, retain));
    733 	else
    734 		return (member(realfld, ignore));
    735 }
    736 
    737 int
    738 member(register char *realfield, register struct ignore **table)
    739 {
    740 	register struct ignore *igp;
    741 
    742 	for (igp = table[hash(realfield)]; igp != 0; igp = igp->i_link)
    743 		if (equal(igp->i_field, realfield))
    744 			return (1);
    745 
    746 	return (0);
    747 }
    748 
    749 /*
    750  * This routine looks for string2 in string1.
    751  * If found, it returns the position string2 is found at,
    752  * otherwise it returns a -1.
    753  */
    754 int
    755 substr(char *string1, char *string2)
    756 {
    757 	int i, j, len1, len2;
    758 
    759 	len1 = strlen(string1);
    760 	len2 = strlen(string2);
    761 	for (i = 0; i < len1 - len2 + 1; i++) {
    762 		for (j = 0; j < len2 && string1[i+j] == string2[j]; j++)
    763 			;
    764 		if (j == len2)
    765 			return(i);
    766 	}
    767 	return(-1);
    768 }
    769 
    770 /*
    771  * Copies src to the dstsize buffer at dst. The copy will never
    772  * overflow the destination buffer and the buffer will always be null
    773  * terminated.
    774  */
    775 char *
    776 nstrcpy(char *dst, int dstsize, char *src)
    777 {
    778 	char *cp, *cp2;
    779 
    780 	cp2 = dst;
    781 	cp = src;
    782 
    783 	while (--dstsize > 0 && *cp != '\0')
    784 		*cp2++ = *cp++;
    785 	*cp2 = '\0';
    786 	return(dst);
    787 }
    788 
    789 /*
    790  * Appends src to the dstsize buffer at dst. The append will never
    791  * overflow the destination buffer and the buffer will always be null
    792  * terminated.
    793  */
    794 char *
    795 nstrcat(char *dst, int dstsize, char *src)
    796 {
    797 	char *cp, *cp2;
    798 
    799 	cp2 = dst;
    800 	cp = src;
    801 
    802 	while (*cp2 != '\0') {
    803 		cp2++;
    804 		dstsize--;
    805 	}
    806 	while (--dstsize > 0 && *cp != '\0')
    807 		*cp2++ = *cp++;
    808 	*cp2 = '\0';
    809 	return(dst);
    810 }
    811