#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents:  mail2news.c news2mail.c rfc822.c sysexits.h
# Wrapped by rsalz@nntp.com on Tue Jan 30 22:17:05 1996
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo '          "shar: End of archive 2 (of 4)."'
if test -f 'mail2news.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mail2news.c'\"
else
  echo shar: Extracting \"'mail2news.c'\" \(16107 characters\)
  sed "s/^X//" >'mail2news.c' <<'END_OF_FILE'
X/*
X**  MAIL2NEWS
X**  Gateway mail messages into netnews.  Usage:
X**	mail2news [inews flags] -o Organization
X**  In order to do this, there are a number of interesting transformations
X**  that need to be made on the headers...
X**
X**  This program is descended from:  @(#)recnews.c 2.10 4/16/85.
X*/
X#include "gate.h"
X#include <signal.h>
X#include <sys/file.h>
X#if	defined(RCSID)
Xstatic char RCS[] =
X	"$Header: /nfs/papaya/u2/rsalz/src/newsgate/RCS/mail2news.c,v 1.19 91/07/18 18:41:45 rsalz Exp Locker: rsalz $";
X#endif	/* defined(RCSID) */
X
X
X#define	WAIT_CORED(s)		(s & 0200)
X#define	WAIT_EXITSIG(s)		(s & 0177)
X#define	WAIT_EXITCODE(s)	((s >> 8) & 0377)
X
X/* For those who don't have this in <sys/file.h>. */
X#if	!defined(R_OK)
X#define	R_OK			4
X#endif	/* !defined(R_OK) */
X
X/* Stuff for pipe(2). */
X#define STDIN			0
X#define	PIPE_READER		0
X#define	PIPE_WRITER		1
X
X
X/* Global variables. */
XSTATIC int	Debugging;
XSTATIC int	ChildPid = -1;
Xchar		*Pname;
X
X
X
X/*
X**  Go down to the absolute minimum.
X*/
XSTATIC void
XTrimEnvironment()
X{
X    static char		*Empty[] = { NULL };
X
X    environ = Empty;
X}
X
X
X/*
X**  Quickie hack to see of a mail message is a "please drop me" request.
X**  Reads the message on the input file, and returns NULL if it should
X**  be ignored, or a FILE handle if we should process it.
X**
X**  The original stand-alone program written was Russ Nelson,
X**  <nelson@clutx.clarkson.edu>.  I hacked on it, and made it into a
X**  subroutine.  Perhaps a better way to test is to make the test less
X**  conservative, and see what "real" articles get caught, and make
X**  adjustments then?  Comments solicited.
X*/
XSTATIC FILE *
XIsSubRequest(F)
X    register FILE	*F;
X{
X    register FILE	*Out;
X    register char	*p;
X    register int	c;
X    register int	drop_or_add;
X    register int	from_or_to;
X    register int	mail_word;
X    register int	count;
X    char		word[SM_SIZE];
X    char		buff[SM_SIZE];
X
X    /* Create temp file; if we can't, let the message through. */
X    Strcpy(buff, TEMPFILE);
X    (void)mktemp(buff);
X    if ((Out = fopen(buff, "w")) == NULL)
X	return F;
X
X    /* Clear counts. */
X    drop_or_add = 0;
X    from_or_to = 0;
X    mail_word = 0;
X    count = 0;
X
X    /* Read input a word at a time. */
X    for (p = word; (c = getc(F)) != EOF; ) {
X	(void)putc(c, Out);
X	if (!isalpha(c)) {
X	    *p = '\0';
X	    if (p > word)
X		count++;
X	    p = word;
X
X	    if (EQ(word, "remove") || EQ(word, "drop") || EQ(word, "off")
X	     || EQ(word, "subscribe") || EQ(word, "get") || EQ(word, "add"))
X		drop_or_add++;
X	    else if (EQ(word, "from") || EQ(word, "to"))
X		from_or_to++;
X	    else if (EQ(word, "mail") || EQ(word, "mailing")
X		  || EQ(word, "list") || EQ(word, "dl"))
X		mail_word++;
X	}
X	else if (p < &word[sizeof word - 1])
X	    *p++ = isupper(c) ? tolower(c) : c;
X    }
X
X    (void)fclose(F);
X    (void)fclose(Out);
X
X    /* Use fancy-shmancy AI techniques to determine what the message is. */
X    c = count < 25 && drop_or_add && from_or_to && mail_word;
X    F = c ? NULL : fopen(buff, "r");
X
X    (void)unlink(buff);
X    return F;
X}
X
X
X/*
X**  Modify the Newsgroups: as directed by the command string.
X*/
XSTATIC void
XDoCommand(hp, command, group)
X    register HBUF		*hp;
X    char			*command;
X    char			*group;
X{
X    register char		*p;
X    register int		i;
X    register int		n;
X    register int		nng;
X    char			**tokens;
X    char			**ng;
X    char			buff[BUFSIZ];
X
X    if ((n = Split(command, &tokens, '\0')) == 0) {
X	SplitFree(&tokens);
X	return;
X    }
X
X    nng = Split(hp->nbuf, &ng, NGDELIM);
X    p = hp->nbuf;
X    switch (tokens[0][0]) {
X    case 'a':				/* Add		*/
X	if (n > 1)
X	    for (p += strlen(p), i = 1; i < n; i++) {
X		*p++ = NGDELIM;
X		p += APPEND(p, tokens[i]);
X	    }
X	break;
X    case 'd':				/* Delete	*/
X	for (i = 0; i < nng; i++)
X	    if (!EQ(ng[i], group)) {
X		if (p > hp->nbuf)
X		    *p++ = NGDELIM;
X		p += APPEND(p, ng[i]);
X	    }
X	if (p == hp->nbuf)
X	    Strcpy(hp->nbuf, "junk");
X	break;
X    case 'k':				/* Kill		*/
X	Fprintf(stderr, "%s:  Your posting to %s was killed by %s.\n",
X	    Pname, hp->nbuf, n > 1 ? tokens[1] : group);
X	exit(EX_NOPERM);
X	/* NOTREACHED */
X    case 'm':				/* Move		*/
X	if (n > 1)
X	    if (nng == 1)
X		Strcpy(hp->nbuf, tokens[1]);
X	    else
X		for (i = 0; i < nng; i++) {
X		    if (p > hp->nbuf)
X			*p++ = NGDELIM;
X		    p += APPEND(p, EQ(ng[i], group) ? tokens[1] : ng[i]);
X		}
X	break;
X    case 'q':				/* Quiet kill	*/
X	if (Debugging) {
X	    (void)printf("Quiet kill (ignored for debugging).\n");
X	    break;
X	}
X	/* Eat the message up, and pretend we delivered it. */
X	while (fgets(buff, sizeof buff, stdin))
X	    continue;
X	exit(EX_OK);
X	/* NOTREACHED */
X    }
X
X    SplitFree(&tokens);
X    SplitFree(&ng);
X}
X
X
X/*
X**  For Ozan Yigit's public domain regex.
X*/
X/* ARGSUSED */
Xvoid
Xre_fail(text, arg)
X    char	*text;
X    int		arg;
X{
X}
X
X
X/*
X**  Split a line that looks like XpatternXcommandX into the pattern and
X**  the command.  Initialize the RE matcher with the pattern, and return
X**  the command.
X*/
XSTATIC char *
XParsePattern(p, lineno)
X    register char	*p;
X    int			lineno;
X{
X    register char	*cp;
X    register char	*command;
X    register char	delim;
X    char		*RE;
X
X    /* Ignore comments and blank lines. */
X    if (*p == '#' || *p == '\0')
X	return NULL;
X
X    for (delim = *p++, RE = cp = p, command = NULL; *cp; *p++ = *cp++)
X	if (*cp == '\\' && cp[1] == delim)
X	    cp++;
X	else if (*cp == delim) {
X	    /* Found delimiter; mark command, terminate RE. */
X	    command = ++cp;
X	    *p = '\0';
X	    break;
X	}
X
X    if (command == NULL || *command == '\0')
X	Fprintf(stderr, "%s:  Incomplete regular expression, line %d.\n",
X	    Pname, lineno);
X    else if ((cp = re_comp(RE)) != NULL)
X	Fprintf(stderr, "%s:  Bad regular expression, line %d: %s.\n",
X	    Pname, lineno, cp);
X    else
X	return command;
X
X#if	defined(lint)
X    /* My, my, aren't we anal. */
X    (void)re_subs("", "");
X    re_modw("");
X#endif	/* defined(lint) */
X
X    return NULL;
X}
X
X
X/*
X**  Convert string to lower case, return a new copy.
X*/
XSTATIC char *
XMakeLowerCopy(s)
X    register char	*s;
X{
X    register char	*p;
X
X    for (p = s = COPY(s); *p; p++)
X	if (isupper(*p))
X	    *p = tolower(*p);
X    return s;
X}
X
X
X/*
X**  Free up something that ReadFile made.
X*/
Xvoid
XFreeFile(V)
X    char		**V;
X{
X    register char	**p;
X
X    if ((p = V) != NULL) {
X	while (*p)
X	    free(*p++);
X	free((char *)V);
X    }
X}
X
X
X/*
X**  Change newsgroups if the Subject:, Keywords:, or Summary: match a
X**  pattern found in the newsgroup remap file.
X*/
XSTATIC void
XEditnewsgroups(hp)
X    register HBUF		*hp;
X{
X    register char		*p;
X    register int		n;
X    register int		i;
X    register int		j;
X    register int		t;
X    char			**groups;
X    char			**mapline;
X    char			*hdrline[4];
X    char			buff[LG_SIZE];
X
X    /* Copy some headers, but if nothing's there, give up. */
X    i = 0;
X    if (hp->title[0])
X	hdrline[i++] = MakeLowerCopy(hp->title);
X    if (hp->keywords[0])
X	hdrline[i++] = MakeLowerCopy(hp->keywords);
X    if (hp->summary[0])
X	hdrline[i++] = MakeLowerCopy(hp->summary);
X    if (i == 0)
X	return;
X    hdrline[i] = NULL;
X
X    /* For all the newsgroups, see if there's a mapping file. */
X    for (n = Split(hp->nbuf, &groups, NGDELIM), i = 0; i < n; i++) {
X	if (groups[i] == NULL || groups[i][0] == '\0')
X	    continue;
X
X	/* Gate the name of the mapping file. */
X#if	defined(IN_ONEPLACE)
X	Strcpy(buff, IN_ONEPLACE);
X#endif	/* defined(IN_ONEPLACE) */
X#if	defined(IN_SPOOLDIR)
X	{
X	    register char	*q;
X
X	    for (p = buff + APPEND(buff, IN_SPOOLDIR), q = groups[i]; *q; q++)
X		*p++ = *q == '.' ? '/' : *q;
X	    Strcpy(p, "/recnews.cmd");
X	}
X#endif	/* defined(IN_SPOOLDIR) */
X#if	defined(IN_CMDDIR)
X	Sprintf(buff, "%s/%s", IN_CMDDIR, groups[i]);
X#endif	/* defined(IN_CMDDIR) */
X
X	if (access(buff, R_OK) >= 0 && (mapline = ReadFile(buff))) {
X	    /* For all lines in the file, if there's a command and the
X	     * pattern matches, execute the command. */
X	    for (j = 0; mapline[j]; j++)
X		if ((p = ParsePattern(mapline[j], j)) != NULL)
X		    for (t = 0; hdrline[t]; t++)
X			if (re_exec(hdrline[t]) == 1) {
X			    DoCommand(hp, p, groups[i]);
X			    break;
X			}
X	    FreeFile(mapline);
X	}
X    }
X
X    /* Free dynamic space. */
X    for (i = 0; hdrline[i]; i++)
X	free(hdrline[i]);
X    SplitFree(&groups);
X}
X
X
X/*
X**  Signal-catcher and child-reapers.
X*/
X
X
X/*
X**  Exit such that sendmail will again later.
X*/
XSTATIC CATCHER
XSig_tempfail()
X{
X    exit(EX_TEMPFAIL);
X}
X
X
X/*
X**  Reap the inews child properly, and exit with his exit code, so that
X**  ultimate success or failure rests with inews.
X*/
XSTATIC CATCHER
Xchildgone()
X{
X    register int	pid;
X    int			W;
X
X    /* Some systems get race conditions, causing this routine to be
X     * invoke multiple times, sigh.  Some systems want SIG_IGN, I think. */
X#if	defined(SIGCHLD)
X    Signal(SIGCHLD, SIG_DFL);
X#endif	/* defined(SIGCHLD) */
X#if	defined(SIGCLD)
X    Signal(SIGCLD, SIG_DFL);
X#endif	/* defined(SIGCLD) */
X
X    if ((pid = wait(&W)) != ChildPid || pid == -1)
X	exit(EX_OSERR);
X    
X    /* Was it a good death? */
X    if (WAIT_EXITSIG(W)) {
X	Fprintf(stderr, "%s:  Child %d killed by signal %d.\n",
X	    Pname, pid, WAIT_EXITSIG(W));
X	if (WAIT_CORED(W))
X	    Fprintf(stderr, "%s:  Child %d dumped core.\n", Pname, pid);
X	exit(EX_SOFTWARE);
X    }
X
X#if	defined(MMDF)
X    /* We need a way to tell temporary errors from permanent ones.  Inews
X     * will reject messages because of too much quoting, for example,
X     * so the message will sit in the queue forever.  Until then we'll
X     * have to lose messages on any error. */
X    exit(0);
X#else
X    exit(WAIT_EXITCODE(W));
X#endif	/* defined(MMDF) */
X}
X
X
X
X/*
X**  Convert the characters following dots to upper case, if they're
X**  lower case.  Two dots in a row will leave one dot in their place.
X**  Modifies the argument.
X*/
XSTATIC char *
XHackPeriods(string)
X    char		*string;
X{
X    register char	*s;
X    register char	*p;
X
X    if (string) {
X	for (p = s = string; *p; *s++ = *p++)
X	    if (*p == '.') {
X		if (*++p == '\0') {
X		    *s++ = '.';
X		    break;
X		}
X		if (islower(*p))
X		    *p = toupper(*p);
X	    }
X	*s = '\0';
X    }
X    return string;
X}
X
X
X
Xint
Xmain(ac, av)
X    register int	ac;
X    register char	*av[];
X{
X    register STRING	*vec;
X    register char	*p;
X    register FILE	*F;
X    register FILE	*Infile;
X    HBUF		H;
X    STRING		*iv;
X    char		buff[BUFSIZ];
X#if	defined(SENDMAIL)
X    char		sendbuff[sizeof buff];
X#endif	/* defined(SENDMAIL) */
X    int			fd[2];
X    int			FlushSubRequests;
X    int			SubjectRequired;
X    int			GotEquals;
X    STRING		error;
X
X    if ((Pname = RDX(av[0], '/')) == NULL)
X	Pname = av[0];
X    else
X	Pname++;
X    Infile = stdin;
X
X    /* Remove any trace of who we are. */
X    TrimEnvironment();
X
X    /* So that cores will actually drop... */
X    if (chdir("/tmp") < 0) {
X	Fprintf(stderr, "%s:  Can't chdir(/tmp), %s.\n", Pname,
X	    strerror(errno));
X	exit(EX_TEMPFAIL);
X    }
X
X    /* If someone wants to shut down the system, tell sendmail to
X     * try again later. */
X    Signal(SIGTERM, Sig_tempfail);
X
X#if	defined(SENDMAIL)
X    /* First read should fetch us the UNIX From_ line.  Not done in MMDF. */
X    if (fgets(buff, sizeof buff, Infile) == NULL)
X	exit(EX_NOINPUT);
X
X#if	defined(REQUIRE_UNIX_FROM)
X    if (!EQn(buff, "From ", 5)) {
X	Fprintf(stderr, "%s:  Input didn't start with UNIX From line:\n",
X	    Pname);
X	Fprintf(stderr,"\t%s.\n", buff);
X	exit(EX_DATAERR);
X    }
X#endif	/* defined(REQUIRE_UNIX_FROM) */
X
X    /* Turn "From foo ... remote from bar ..." into "Sender: bar!foo". */
X    if (EQn(buff, "From ", 5)) {
X	Strcpy(sendbuff, "Sender: ");
X	for (p = buff + 4; (p = IDX(p + 1, 'r')) != NULL; )
X	    if (strncmp(p, "remote from ", 12) == 0
X	     && sscanf(p, "remote from %s", sendbuff + 8) == 1) {
X		Strcat(sendbuff, "!");
X		break;
X	    }
X	if (sscanf(buff, "From %s", sendbuff + strlen(sendbuff)) == 1)
X	    Strcpy(buff, sendbuff);
X    }
X#endif	/* defined(SENDMAIL) */
X
X    /* Read the mail header. */
X    rfc822read(&H, Infile, buff, sizeof buff);
X
X    /* Process the argument list, copying anything that we don't recognize
X     * over to the inews argument list and changing things as we see fit. */
X    FlushSubRequests = FALSE;
X    GotEquals = FALSE;
X    SubjectRequired = TRUE;
X    iv = NEW(STRING, ac + 2);
X    iv[0] = INEWS;
X#if	!defined(NO_H_FLAG)
X    iv[1] = "-h";
X#endif	/* defined(NO_H_FLAG) */
X    for (vec = iv + 2; (p = *++av) != NULL; )
X	if (p[0] != '-')
X	    *vec++ = p;
X	 else
X	    switch(p[1]) {
X	    case 'x':
X		SubjectRequired = FALSE;
X		/* FALLTHROUGH */
X	    default:
X		*vec++ = p;
X		break;
X	    case '=':
X#if	!defined(NO_H_FLAG)
X		/* Move past the old iv[0], and set the new iv[0] to be
X		 * the program to run. */
X		if (!GotEquals)
X		    iv++;
X#endif	/* defined(NO_H_FLAG) */
X		iv[0] = p[2] ? &p[2] : *++av;
X		GotEquals++;
X		break;
X	    case '.':
X		Debugging++;
X		break;
X	    case 'n':
X		/* Newsgroup this messages goes to. */
X		Strcpy(H.nbuf, p[2] ? &p[2] : *++av);
X		break;
X	    case 'o':
X		/* Default organization. */
X		p = p[2] ? &p[2] : *++av;
X		if (H.organization[0] == '\0')
X		    Strcpy(H.organization, HackPeriods(p));
X		SubjectRequired = FALSE;
X		break;
X	    case 'd':
X		/* Default distribution. */
X		p = p[2] ? &p[2] : *++av;
X		if (H.distribution[0] == '\0')
X		    Strcpy(H.distribution, p);
X		break;
X	    case 'a':
X		/* Default approval. */
X		p = p[2] ? &p[2] : *++av;
X		if (H.approved[0] == '\0')
X		    Strcpy(H.approved, p);
X		break;
X	    case 'F':
X		FlushSubRequests = TRUE;
X		break;
X	    }
X    *vec++ = NULL;
X
X    /* Bash on the mail header. */
X    if ((error = HackHeader(&H, SubjectRequired)) != NULL) {
X	Fprintf(stderr, "%s:  Rejected by netnews because:\n", Pname);
X	Fprintf(stderr, "\t%s.\n", error);
X	if (H.nbuf[0])
X	    Fprintf(stderr, "\tIt was going into the newsgroup%s %s.\n",
X		IDX(H.nbuf, NGDELIM) ? "s" : "", H.nbuf);
X	exit(EX_DATAERR);
X    }
X    Editnewsgroups(&H);
X
X    if (Debugging) {
X	for (vec = iv; *vec; vec++)
X	    (void)printf(" |%s| ", *vec);
X	(void)printf("\n");
X	if (!rfc822write(&H, stdout))
X	    Fprintf(stderr, "%s:  Can't write header, %s.\n",
X		Pname, strerror(errno));
X	while (fgets(buff, sizeof buff, Infile))
X	    Fputs(buff, stdout);
X	exit(EX_OK);
X    }
X
X    if (FlushSubRequests && (Infile = IsSubRequest(Infile)) == NULL) {
X	Fprintf(stderr, "%s:  Rejected by netnews becase:\n", Pname);
X	Fprintf(stderr, "\tIt seems like a subscription request.\n");
X	exit(EX_DATAERR);
X    }
X
X    /* Get ready to spawn an inews. */
X    if (pipe(fd) < 0) {
X	Fprintf(stderr, "%s:  Can't pipe, %s.\n", Pname, strerror(errno));
X	exit(EX_TEMPFAIL);
X    }
X    Fflush(stderr);
X    Fflush(stdout);
X#if	defined(SIGCHLD)
X    Signal(SIGCHLD, childgone);
X#endif	/* defined(SIGCHLD) */
X#if	defined(SIGCLD)
X    Signal(SIGCLD, childgone);
X#endif	/* defined(SIGCLD) */
X
X    if ((ChildPid = fork()) < 0) {
X	Fprintf(stderr,"%s:  Can't fork, %s.\n", Pname, strerror(errno));
X	exit(EX_TEMPFAIL);
X    }
X    if (ChildPid == 0) {
X	/* Redirect I/O; it's unlikely the test below will fail. */
X	if (fd[PIPE_READER] != STDIN) {
X	    Close(STDIN);
X	    if (dup(fd[PIPE_READER]) != STDIN)
X		Fprintf(stderr, "%s:  Can't redirect input, %s.\n",
X		    Pname, strerror(errno));
X	}
X	Close(fd[PIPE_READER]);
X	Close(fd[PIPE_WRITER]);
X	(void)execv(iv[0], iv);
X	Fprintf(stderr, "%s:  Can't exec %s, %s.\n",
X	    Pname, iv[0], strerror(errno));
X	exit(EX_OSERR);
X    }
X
X    /* Set things up after the fork. */
X    Close(fd[PIPE_READER]);
X    Signal(SIGPIPE, childgone);
X    if ((F = fdopen(fd[PIPE_WRITER], "w")) == NULL)
X	exit(EX_OSERR);
X
X    /* Stuff the header. */
X    if (!rfc822write(&H, F)) {
X	Fprintf(stderr, "%s:  Can't write header, %s.\n",
X	    Pname, strerror(errno));
X	exit(EX_IOERR);
X    }
X
X    /* Write the rest of the message. */
X    while (fgets(buff, sizeof buff, Infile)) {
X	Fputs(buff, F);
X	if (ferror(F))
X	    break;
X    }
X
X    /* Close down the pipe. */
X    Fflush(F);
X    if (ferror(F)) {
X	Fprintf(stderr, "%s:  Error flushing pipe to news, %s.\n",
X	    Pname, strerror(errno));
X	exit(EX_IOERR);
X    }
X    if (fclose(F) == EOF)
X	Fprintf(stderr, "%s:  Error closing pipe to news, %s.\n",
X	    Pname, strerror(errno));
X
X    /* Wait for inews, and exit as it does. */
X    childgone();
X    return 0;
X}
END_OF_FILE
  if test 16107 -ne `wc -c <'mail2news.c'`; then
    echo shar: \"'mail2news.c'\" unpacked with wrong size!
  fi
  # end of 'mail2news.c'
