Subject:  News/mail gateway package, Part02/04
Newsgroups: alt.sources

Archive-name: newsgate2beta/part02

#! /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.1 mail2news.c news2mail.c rfc822.c
# Wrapped by rsalz@litchi.bbn.com on Fri Feb 28 11:14:00 1992
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.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mail2news.1'\"
else
  echo shar: Extracting \"'mail2news.1'\" \(5693 characters\)
  sed "s/^X//" >'mail2news.1' <<'END_OF_FILE'
X.\" $Header: /nfs/papaya/u2/rsalz/src/newsgate/RCS/mail2news.1,v 1.13 91/07/18 15:01:54 rsalz Exp Locker: rsalz $
X.TH MAIL2NEWS 1 LOCAL
X.SH NAME
Xmail2news \- feed a mail message into Usenet news
X.SH SYNOPSIS
X.B mail2news
X[
X.B \-\&.
X] [
X.B \-F
X] [
X.BI \-= program
X] [
X.BI \-n newsgroups
X] [
X.BI \-o organization
X] [
X.BI \-d distribution
X] [
X.BI \-a approval
X] [
X.B inewsflags
X]
X.SH DESCRIPTION
X.I Mail2news
Xreads an RFC822 Mail message on standard input, writes the headers to conform
Xwith RFC1036 (superceeding RFC850) Usenet News article.
XA subject is required, unless one of the
X.IR inews (8)
X``\-x'' or ``\-o'' flags are specified.
X.PP
XIf the ``\-.'' flag is specified, the modified article is sent to the
Xstandard output, rather than feed into
X.IR inews .
X.PP
XBy default,
X.I mail2news
Xsends its input to ``inews \-h''; this can be changed by using the ``\-=''
Xflag, which will send the output to the specified program.
XNote that no argument parsing is done, so that only a single word may
Xbe specified with the ``\-='' flag.
X.PP
XThe ``\-o,'' ``\-n,'' ``\-d,'' or ``\-a'' flags may be used to specify the
XOrganization, Newsgroups, Distribution, or Approved header in the message.
XNote that unlike the
X.I inews
Xparallels of these flags, the ``\-n'' flag overrides any Newsgroups header
Xthat might appear in the message, while the other flags specify defaults
Xif the message does not have the headers.
XIn addition, as a concession to
X.IR sendmail (8)
Xalias processing the argument to the ``\-o'' flag is handled specially.
XSince many
X.I sendmail.cf
Xfiles do not have the ``preserve case'' flag on for the ``prog'' mailer,
Xa period in the argument means that the following character should be converted
Xto uppercase; two periods mean to output a single period.
XFor example, if the following line is in
X.IR /usr/lib/aliases (5):
X.RS
Xpost\-news\-admin:
X    "|/usr/local/bin/mail2news \-o '.the .b...b...n.. .gateway' \-n news.admin"
X.RE
Xthen messages to mailed to
X.I post\-news\-admin
Xwill be posted to the ``news.admin'' newsgroup with the following header line:
X.RS
XOrganization: The B.B.N. Gateway
X.RE
X.PP
XIf the ``\-F'' flag is given, then
X.I mail2news
Xwill try to filter out
Xthe ``please add me to your mailing list'' messages often sent by
Xnetwork neophytes.
XThis is done by looking for short messages which have words like
X.I subscribe
Xin them.
X.PP
X.I Mail2news
Xcan modify the newsgroups an article is sent to, based on regular expressions
Xfound in the ``Title:'', ``Keywords:'', and ``Summary:'' headers.
XThe headers are scanned once for each newsgroup the article is originally
Xdestined for, provided a mapping file exists for the newsgroup.
X(The path to the mapping file depends on a compile\-time parameter.)
XFor example, if a mail message is to be sent to
X.I talk.foo
Xand
X.IR soc.bar ,
X.I mail2news
Xwill scan the headers twice.
XIf the file
X.I /usr/lib/news/.admin/talk.foo
X(or
X.IR /usr/spool/news/talk/foo/recnews.cmd )
Xexists, the commands in that file are interpreted.
XThe scan is repeated, with the appropriate file for
X.IR soc.bar .
XIn the explanation below, the ``current newsgroup'' will be first
X.I talk.foo
Xand then
X.IR soc.bar .
X.PP
XBlank lines, and lines beginning with a pound sign
X.RI ( # )
Xare ignored.
XOther lines should like like this:
X.RS
X.IR "delim pattern delim command whitespace " "[" "arg" "]"
X.RE
XThe
X.I delim
Xcan be any character; if it appears in the pattern, it should be
Xpreceeded by a backslash
X.RI ( \e ).
XThe
X.I pattern
Xis an
X.RI `` ed (1)\-style''
Xregular expression, as detailed in the
X.IR regex (3L)
Xmanual page.
XThe
X.I command
Xis a single letter from the set
X.RI [ adkmq ],
Xand is described below.
XThe
X.I whitespace
Xis, obviously, any combination of spaces and tabs.
XFinally, the optional
X.I arg
Xshould be a comma\-separated list of newsgroups to be acted on by the
Xcommand.
X.PP
XThe valid commands are:
X.RS
X.nf
X.ta \w'm      'u +\w'Quiet kill    'u
Xa	Add	All newsgroups in the arg are added
Xd	Delete	The current group is deleted from the article
Xk	Kill	The article is returned, and arg is given as a contact name
Xm	Move	The current group is deleted and the arg group is added
Xq	Quiet kill	The article is quietly ignored
X.fi
X.RE
X.PP
XFor example, here is a fragment from the mapping file for a
X.I local.bboard
Xnewsgroup:
X.RS
X.nf
X.ta \w'/apartment/m   'u
X# Move seminars and talks
X/seminar/m	local.seminars,region.seminars
X/lecture/m	local.seminars,region.seminars
X# Move housing requests
X/housing/m	local.housing
X/apartment/m	local.housing
X# Kill flames
X/flame/k	the anti-flame society
X/jerk/q	@placeholder
X# Copy machine downtime announcements
X/downtime/a	local.config,news.config
X# If you want something, it's not forsale
X/want/a		local.wanted
X/want/d		@placeholder
X.fi
X.RE
XThe last two lines show how one might tweak cross\-posts, if the mapping
Xfile were for a
X.I local.wanted
Xnewsgroup.
X.SH DIAGNOSTICS
XAlmost all return values from system calls and standard I/O library routines
Xare checked.
XIf anything goes wrong, a diagnostic message is sent to standard error, where
Xit will presumably be picked up by
X.I sendmail
X(or other mail-processing program) and returned to the originator, who will
Xin turn be confused because she doesn't know anything at all about this
Xgateway program.
X.SH FILES
X.ta \w'uucp\-2\-inet  'u
Xuucp\-2\-inet	Mapping of ``Path:'' names to Internet hostnames.
X.br
Xinews	News delivery program.
X.br
XThe full paths to these files are compile\-time constants; see
X.I gate.h
Xin the source for the exact details.
X.SH "SEE ALSO"
Xgag(1L), mkmailpost(1L), news2mail(1L).
X.SH AUTHORS
XRich $alz <rsalz@bbn.com>, after
X.I nrecnews
Xby
X.br
XErik E. Fair <fair@apple.com>.
X.br
XOzan Yigit <yunexus!oz> wrote the regular\-expression routines.
END_OF_FILE
  if test 5693 -ne `wc -c <'mail2news.1'`; then
    echo shar: \"'mail2news.1'\" unpacked with wrong size!
  fi
  # end of 'mail2news.1'
fi
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'\" \(13630 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    {	"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	;
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", 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 13630 -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'\" \(14455 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
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    {	"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; n < len && (c = getc(fp)) != EOF && c != '\n'; )
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    extern time_t	getdate();
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    lasttime = getdate(datestr, (struct timeb *)NULL);
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	lasttime = getdate(buf, (struct timeb *)NULL);
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->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
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_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 14455 -ne `wc -c <'rfc822.c'`; then
    echo shar: \"'rfc822.c'\" unpacked with wrong size!
  fi
  # end of 'rfc822.c'
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
exit 0 # Just in case...
