/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 1985-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #pragma ident "%Z%%M% %I% %E% SMI" #include "rcv.h" #include /* * mailx -- a modified version of a University of California at Berkeley * mail program * * Auxiliary functions. */ static char *phrase(char *name, int token, int comma); static char *ripoff(register char *buf); /* * Return a pointer to a dynamic copy of the argument. */ char * savestr(char *str) { register char *cp, *cp2, *top; for (cp = str; *cp; cp++) ; top = (char *)salloc((unsigned)(cp-str + 1)); if (top == NOSTR) return(NOSTR); for (cp = str, cp2 = top; *cp; cp++) *cp2++ = *cp; *cp2 = 0; return(top); } /* * Announce a fatal error and die. */ void panic(char *str) { fprintf(stderr, gettext("mailx: Panic - %s\n"), str); exit(1); /* NOTREACHED */ } /* * Touch the named message by setting its MTOUCH flag. * Touched messages have the effect of not being sent * back to the system mailbox on exit. */ void touch(int mesg) { register struct message *mp; if (mesg < 1 || mesg > msgCount) return; mp = &message[mesg-1]; mp->m_flag |= MTOUCH; if ((mp->m_flag & MREAD) == 0) mp->m_flag |= MREAD|MSTATUS; } /* * Test to see if the passed file name is a directory. * Return true if it is. */ int isdir(char name[]) { struct stat sbuf; if (stat(name, &sbuf) < 0) return(0); return((sbuf.st_mode & S_IFMT) == S_IFDIR); } /* * Count the number of arguments in the given string raw list. */ int argcount(char **argv) { register char **ap; for (ap = argv; *ap != NOSTR; ap++) ; return(ap-argv); } /* * Return the desired header line from the passed message * pointer (or NOSTR if the desired header field is not available). * Read all the header lines and concatenate multiple instances of * the requested header. */ char * hfield(char field[], struct message *mp, char *(*add)(char *, char *)) { register FILE *ibuf; char linebuf[LINESIZE]; register long lc; char *r = NOSTR; ibuf = setinput(mp); if ((lc = mp->m_lines) <= 0) return(NOSTR); if (readline(ibuf, linebuf) < 0) return(NOSTR); lc--; while ((lc = gethfield(ibuf, linebuf, lc)) >= 0) if (ishfield(linebuf, field)) r = (*add)(r, hcontents(linebuf)); return r; } /* * Return the next header field found in the given message. * Return > 0 if something found, <= 0 elsewise. * Must deal with \ continuations & other such fraud. */ int gethfield( register FILE *f, char linebuf[], register long rem) { char line2[LINESIZE]; register char *cp, *cp2; register int c; for (;;) { if (rem <= 0) return(-1); if (readline(f, linebuf) < 0) return(-1); rem--; if (strlen(linebuf) == 0) return(-1); if (isspace(linebuf[0])) continue; if (!headerp(linebuf)) return(-1); /* * I guess we got a headline. * Handle wraparounding */ for (;;) { if (rem <= 0) break; c = getc(f); ungetc(c, f); if (!isspace(c) || c == '\n') break; if (readline(f, line2) < 0) break; rem--; cp2 = line2; for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) ; if (strlen(linebuf) + strlen(cp2) >= (unsigned)LINESIZE-2) break; cp = &linebuf[strlen(linebuf)]; while (cp > linebuf && (isspace(cp[-1]) || cp[-1] == '\\')) cp--; *cp++ = ' '; for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) ; nstrcpy(cp, LINESIZE - (cp - linebuf), cp2); } if ((c = strlen(linebuf)) > 0) { cp = &linebuf[c-1]; while (cp > linebuf && isspace(*cp)) cp--; *++cp = 0; } return(rem); } /* NOTREACHED */ } /* * Check whether the passed line is a header line of * the desired breed. */ int ishfield(char linebuf[], char field[]) { register char *cp; if ((cp = strchr(linebuf, ':')) == NOSTR) return(0); if (cp == linebuf) return(0); *cp = 0; if (icequal(linebuf, field)) { *cp = ':'; return(1); } *cp = ':'; return(0); } /* * Extract the non label information from the given header field * and return it. */ char * hcontents(char hfield[]) { register char *cp; if ((cp = strchr(hfield, ':')) == NOSTR) return(NOSTR); cp++; while (*cp && isspace(*cp)) cp++; return(cp); } /* * Compare two strings, ignoring case. */ int icequal(register char *s1, register char *s2) { while (toupper(*s1++) == toupper(*s2)) if (*s2++ == 0) return(1); return(0); } /* * Copy a string, lowercasing it as we go. Here dstsize is the size of * the destination buffer dst. */ void istrcpy(char *dst, int dstsize, char *src) { register char *cp, *cp2; cp2 = dst; cp = src; while (--dstsize > 0 && *cp != '\0') *cp2++ = tolower(*cp++); *cp2 = '\0'; } /* * The following code deals with input stacking to do source * commands. All but the current file pointer are saved on * the stack. */ static int ssp = -1; /* Top of file stack */ static struct sstack { FILE *s_file; /* File we were in. */ int s_cond; /* Saved state of conditionals */ int s_loading; /* Loading .mailrc, etc. */ } *sstack; /* * Pushdown current input file and switch to a new one. * Set the global flag "sourcing" so that others will realize * that they are no longer reading from a tty (in all probability). */ int source(char name[]) { register FILE *fi; register char *cp; if ((cp = expand(name)) == NOSTR) return(1); if ((fi = fopen(cp, "r")) == NULL) { printf(gettext("Unable to open %s\n"), cp); return(1); } if (!maxfiles) { if ((maxfiles = (int)ulimit(4, 0)) < 0) #ifndef _NFILE # define _NFILE 20 #endif maxfiles = _NFILE; sstack = (struct sstack *)calloc(maxfiles, sizeof(struct sstack)); if (sstack == NULL) { printf(gettext( "Couldn't allocate memory for sourcing stack\n")); fclose(fi); return(1); } } sstack[++ssp].s_file = input; sstack[ssp].s_cond = cond; sstack[ssp].s_loading = loading; loading = 0; cond = CANY; input = fi; sourcing++; return(0); } /* * Pop the current input back to the previous level. * Update the "sourcing" flag as appropriate. */ int unstack(void) { if (ssp < 0) { printf(gettext("\"Source\" stack over-pop.\n")); sourcing = 0; return(1); } fclose(input); if (cond != CANY) printf(gettext("Unmatched \"if\"\n")); cond = sstack[ssp].s_cond; loading = sstack[ssp].s_loading; input = sstack[ssp--].s_file; if (ssp < 0) sourcing = loading; return(0); } /* * Touch the indicated file. * This is nifty for the shell. * If we have the utime() system call, this is better served * by using that, since it will work for empty files. * On non-utime systems, we must sleep a second, then read. */ void alter(char name[]) { int rc = utime(name, utimep); extern int errno; if (rc != 0) { fprintf(stderr, gettext("Cannot utime %s in aux:alter\n"), name); fprintf(stderr, gettext("Errno: %d\n"), errno); } } /* * Examine the passed line buffer and * return true if it is all blanks and tabs. */ int blankline(const char linebuf[]) { register const char *cp; for (cp = linebuf; *cp; cp++) if (!any(*cp, " \t")) return(0); return(1); } /* * Skin an arpa net address according to the RFC 822 interpretation * of "host-phrase." */ static char * phrase(char *name, int token, int comma) { register char c; register char *cp, *cp2; char *bufend, *nbufp; int gotlt, lastsp, didq; char nbuf[LINESIZE]; int nesting; if (name == NOSTR) return(NOSTR); if (strlen(name) >= (unsigned)LINESIZE) nbufp = (char *)salloc(strlen(name)); else nbufp = nbuf; gotlt = 0; lastsp = 0; bufend = nbufp; for (cp = name, cp2 = bufend; (c = *cp++) != 0;) { switch (c) { case '(': /* Start of a comment, ignore it. */ nesting = 1; while ((c = *cp) != 0) { cp++; switch(c) { case '\\': if (*cp == 0) goto outcm; cp++; break; case '(': nesting++; break; case ')': --nesting; break; } if (nesting <= 0) break; } outcm: lastsp = 0; break; case '"': /* Start a quoted string. Copy it in its entirety. */ didq = 0; while ((c = *cp) != 0) { cp++; switch (c) { case '\\': if ((c = *cp) == 0) goto outqs; cp++; break; case '"': goto outqs; } if (gotlt == 0 || gotlt == '<') { if (lastsp) { lastsp = 0; *cp2++ = ' '; } if (!didq) { *cp2++ = '"'; didq++; } *cp2++ = c; } } outqs: if (didq) *cp2++ = '"'; lastsp = 0; break; case ' ': case '\t': case '\n': if (token && (!comma || c == '\n')) { done: cp[-1] = 0; return cp; } lastsp = 1; break; case ',': *cp2++ = c; if (gotlt != '<') { if (token) goto done; bufend = cp2; gotlt = 0; } break; case '<': cp2 = bufend; gotlt = c; lastsp = 0; break; case '>': if (gotlt == '<') { gotlt = c; break; } /* FALLTHROUGH . . . */ default: if (gotlt == 0 || gotlt == '<') { if (lastsp) { lastsp = 0; *cp2++ = ' '; } *cp2++ = c; } break; } } *cp2 = 0; return (token ? --cp : equal(name, nbufp) ? name : nbufp == nbuf ? savestr(nbuf) : nbufp); } char * skin(char *name) { return phrase(name, 0, 0); } /* * Here sz is the buffer size of word. */ char * yankword(char *name, char *word, int sz, int comma) { char *cp; if (name == 0) return 0; while (isspace(*name)) name++; if (*name == 0) return 0; cp = phrase(name, 1, comma); nstrcpy(word, sz, name); return cp; } int docomma(char *s) { return s && strpbrk(s, "(<,"); } /* * Fetch the sender's name from the passed message. */ char * nameof(register struct message *mp) { char namebuf[LINESIZE]; char linebuf[LINESIZE]; register char *cp, *cp2; register FILE *ibuf; int first = 1, wint = 0; char *tmp; if (value("from") && (cp = hfield("from", mp, addto)) != NOSTR) return ripoff(cp); ibuf = setinput(mp); copy("", namebuf); if (readline(ibuf, linebuf) <= 0) return(savestr(namebuf)); newname: for (cp = linebuf; *cp != ' '; cp++) ; while (any(*cp, " \t")) cp++; for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") && cp2-namebuf < LINESIZE-1; *cp2++ = *cp++) ; *cp2 = '\0'; for (;;) { if (readline(ibuf, linebuf) <= 0) break; if (substr(linebuf,"forwarded by ") != -1) continue; if (linebuf[0] == 'F') cp = linebuf; else if (linebuf[0] == '>') cp = linebuf + 1; else break; if (strncmp(cp, "From ", 5) != 0) break; if ((wint = substr(cp, "remote from ")) != -1) { cp += wint + 12; if (first) { copy(cp, namebuf); first = 0; } else { tmp = strrchr(namebuf, '!') + 1; nstrcpy(tmp, sizeof (namebuf) - (tmp - namebuf), cp); } nstrcat(namebuf, sizeof (namebuf), "!"); goto newname; } else break; } for (cp = namebuf; *cp == '!'; cp++); while (ishost(host, cp)) cp = strchr(cp, '!') + 1; if (value("mustbang") && !strchr(cp, '!')) { snprintf(linebuf, sizeof (linebuf), "%s!%s", host, cp); cp = linebuf; } if (cp2 = hfield("from", mp, addto)) return(splice(cp, cp2)); else return(savestr(cp)); } /* * Splice an address into a commented recipient header. */ char * splice(char *addr, char *hdr) { char buf[LINESIZE]; char *cp, *cp2; if (cp = strchr(hdr, '<')) { cp2 = strchr(cp, '>'); if (cp2 == NULL) { nstrcpy(buf, sizeof (buf), addr); } else { snprintf(buf, sizeof (buf), "%.*s%s%s", cp - hdr + 1, hdr, addr, cp2); } } else if (cp = strchr(hdr, '(')) { snprintf(buf, sizeof (buf), "%s %s", addr, cp); } else nstrcpy(buf, sizeof (buf), addr); return savestr(ripoff(buf)); } static char * ripoff(register char *buf) { register char *cp; cp = buf + strlen(buf); while (--cp >= buf && isspace(*cp)); if (cp >= buf && *cp == ',') cp--; *++cp = 0; return buf; } /* * Are any of the characters in the two strings the same? */ int anyof(register char *s1, register char *s2) { register int c; while ((c = *s1++) != 0) if (any(c, s2)) return(1); return(0); } /* * See if the given header field is supposed to be ignored. * Fields of the form "Content-*" can't be ignored when saving. */ int isign(char *field, int saving) { char realfld[BUFSIZ]; /* * Lower-case the string, so that "Status" and "status" * will hash to the same place. */ istrcpy(realfld, sizeof (realfld), field); if (saving && strncmp(realfld, "content-", 8) == 0) return (0); if (nretained > 0) return (!member(realfld, retain)); else return (member(realfld, ignore)); } int member(register char *realfield, register struct ignore **table) { register struct ignore *igp; for (igp = table[hash(realfield)]; igp != 0; igp = igp->i_link) if (equal(igp->i_field, realfield)) return (1); return (0); } /* * This routine looks for string2 in string1. * If found, it returns the position string2 is found at, * otherwise it returns a -1. */ int substr(char *string1, char *string2) { int i, j, len1, len2; len1 = strlen(string1); len2 = strlen(string2); for (i = 0; i < len1 - len2 + 1; i++) { for (j = 0; j < len2 && string1[i+j] == string2[j]; j++) ; if (j == len2) return(i); } return(-1); } /* * Copies src to the dstsize buffer at dst. The copy will never * overflow the destination buffer and the buffer will always be null * terminated. */ char * nstrcpy(char *dst, int dstsize, char *src) { char *cp, *cp2; cp2 = dst; cp = src; while (--dstsize > 0 && *cp != '\0') *cp2++ = *cp++; *cp2 = '\0'; return(dst); } /* * Appends src to the dstsize buffer at dst. The append will never * overflow the destination buffer and the buffer will always be null * terminated. */ char * nstrcat(char *dst, int dstsize, char *src) { char *cp, *cp2; cp2 = dst; cp = src; while (*cp2 != '\0') { cp2++; dstsize--; } while (--dstsize > 0 && *cp != '\0') *cp2++ = *cp++; *cp2 = '\0'; return(dst); }