fi
if test -f 'news2mail.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'news2mail.c'\"
else
  echo shar: Extracting \"'news2mail.c'\" \(13777 characters\)
  sed "s/^X//" >'news2mail.c' <<'END_OF_FILE'
X/*
X**  NEWS2MAIL
X**  Read a news article on standard input, and send it to the mailing
X**  list as directed by the command-line arguments.  It does some
X**  parsing and converting of news headers into mail headers.
X**
X**  This program wants to lie to sendmail, so it should be setuid to
X**  one of the "trusted" users as listed in your sendmail.cf file.
X**
X*/
X#include "gate.h"
X#include <sys/stat.h>
X#if	defined(RCSID)
Xstatic char RCS[] =
X	"$Header: /nfs/papaya/u2/rsalz/src/newsgate/RCS/news2mail.c,v 1.14 91/07/18 21:14:26 rsalz Exp Locker: rsalz $";
X#endif	/* defined(RCSID) */
X
X
X/* Flags for special header lines. */
Xtypedef enum _HEADERTYPE {
X    HDR_NORM,
X    HDR_SUBJ,
X    HDR_CTRL,
X    HDR_REFS,
X    HDR_PATH,
X    HDR_FROM
X} HEADERTYPE;
X
X
X/* Header-cracking datatype. */
Xtypedef struct _HEADER {
X    STRING		Tag;
X    int			Length;
X    HEADERTYPE		Flag;
X    char		Value[SM_SIZE];
X} HEADER;
X
XSTATIC int	Debugging;
Xchar		*Pname;
X
X/* The headers we care about. */
XSTATIC HEADER	Table[] = {
X    {	"Control",	 7,	 HDR_CTRL	},
X    {	"Date",		 4,	 HDR_NORM	},
X    {	"From",		 4,	 HDR_FROM	},
X    {	"Message-ID",	10,	 HDR_NORM	},
X    {	"Organization",	12,	 HDR_NORM	},
X    {	"MIME-Version",	12,	HDR_NORM	},
X    {	"Content-Type",	12,	HDR_NORM	},
X    {	"Content-Transfer-Encoding", 25, HDR_NORM },
X    {	"Path",		 4,	 HDR_PATH	},
X    {	"References",	10,	 HDR_REFS	},
X    {	"Reply-To",	 8,	 HDR_NORM	},
X    {	"Subject",	 7,	 HDR_SUBJ	},
X};
X
X
X#if	defined(USE_PATH_FOR_FROM)
X/*
X**  Figure out the return address, by doing some optimization on the
X**  path.  If we find an Internet host or a UUCP neighbor, remove all
X**  hosts before that one from the path.
X*/
XSTATIC char *
XEditPath(path, Host)
X    register char	*path;
X    char		*Host;
X{
X    static char		**Hinet;
X    static char		**Huucp;
X    static char		**Lsys;
X    static char		buff[SM_SIZE];
X    register char	*p;
X    register char	*q;
X    register char	**V;
X    register int	ac;
X    register int	i;
X    register int	uucppal;
X    register int	inetpal;
X    FILE		*F;
X    FILE		*P;
X    struct stat		Sb1;
X    struct stat		Sb2;
X    char		**av;
X    char		*Inetsite;
X
X    if ((ac = Split(path, &av, '!')) == 2) {
X	/* Simple case:  "site!user" */
X	Strcpy(buff, av[1]);
X	SplitFree(&av);
X	return buff;
X    }
X
X    /* Initialize.  This is silly for now, but eventually we might want
X     * to be able to handle a batched feed. */
X#if	defined(UUNAME)
X    if (Lsys == NULL) {
X	p = UUNAME;
X	/* If someone diddled L.sys, rebuild uuname output. */
X	if (stat(p, &Sb2) < 0
X#if	defined(L_SYS)
X	 || stat(L_SYS, &Sb1) < 0
X#endif	/* defined(L_SYS) */
X	 || Sb1.st_mtime >= Sb2.st_mtime)
X	    if ((F = fopen(p, "w"))  == NULL)
X		Fprintf(stderr, "%s:  Can't create %s, %s.\n",
X			Pname, p, strerror(errno));
X	    else {
X		if ((P = popen("exec uuname", "r")) == NULL)
X		    Fprintf(stderr, "%s:  popen failed, %s.\n",
X			    Pname, strerror(errno));
X		else {
X		    while (fgets(buff, sizeof buff, P))
X			Fputs(buff, F);
X		    if (pclose(P))
X			Fprintf(stderr, "%s:  pclose failed, %s.\n",
X				Pname, strerror(errno));
X		}
X		if (fclose(F) == EOF)
X		    Fprintf(stderr, "%s:  Error closing %s, %s.\n",
X			    Pname, p, strerror(errno));
X	    }
X
X	/* Slurp up names of UUCP hosts we talk to. */
X	Lsys = ReadFile(p);
X
X#if	defined(UUCP_INET)
X	/* Slurp up the UUCP->Internet name mappings. */
X	for (Huucp = ReadFile(UUCP_INET), i = 1; Huucp[i]; i++)
X	    continue;
X	for (Hinet = NEW(char*, i), i = 0; (p = Huucp[i]) != NULL; i++) {
X	    while (*p && !WHITE(*p))
X		p++;
X	    if (*p)
X		for (*p++ = '\0'; *p && WHITE(*p); p++)
X		    continue;
X	    Hinet[i] = p;
X	}
X#endif	/* defined(UUCP_INET) */
X    }
X#endif	/* defined(UUNAME) */
X
X    /* Scan the path, noting if we find a UUCP or Internet neighbor. */
X    for (uucppal = 1, inetpal = 0, i = 0; i < ac; i++) {
X	if (Lsys)
X	    for (V = Lsys; *V; V++)
X		if (EQ(av[i], *V))
X		    uucppal = i;
X	if (Huucp)
X	    for (V = Huucp; *V; V++)
X		if (EQ(av[i], *V)) {
X		    inetpal = i;
X		    Inetsite = Hinet[V - Huucp];
X		}
X    }
X
X    if (inetpal < uucppal) {
X	/* No Internet site found, turn a!b!c into a!b!c@this-host */
X	for (p = buff + APPEND(buff, av[uucppal]); ++uucppal < ac; ) {
X	    *p++ = '!';
X	    p += APPEND(p, av[uucppal]);
X	}
X	*p++ = '@';
X	Strcpy(p, Host);
X    }
X    else if (inetpal == ac - 1)
X	/* Turn a!b!inet!user into user@inet.domain.name */
X	Sprintf(buff, "%s@%s", av[ac], Inetsite);
X    else {
X	/* Turn a!inet!b!user into b!user@inet.domain.name */
X	for (p = buff + APPEND(buff, av[++inetpal]); ++inetpal < ac; ) {
X	    *p++ = '!';
X	    p += APPEND(p, av[inetpal]);
X	}
X	*p++ = '@';
X	Strcpy(p, Inetsite);
X    }
X
X    /* Convert all but the last "@" to "%" (fie on decwrl and psuecl!). */
X    if ((p = IDX(buff, '@')) != NULL)
X	for ( ; (q = IDX(p + 1, '@')) != NULL; p = q)
X	    *p = '%';
X
X    SplitFree(&av);
X    return buff;
X}
X#endif	/* defined(USE_PATH_FOR_FROM) */
X
X
X/*
X**  Hack up the references, taking only the last three.
X*/
XSTATIC char *
XTrimReferences(refs)
X    char		 *refs;
X{
X    static char		buff[SM_SIZE];
X    register char	*p;
X    register int	i;
X    register int	ac;
X    char		**av;
X
X    if ((ac = Split(refs, &av, '\0')) != 0) {
X	/* Tricky.  If there are five references, we want subscripts 2,3,4. */
X	i = ac < 3 ? 0 : ac - 3;
X	for (p = buff + APPEND(buff, av[i]); ++i < ac; ) {
X	    *p++ = ',';
X	    *p++ = ' ';
X	    p += APPEND(p, av[i]);
X	}
X	SplitFree(&av);
X    }
X    else
X	buff[0] = '\0';
X    return buff;
X}
X
X
X#if	!defined(HAVE_PUTENV)
X/*
X**  A brute-forced implementation of putenv.  Wastes memory.  Consider
X**  it incentive to install the free BSD version...
X*/
Xint
Xputenv(val)
X    char	*val;
X{
X    char	**new;
X    int		i;
X    int		length;
X    int		found;
X    char	*p;
X
X    /* See if the value is already in the environment. */
X    found = -1;
X    if (p = IDX(val, '=')) {
X	for (length = ++p - val, i = 0; environ[i]; i++)
X	    if (EQn(val, environ[i], length)) {
X		found = i;
X		break;
X	    }
X    }
X
X    /* Get the size, and space for the new environment. */
X    for (i = 0; environ[i]; i++)
X	continue;
X    i += 2;
X    new = NEW(char*, i);
X    new[0] = val;
X
X    /* Copy the old to the new. */
X    for (i = 0; environ[i]; i++)
X	if (i != found)
X	    new[i + 1] = environ[i];
X    new[i + 1] = NULL;
X    environ = new;
X    return 0;
X}
X#endif	/* !defined(HAVE_PUTENV) */
X
X
X/*
X**  Print a usage message and exit.
X*/
XSTATIC void
XUsage()
X{
X    Fprintf(stderr, "Usage:\n\t%s %s %s\n",
X	Pname,
X	"[-.] [-e var=val]",
X	"listname listaddr listadmin host [article]");
X    exit(EX_USAGE);
X}
X
X
Xint
Xmain(ac, av)
X    int			ac;
X    register char	*av[];
X{
X    static char		tmp[sizeof TEMPFILE];
X    register FILE	*F;
X    register HEADER	*hp;
X    register char	*p;
X    STRING		sv[10];
X    char		buff[BUFSIZ];
X    char		SenderAddr[SM_SIZE];
X    char		ToAddr[SM_SIZE];
X    char		Host[SM_SIZE];
X#if	defined(USE_PATH_FOR_FROM)
X    char		Fullname[SM_SIZE];
X#endif	/* defined(USE_PATH_FOR_FROM) */
X    int			i;
X    int			HadEflag;
X    char		*Listname;
X    char		*Listaddr;
X    char		*Listadmin;
X    char		*Listhost;
X    char		*Article;
X
X    /* Set defaults. */
X    if ((Pname = RDX(av[0], '/')) == NULL)
X	Pname = av[0];
X    else
X	Pname++;
X    HadEflag = FALSE;
X
X    /* Parse JCL. */
X    while ((i = getopt(ac, av, "E:.")) != EOF)
X	switch (i) {
X	default:
X	    Usage();
X	    /* NOTREACHED */
X	case '.':
X	    Debugging = TRUE;
X	    break;
X	case 'E':
X	    if (putenv(COPY(optarg))) {
X		Fprintf(stderr, "%s:  Can't add to environment, %s.\n",
X		    Pname, strerror(errno));
X		exit(EX_TEMPFAIL);
X	    }
X	    HadEflag = TRUE;
X	    break;
X	}
X    ac -= optind;
X    av += optind;
X    if (ac != 4 && ac != 5)
X	Usage();
X
X    /* Parse the positional parameters. */
X    Listname = av[0];
X    Listaddr = av[1];
X    Listadmin = av[2];
X    Listhost = av[3];
X    Article = av[4];
X
X    /* Arrange for logging. */
X    if (!Debugging && freopen(ERR_LOG, "a", stderr) == NULL)
X	/* Sigh; error in error handler.... */
X	(void)freopen("/dev/console", "w", stderr);
X
X    if (Article && freopen(Article, "r", stdin) == NULL) {
X	Fprintf(stderr, "%s:  Can't open %s, %s.\n",
X	    Pname, Article, strerror(errno));
X	exit(EX_NOINPUT);
X    }
X
X    /* Who are we? */
X#if	defined(WHOAMI)
X    Strcpy(Host, WHOAMI);
X#else
X    if (gethostname(Host, sizeof Host) < 0) {
X	Fprintf(stderr, "%s:  Can't get hostname, %s.\n",
X	    Pname, strerror(errno));
X	exit(EX_TEMPFAIL);
X    }
X#endif	/* defined(WHOAMI) */
X
X    /* Read headers, storing the ones we want. */
X    while (fgets(buff, sizeof buff, stdin)) {
X	if ((p = IDX(buff, '\n')) != NULL)
X	    *p = '\0';
X	else
X	    Fprintf(stderr, "%s:  Header line too long (%d bytes max)\n\t%s\n",
X		Pname, sizeof buff, buff);
X	if (p == buff)
X	    /* Blank line means end of headers. */
X	    break;
X
X	for (hp = Table; hp < ENDOF(Table); hp++)
X	    if (buff[hp->Length] == ':' && EQn(hp->Tag, buff, hp->Length)) {
X		/* Skip whitespace. */
X		for (p = &buff[hp->Length + 1]; *p && WHITE(*p); p++)
X		    continue;
X		switch (hp->Flag) {
X		case HDR_SUBJ:
X		    if (!EQn(p, "cmsg ", 5)) {
X			Strcpy(hp->Value, p);
X			break;
X		    }
X		    /* FALLTHROUGH */
X		case HDR_CTRL:
X		    /* Eat rest of message. */
X		    while (fgets(buff, sizeof buff, stdin))
X			continue;
X		    exit(EX_OK);
X		    /* NOTREACHED */
X		case HDR_NORM:
X		    Strcpy(hp->Value, p);
X		    break;
X		case HDR_FROM:
X#if	defined(USE_PATH_FOR_FROM)
X		    /* Turn "joe@site.uucp (My Name)" into "(My Name)" */
X		    if ((p = IDX(p, ' ')) && p[1] == '(')
X			Strcpy(Fullname, ++p);
X		    else
X			Fullname[0] = '\0';
X#else
X		    Strcpy(hp->Value, p);
X#endif	/* defined(USE_PATH_FOR_FROM) */
X		    break;
X		case HDR_REFS:
X		    Strcpy(hp->Value, TrimReferences(p));
X		    break;
X#if	defined(USE_PATH_FOR_FROM)
X		case HDR_PATH:
X		    Strcpy(hp->Value, EditPath(p, Host));
X		    break;
X#endif	/* defined(USE_PATH_FOR_FROM) */
X		}
X	}
X    }
X
X    /* Set up temp output. */
X    Strcpy(tmp, TEMPFILE);
X    (void)mktemp(tmp);
X    if ((F = fopen(tmp, "w")) == NULL) {
X	Fprintf(stderr, "%s:  Can't create %s, %s.\n",
X	    Pname, tmp, strerror(errno));
X	exit(EX_CANTCREAT);
X    }
X
X    if (IDX(Listadmin, '@'))
X	Strcpy(SenderAddr, Listadmin);
X    else
X	Sprintf(SenderAddr, "%s@%s", Listadmin, Listhost);
X    if (IDX(Listaddr, '@'))
X	Strcpy(ToAddr, Listaddr);
X    else
X	Sprintf(ToAddr, "%s@%s", Listaddr, Listhost);
X
X    /* Print out a sanitized header for mail. */
X    if (IDX(Listname, '@'))
X	Strcpy(buff, Listname);
X    else
X	Sprintf(buff, "%s@%s", Listname, Listhost);
X    Fprintf(F, "Received: from GATEWAY by %s with netnews\n", Host);
X    Fprintf(F, "\tfor %s (%s)\n", ToAddr, buff);
X    Fprintf(F, "To: %s\n", buff);
X    for (hp = Table; hp < ENDOF(Table); hp++)
X	if (hp->Flag == HDR_PATH) {
X#if	defined(USE_PATH_FOR_FROM)
X	    Fprintf(F, "From: %s %s\n", hp->Value, Fullname);
X#endif	/* defined(USE_PATH_FOR_FROM) */
X	    Fprintf(F, "Sender: %s\n", SenderAddr);
X	}
X	else if (hp->Value[0])
X	    Fprintf(F, "%s: %s\n", hp->Tag, hp->Value);
X    Fprintf(F, "\n");
X
X    /* Dump the body of the message. */
X    while (fgets(buff, sizeof buff, stdin))
X	Fputs(buff, F);
X    if (fclose(F) == EOF)
X	Fprintf(stderr, "%s:  Error closing %s, %s.\n",
X	    Pname, tmp, strerror(errno));
X
X    /* Set I/O correctly.  Stderr is going to the log, stdin should be the
X     * message we created.  Easiest thing is to unlink an open file. */
X    if (freopen(tmp, "r", stdin) == NULL) {
X	Fprintf(stderr, "%s:  Can't open %s for reading, %s.\n",
X	    Pname, tmp, strerror(errno));
X	exit(EX_OSERR);
X    }
X    if (unlink(tmp) < 0)
X	Fprintf(stderr, "%s:  Can't unlink %s, %s.\n",
X	    Pname, tmp, strerror(errno));
X
X    /* Common code for all argument vectors. */
X    i = 0;
X
X#if	defined(MAILSCRIPT)
X    /* Build the MAILSCRIPT argument vector. */
X    sv[i++] = MAILSCRIPT;
X    /* Headers are inline. */
X    sv[i++] = "-ASIS";
X    /* Set the logical sender/from address. */
X    sv[i++] = "-From";
X    sv[i++] = SenderAddr;
X    /* Set the recipient. */
X    sv[i++] = "-recip";
X    sv[i++] = ToAddr;
X#endif	/* defined(MAILSCRIPT) */
X
X#if	defined(SENDMAIL)
X    /* Build of the SENDMAIL argument vector. */
X    sv[i++] = SENDMAIL;
X    /* Ignore periods as message terminator (same as -oi). */
X    sv[i++] = "-i";
X    /* Queued delivery. */
X    sv[i++] = "-odq";
X    /* Set the "From:" address. */
X    sv[i++] = "-f";
X    sv[i++] = SenderAddr;
X    /* Set the recipient. */
X    sv[i++] = ToAddr;
X#endif	/* defined(SENDMAIL) */
X
X#if	defined(MMDF)
X    /* Build of the MMDF argument vector. */
X    sv[i++] = MMDF;
X    /* Deliver to mailbox (m), deliver all mail now (ln), trust me (t), send
X     * no warnings (z), return to "Sender:" (s), get recipients from the
X     * "To:" address (xto*). */
X#if	defined(MMDF_DELIVER_NOW)
X    sv[i++] = "-mlntzsxto*";
X#else
X    sv[i++] = "-mtzsxto*";
X#endif	/* defined(MMDF_DELIVER_NOW) */
X    /* Set the From: address. */
X    sv[i++] = SenderAddr;
X#endif	/* defined(MMDF) */
X
X    /* Null-terminate the vector. */
X    sv[i] = NULL;
X
X    if (Debugging) {
X	for (i = 0; sv[i]; i++)
X	    (void)printf(" |%s| ", sv[i]);
X	(void)printf("\n");
X	if (HadEflag) {
X	    for (i = 0; sv[i]; i++)
X		(void)printf(" [%s] ", sv[i]);
X	    (void)printf("\n");
X	}
X	while (fgets(buff, sizeof buff, stdin))
X	    Fputs(buff, stdout);
X	exit(EX_OK);
X    }
X
X    /* Try to setuid, if desired.  Could "factor out" the execv from the
X     * #if, but I hate the way the resultant "dangling else" looks. */
X#if	defined(TRUSTED)
X    if (setuid(TRUSTED) < 0)
X	Fprintf(stderr, "%s:  Can't setuid to %d, %s.\n",
X		Pname, TRUSTED, strerror(errno));
X    else
X	(void)execv(sv[0], sv);
X#else
X    (void)execv(sv[0], sv);
X#endif	/* defined(TRUSTED) */
X
X    /* Something failed; dump the message and quit */
X    Fprintf(stderr, "%s:  Can't execv %s, %s.\n",
X	Pname, sv[0], strerror(errno));
X    while (fgets(buff, sizeof buff, stdin))
X	Fputs(buff, stdout);
X    exit(EX_OSERR);
X    return EX_OSERR;
X}
END_OF_FILE
  if test 13777 -ne `wc -c <'news2mail.c'`; then
    echo shar: \"'news2mail.c'\" unpacked with wrong size!
  fi
  # end of 'news2mail.c'
fi
if test -f 'rfc822.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rfc822.c'\"
else
  echo shar: Extracting \"'rfc822.c'\" \(15611 characters\)
  sed "s/^X//" >'rfc822.c' <<'END_OF_FILE'
X/*
X**  Routines to read and write mail and news headers.  The code here
X**  is gross and complicated.
X*/
X#include "gate.h"
X#include <time.h>
X#if	defined(RCSID)
Xstatic char RCS[] =
X	"$Header: /nfs/papaya/u2/rsalz/src/newsgate/RCS/rfc822.c,v 1.11 91/07/22 10:26:51 rsalz Exp Locker: rsalz $";
X#endif	/* defined(RCSID) */
X
Xextern time_t		time();
Xextern char		*asctime();
Xextern struct tm	*gmtime();
X
X#define QUESTIONABLE(c) \
X	((c) == '"' || (c) == '(' || (c) == ')' || (c) == '\\')
X
X#define HDR_OTHER		-1
X#define HDR_END			FALSE
X#define HDR_APPROVED		 1
X#define HDR_CONTROL		 2
X#define HDR_DATE		 3
X#define HDR_DISTRIBUTION	 4
X#define HDR_EXPIRE		 5
X#define HDR_FOLLOWTO		 6
X#define HDR_FROM 		 7
X#define HDR_KEYWORDS		 8
X#define HDR_MESSAGEID		 9
X#define HDR_NEWSGROUP 		10
X#define HDR_ORGANIZATION	11
X#define HDR_REFERENCES		12
X#define HDR_REPLYTO		13
X#define HDR_SENDER		14
X#define HDR_SUMMARY		15
X#define HDR_TITLE 		16
X#define HDR_MIMEVERS		17
X#define HDR_MIMEXFER		18
X#define HDR_MIMETYPE		19
X
X
X/*
X**  The list of headers we recognize; all others are stripped.
X*/
Xtypedef struct _HTYPE {
X    STRING	Name;
X    int		Type;
X} HTYPE;
X
XSTATIC HTYPE	HeaderList[] = {
X    {	"Approved:",		HDR_APPROVED		},
X    {	"Control:",		HDR_CONTROL		},
X    {	"Date:",		HDR_DATE		},
X    {	"Posted:",		HDR_DATE		},
X    {	"Distribution:",	HDR_DISTRIBUTION	},
X    {	"Expires:",		HDR_EXPIRE		},
X    {	"Followup-To:",		HDR_FOLLOWTO		},
X    {	"From:",		HDR_FROM		},
X    {	"Keywords:",		HDR_KEYWORDS		},
X    {	"MIME-Version:",	HDR_MIMEVERS		},
X    {	"Content-Type:",	HDR_MIMETYPE		},
X    {	"Content-Transfer-Encoding", HDR_MIMEXFER	},
X    {	"Message-ID:",		HDR_MESSAGEID		},
X    {	"Newsgroups:",		HDR_NEWSGROUP		},
X    {	"Organization:",	HDR_ORGANIZATION	},
X    {	"In-Reply-To:",		HDR_REFERENCES		},
X    {	"References:",		HDR_REFERENCES		},
X    {	"Reply-To:",		HDR_REPLYTO		},
X    {	"Sender:",		HDR_SENDER		},
X    {	"Summary:",		HDR_SUMMARY		},
X    {	"Subject:",		HDR_TITLE		},
X    {	"Title:",		HDR_TITLE		},
X};
X
X
X
X/*
X**  Getline is like fgets, but deals with continuation lines.  It also
X**  ensures that even if a line that is too long is received, the
X**  remainder of the line is thrown away instead of treated like a second
X**  line.
X*/
XSTATIC char *
XGetline(buf, len, fp)
X    char		*buf;
X    int			len;
X    FILE		*fp;
X{
X    register char	*cp;
X    register int	c;
X    register int	n;
X
X    for (n = 0, cp = buf; (c = getc(fp)) != EOF && c != '\n' && n < len; )
X	if (!iscntrl(c) || c == '\b' || c == '\t') {
X	    *cp++ = c;
X	    n++;
X	}
X    if (c == EOF && cp == buf)
X	return NULL;
X    *cp = '\0';
X
X    if (c != '\n')
X	/* Line too long - read part didn't fit into a newline */
X	while ((c = getc(fp)) != '\n' && c != EOF)
X	    continue;
X    else if (cp == buf) {
X	/* Don't look for continuation of blank lines */
X	*cp++ = '\n';
X	*cp = '\0';
X	return buf;
X    }
X
X    while ((c = getc(fp)) == ' ' || c == '\t') {
X	/* Continuation line. */
X	if ((n += 2) < len) {
X	    *cp++ = '\n';
X	    *cp++ = c;
X	}
X	while ((c = getc(fp)) != '\n' && c != EOF)
X	    if ((!iscntrl(c) || c == '\b' || c == '\t') && n++ < len)
X		*cp++ = c;
X    }
X    if (n >= len - 1)
X	cp = buf + len - 2;
X    *cp++ = '\n';
X    *cp = '\0';
X    if (c != EOF)
X	/* push back first char of next header */
X	(void)ungetc(c, fp);
X    return buf;
X}
X
X
X/*
X**  I guess this is basically strncasecmp
X*/
XSTATIC int
Xprefix(full, pref)
X    register char	*full;
X    register char	*pref;
X{
X    register char	fc;
X    register char	pc;
X
X    while ((pc = *pref++) != '\0') {
X	fc = *full++;
X	if (isupper(fc))
X	    fc = tolower(fc);
X	if (isupper(pc))
X	    pc = tolower(pc);
X	if (fc != pc)
X	    return FALSE;
X    }
X    return TRUE;
X}
X
X
XSTATIC int
XHeaderType(p)
X    register char	*p;
X{
X    static int		save = HDR_END;
X    register HTYPE	*hp;
X    char		*colon;
X    char		*space;
X
X    /* some consistency checks (i.e. is this really a header line?) */
X    if (p == NULL || !isascii(*p) || *p == '\n')
X	return save = HDR_END;
X
X    if (WHITE(*p))
X	/* Continuation line. */
X	return save;
X
X    /* If we don't get a "<no space> <colon> <space>", it's not a header. */
X    if ((colon = IDX(p, ':')) == NULL
X     || ((space = IDX(p, ' ')) && space < colon))
X	return save = HDR_END;
X
X    for (hp = HeaderList; hp < ENDOF(HeaderList); hp++)
X	if (prefix(p, hp->Name))
X	    return save = hp->Type;
X    return save = HDR_OTHER;
X}
X
X
X/*
X**  Get the contents of the field of the header line, appending it, with a
X**  space delimeter if it's a continuation line.  If there is already
X**  something in the header storage, skip this header line and the
X**  continuations.
X*/
XSTATIC void
Xgetfield(src, dest, size)
X    register char	*src;
X    register char	*dest;
X    register int	size;
X{
X    static int		skip = FALSE;
X    register char	*p;
X
X    if (src == NULL || dest == NULL)
X	return;
X
X    if (WHITE(*src)) {
X	/* Continuation line.  If skipping or no room, ignore. */
X	if (skip || (size -= strlen(dest)) <= 0)
X	    return;
X	/* Munch all but one whitespace, append it to header. */
X	while (*src && WHITE(*src))
X	    src++;
X	*--src = ' ';
X	(void)strncat(dest, src, size - 1);
X    }
X    else {
X	skip = FALSE;
X	if (*dest) {
X	    /* Already got a value, so mark this as one to skip. */
X	    skip = TRUE;
X	    return;
X	}
X	if ((src = IDX(src, ':')) == NULL)
X	    /* Can't happen! */
X	    return;
X	/* Skip colon, eat whitespace. */
X	for (src++; *src && WHITE(*src); )
X	    src++;
X	(void)strncpy(dest, src, size - 1);
X    }
X
X    /* Munch trailing whitespace. */
X    for (p = dest + strlen(dest); --p >= dest && (*p == '\n' || WHITE(*p)); )
X	continue;
X    p[1] = '\0';
X}
X
X
XSTATIC time_t
Xcgtdate(datestr)
X    char		*datestr;
X{
X    static time_t	lasttime;
X    static char		save[SM_SIZE];
X    char		junk[40];
X    char		month[40];
X    char		day[30];
X    char		tod[60];
X    char		year[50];
X    char		buf[SM_SIZE];
X#if	defined(USE_GETABSDATE)
X    extern time_t	getabsdate();
X#else
X#if	defined(USE_PARSEDATE)
X    extern time_t	parse();
X#else
X    extern time_t	getdate();
X#endif	/* defined(USE_PARSEDATE) */
X#endif	/* defined(USE_GETABSDATE) */
X
X    if (save[0] && EQ(datestr, save))
X	return lasttime;
X
X#if	defined(USE_GETABSDATE)
X    lasttime = getabsdate(datestr, (struct timeb *)NULL);
X#else
X#if	defined(USE_PARSEDATE)
X    lasttime = parsedate(datestr, (struct _TIMEINFO *)NULL);
X#else
X    lasttime = getdate(datestr, (struct timeb *)NULL);
X#endif	/* defined(USE_PARSEDATE) */
X#endif	/* defined(USE_GETABSDATE) */
X
X    if (lasttime < 0 &&
X      sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) {
X	(void)sprintf(buf, "%s %s, %s %s", month, day, year, tod);
X#if	defined(USE_GETABSDATE)
X	lasttime = getabsdate(buf, (struct timeb *)NULL);
X#else
X#if	defined(USE_PARSEDATE)
X	lasttime = parsedate(buf, (struct _TIMEINFO *)NULL);
X#else
X	lasttime = getdate(buf, (struct timeb *)NULL);
X#endif	/* defined(USE_PARSEDATE) */
X#endif	/* defined(USE_GETABSDATE) */
X    }
X    (void)strncpy(save, datestr, sizeof save);
X    return lasttime;
X}
X
X
X/*
X**  Print the date in ARPA format, not ctime(3) format.
X*/
XSTATIC void
XDoDate(fp, ud)
X    FILE		*fp;
X    register char	*ud;
X{
X    register char	*p;
X    register char	*q;
X    register int	i;
X    char		buff[SM_SIZE];
X
X    q = buff;
X
X    /* "16" */
X    p = &ud[8];
X    if (*p == ' ')
X	p++;
X    else
X	*q++ = *p++;
X    *q++ = *p++; *q++ = ' ';
X
X    /* "Sep " */
X    p = &ud[4]; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' ';
X
X    /* "79 " */
X    p = &ud[22]; *q++ = *p++; *q++ = *p++; *q++ = ' ';
X
X    /* "01:03:52" */
X    for (p = &ud[11], i = 8; i > 0; i--)
X	*q++ = *p++;
X
X    /* " GMT" */
X    *q++ = ' '; *q++ = 'G'; *q++ = 'M'; *q++ = 'T'; *q = '\0';
X
X    Fprintf(fp, "Date: %s\n", buff);
X}
X
X
X/*
X**  Crack an RFC822 from header field into address and fullname.  We do
X**  this to make sure we write things out in official form.  "Be liberal
X**  in what you accept, conservative in what you generate."  Anyhow, we
X**  read things into three buffers, one for all <...> text, one for all
X**  (...) text, and a third for stuff not in either.  Either the first or
X**  third buffer will be the real address, depending on whether there is
X**  anything in buffer two or not.
X*/
Xint
XCrackFrom(addr, name, p)
X    ADDRCHAR		*addr;
X    char		*name;
X    char		*p;
X{
X    register ADDRCHAR	*adp;
X    register char	*bp;
X    register char	*ap;
X    register ADDRCHAR	*cp;
X    register ADDRCHAR	CurrChar;
X    register int	comment;
X    register int	address;
X    register int	addrfound;
X    register int	QuoteNext;
X    register int	InQuotes;
X    ADDRCHAR		*xp;
X    ADDRCHAR		comm[LG_SIZE];
X    ADDRCHAR		commbuf[LG_SIZE];
X    ADDRCHAR		addrbuf[LG_SIZE];
X
X    /* Just to make sure. */
X    *name = '\0';
X    *addr = '\0';
X
X    if (p == NULL)
X	return FALSE;
X
X    /* Eat leading white space. */
X    while (*p && isspace(*p))
X	p++;
X
X    /* Set defaults. */
X    comm[0] = '\0';
X    commbuf[0] = '\0';
X    addrbuf[0] = '\0';
X    adp = addrbuf;
X    comment = 0;
X    addrfound = 0;
X    address = 0;
X    QuoteNext = 0;
X    InQuotes = 0;
X    for (; *p; p++) {
X	CurrChar = (*p & UNQUOTE_MASK) | QuoteNext;
X	QuoteNext = CurrChar == '\\' ? QUOTE_MASK : 0;
X	if (!comment && CurrChar == '"')
X	    InQuotes = InQuotes ? 0 : QUOTE_MASK;
X	else
X	    CurrChar |= InQuotes;
X	switch (CurrChar) {
X	case '(':
X	    if (comment == 0) {
X		cp = commbuf;
X		*cp = '\0';
X	    }
X	    comment++;
X	    break;
X	case ')':
X	    if (comment > 0 && --comment == 0) {
X		*cp = '\0';
X		xp = comm;
X		if (*xp) {
X		    while (*xp)
X			xp++;
X		    *xp++ = ',';
X		    *xp++ = ' ';
X		}
X		for (cp = &commbuf[1]; (*xp++ = *cp) != '\0'; cp++)
X		    continue;
X		cp = NULL;
X		continue;
X	    }
X	    break;
X	case '<':
X	    if (address)
X		return FALSE;	/* AWK! Abort! */
X	    if (!comment) {
X		address++;
X		*adp = '\0';
X		adp = addr;
X	    }
X	    break;
X	case '>':
X	    if (!comment && address) {
X		address--;
X		addrfound++;
X		for (*adp = '\0', adp = addrbuf; *adp; )
X		    adp++;
X		continue;
X	    }
X	    break;
X	}
X
X	if (comment)
X	    *cp++ = CurrChar;
X	else if (!address || CurrChar != '<')
X	    *adp++ = CurrChar;
X	if (*p == '\0')
X	    break;
X    }
X
X    *adp++ = '\0';
X
X    if (addrfound) {
X	for (ap = name, xp = addrbuf; (*ap++ = (*xp & UNQUOTE_MASK)) != 0; xp++)
X	    continue;
X    }
X    else {
X	for (cp = addr, xp = addrbuf; (*cp++ = *xp) != 0; xp++)
X	    continue;
X	*name = '\0';
X    }
X
X    /* Just to be sure that we got the full name, we'll take all of
X     * the comments. */
X    if (*comm) {
X	ap = name;
X	if (*ap) {
X	    while (*ap)
X		ap++;
X	    *ap++ = ',';
X	    *ap++ = ' ';
X	}
X	for (xp = comm; (*ap = (*xp++ & UNQUOTE_MASK)) != 0; ap++)
X	    continue;
X    }
X    /* Copy back, skipping leading spaces and trailing spaces. */
X    for (cp = addr; isspace(*cp); cp++)
X	continue;
X    for (xp = addr; (*xp = *cp++) != 0; )
X	xp++;
X    while (*addr && isspace(*--xp))
X	*xp = '\0';
X
X    /* Since characters in (comments) are interpreted differently from
X     * those in "phrase" <address>, play it safe and remove all
X     * questionable characters. */
X    for (ap = name, bp = name; isspace(*bp) || QUESTIONABLE(*bp); bp++)
X	continue;
X    while ((*ap = *bp++) != '\0')
X	if (!QUESTIONABLE(*ap))
X	    ap++;
X	while (*name && isspace(*--ap))
X	    *ap = '\0';
X    return TRUE;
X}
X
X
X/*
X**  Write out an RFC822 header, paying no attention to line limits.
X**  Ideally, we should do continuations in here...
X*/
Xint
Xrfc822write(hp, fp)
X    register HBUF	*hp;
X    register FILE	*fp;
X{
X    time_t		t;
X
X    if (hp->path[0])
X	Fprintf(fp, "Path: %s\n", hp->path);
X    if (hp->from[0])
X	Fprintf(fp, "From: %s\n", hp->from);
X    if (hp->nbuf[0])
X	Fprintf(fp, "Newsgroups: %s\n", hp->nbuf);
X    if (hp->title[0])
X	Fprintf(fp, "Subject: %s\n", hp->title);
X    if (hp->ident[0])
X	Fprintf(fp, "Message-ID: %s\n", hp->ident);
X    /* Get current time. This will be used resolve the timezone. */
X    if (hp->subdate[0] == '\0')
X	(void)time(&t);
X    else if ((t = cgtdate(hp->subdate)) < 0) {
X	Fprintf(fp, "X-Unparseable-Date: %s\n", hp->subdate);
X	(void)time(&t);
X    }
X    DoDate(fp, asctime(gmtime(&t)));
X    if (hp->expdate[0])
X	Fprintf(fp, "Expires: %s\n", hp->expdate);
X    if (hp->followid[0])
X	Fprintf(fp, "References: %s\n", hp->followid);
X    if (hp->ctlmsg[0])
X	Fprintf(fp, "Control: %s\n", hp->ctlmsg);
X    if (hp->sender[0])
X	Fprintf(fp, "Sender: %s\n", hp->sender);
X    if (hp->mimeversion[0])
X	Fprintf(fp, "MIME-Version: %s\n", hp->mimeversion);
X    if (hp->mimexfer[0])
X	Fprintf(fp, "Content-Transfer-Encoding: %s\n", hp->mimexfer);
X    if (hp->mimetype[0])
X	Fprintf(fp, "Content-Type: %s\n", hp->mimetype);
X    if (hp->replyto[0])
X	Fprintf(fp, "Reply-To: %s\n", hp->replyto);
X    if (hp->followto[0])
X	Fprintf(fp, "Followup-To: %s\n", hp->followto);
X    if (hp->distribution[0])
X	Fprintf(fp, "Distribution: %s\n", hp->distribution);
X    if (hp->organization[0])
X	Fprintf(fp, "Organization: %s\n", hp->organization);
X    if (hp->keywords[0])
X	Fprintf(fp, "Keywords: %s\n", hp->keywords);
X    if (hp->summary[0])
X	Fprintf(fp, "Summary: %s\n", hp->summary);
X    if (hp->approved[0])
X	Fprintf(fp, "Approved: %s\n", hp->approved);
X    Fprintf(fp, "\n");
X    return !ferror(fp);
X}
X
X
Xvoid
Xrfc822read(hp, fp, buff, buffsize)
X    register HBUF	*hp;
X    register FILE	*fp;
X    char		*buff;
X    int			buffsize;
X{
X    register int	i;
X    long		curpos;
X
X    /* Zap out the headers. */
X    hp->approved[0] = '\0';
X    hp->ctlmsg[0] = '\0';
X    hp->subdate[0] = '\0';
X    hp->distribution[0] = '\0';
X    hp->expdate[0] = '\0';
X    hp->followto[0] = '\0';
X    hp->from[0] = '\0';
X    hp->followid[0] = '\0';
X    hp->keywords[0] = '\0';
X    hp->ident[0] = '\0';
X    hp->nbuf[0] = '\0';
X    hp->organization[0] = '\0';
X    hp->title[0] = '\0';
X    hp->replyto[0] = '\0';
X    hp->summary[0] = '\0';
X    hp->path[0] = '\0';
X    hp->sender[0] = '\0';
X    hp->mimeversion[0] = '\0';
X    hp->mimexfer[0] = '\0';
X    hp->mimetype[0] = '\0';
X
X    i = HeaderType(buff);
X    do {
X	curpos = ftell(fp);
X	switch (i) {
X	case HDR_APPROVED:
X	    getfield(buff, hp->approved, sizeof hp->approved);
X	    break;
X	case HDR_CONTROL:
X	    getfield(buff, hp->ctlmsg, sizeof hp->ctlmsg);
X	    break;
X	case HDR_DATE:
X	    getfield(buff, hp->subdate, sizeof hp->subdate);
X	    break;
X	case HDR_DISTRIBUTION:
X	    getfield(buff, hp->distribution, sizeof hp->distribution);
X	    break;
X	case HDR_EXPIRE:
X	    getfield(buff, hp->expdate, sizeof hp->expdate);
X	    break;
X	case HDR_FOLLOWTO:
X	    getfield(buff, hp->followto, sizeof hp->followto);
X	    break;
X	case HDR_FROM:
X	    getfield(buff, hp->from, sizeof hp->from);
X	    break;
X	case HDR_KEYWORDS:
X	    getfield(buff, hp->keywords, sizeof hp->keywords);
X	    break;
X	case HDR_MESSAGEID:
X	    getfield(buff, hp->ident, sizeof hp->ident);
X	    break;
X	case HDR_NEWSGROUP:
X	    getfield(buff, hp->nbuf, sizeof hp->nbuf);
X	    break;
X	case HDR_ORGANIZATION:
X	    getfield(buff, hp->organization, sizeof hp->organization);
X	    break;
X	case HDR_REFERENCES:
X	    getfield(buff, hp->followid, sizeof hp->followid);
X	    break;
X	case HDR_REPLYTO:
X	    getfield(buff, hp->replyto, sizeof hp->replyto);
X	    break;
X	case HDR_SENDER:
X	    getfield(buff, hp->sender, sizeof hp->sender);
X	    break;
X	case HDR_MIMEVERS:
X	    getfield(buff, hp->mimeversion, sizeof hp->mimeversion);
X	    break;
X	case HDR_MIMEXFER:
X	    getfield(buff, hp->mimexfer, sizeof hp->mimexfer);
X	    break;
X	case HDR_MIMETYPE:
X	    getfield(buff, hp->mimetype, sizeof hp->mimetype);
X	    break;
X	case HDR_SUMMARY:
X	    getfield(buff, hp->summary, sizeof hp->summary);
X	    break;
X	case HDR_TITLE:
X	    getfield(buff, hp->title, sizeof hp->title);
X	    break;
X	}
X    } while ((i = HeaderType(Getline(buff, buffsize, fp))) != HDR_END);
X
X    if (*buff != '\n')
X	(void)fseek(fp, curpos, 0);
X}
END_OF_FILE
  if test 15611 -ne `wc -c <'rfc822.c'`; then
    echo shar: \"'rfc822.c'\" unpacked with wrong size!
  fi
  # end of 'rfc822.c'
fi
if test -f 'sysexits.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sysexits.h'\"
else
  echo shar: Extracting \"'sysexits.h'\" \(4568 characters\)
  sed "s/^X//" >'sysexits.h' <<'END_OF_FILE'
X/*
X * Copyright (c) 1987 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley.  The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X *
X *	@(#)sysexits.h	4.5 (Berkeley) 7/6/88
X */
X
X/*
X**  SYSEXITS.H -- Exit status codes for system programs.
X**
X**	This include file attempts to categorize possible error
X**	exit statuses for system programs, notably delivermail
X**	and the Berkeley network.
X**
X**	Error numbers begin at EX__BASE to reduce the possibility of
X**	clashing with other exit statuses that random programs may
X**	already return.  The meaning of the codes is approximately
X**	as follows:
X**
X**	EX_USAGE -- The command was used incorrectly, e.g., with
X**		the wrong number of arguments, a bad flag, a bad
X**		syntax in a parameter, or whatever.
X**	EX_DATAERR -- The input data was incorrect in some way.
X**		This should only be used for user's data & not
X**		system files.
X**	EX_NOINPUT -- An input file (not a system file) did not
X**		exist or was not readable.  This could also include
X**		errors like "No message" to a mailer (if it cared
X**		to catch it).
X**	EX_NOUSER -- The user specified did not exist.  This might
X**		be used for mail addresses or remote logins.
X**	EX_NOHOST -- The host specified did not exist.  This is used
X**		in mail addresses or network requests.
X**	EX_UNAVAILABLE -- A service is unavailable.  This can occur
X**		if a support program or file does not exist.  This
X**		can also be used as a catchall message when something
X**		you wanted to do doesn't work, but you don't know
X**		why.
X**	EX_SOFTWARE -- An internal software error has been detected.
X**		This should be limited to non-operating system related
X**		errors as possible.
X**	EX_OSERR -- An operating system error has been detected.
X**		This is intended to be used for such things as "cannot
X**		fork", "cannot create pipe", or the like.  It includes
X**		things like getuid returning a user that does not
X**		exist in the passwd file.
X**	EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp,
X**		etc.) does not exist, cannot be opened, or has some
X**		sort of error (e.g., syntax error).
X**	EX_CANTCREAT -- A (user specified) output file cannot be
X**		created.
X**	EX_IOERR -- An error occurred while doing I/O on some file.
X**	EX_TEMPFAIL -- temporary failure, indicating something that
X**		is not really an error.  In sendmail, this means
X**		that a mailer (e.g.) could not create a connection,
X**		and the request should be reattempted later.
X**	EX_PROTOCOL -- the remote system returned something that
X**		was "not possible" during a protocol exchange.
X**	EX_NOPERM -- You did not have sufficient permission to
X**		perform the operation.  This is not intended for
X**		file system problems, which should use NOINPUT or
X**		CANTCREAT, but rather for higher level permissions.
X**		For example, kre uses this to restrict who students
X**		can send mail to.
X**
X**	Maintained by Eric Allman (eric@berkeley, ucbvax!eric) --
X**		please mail changes to me.
X**
X**			@(#)sysexits.h	4.5		7/6/88
X*/
X
X# define EX_OK		0	/* successful termination */
X
X# define EX__BASE	64	/* base value for error messages */
X
X# define EX_USAGE	64	/* command line usage error */
X# define EX_DATAERR	65	/* data format error */
X# define EX_NOINPUT	66	/* cannot open input */
X# define EX_NOUSER	67	/* addressee unknown */
X# define EX_NOHOST	68	/* host name unknown */
X# define EX_UNAVAILABLE	69	/* service unavailable */
X# define EX_SOFTWARE	70	/* internal software error */
X# define EX_OSERR	71	/* system error (e.g., can't fork) */
X# define EX_OSFILE	72	/* critical OS file missing */
X# define EX_CANTCREAT	73	/* can't create (user) output file */
X# define EX_IOERR	74	/* input/output error */
X# define EX_TEMPFAIL	75	/* temp failure; user is invited to retry */
X# define EX_PROTOCOL	76	/* remote error in protocol */
X# define EX_NOPERM	77	/* permission denied */
X# define EX_CONFIG	78	/* configuration error */
END_OF_FILE
  if test 4568 -ne `wc -c <'sysexits.h'`; then
    echo shar: \"'sysexits.h'\" unpacked with wrong size!
  fi
  # end of 'sysexits.h'
fi
echo shar: End of archive 2 \(of 4\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still must unpack the following archives:
    echo "        " ${MISSING}
fi
exit 0
