[comp.sources.unix] v24i053: News/mail gateway package, Part03/04

rsalz@uunet.uu.net (Rich Salz) (03/16/91)

Submitted-by: Rich $alz <rsalz@bbn.com>
Posting-number: Volume 24, Issue 53
Archive-name: newsgate/part03

#! /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:  gate.h hdr.c lex.l mail-interface mail2news.1 misc.c
#   news2mail.1 signoff.c sysexits.h uucp-2-inet
# Wrapped by rsalz@litchi.bbn.com on Fri Mar 15 16:42:27 1991
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 3 (of 4)."'
if test -f 'gate.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'gate.h'\"
else
  echo shar: Extracting \"'gate.h'\" \(6518 characters\)
  sed "s/^X//" >'gate.h' <<'END_OF_FILE'
X/*
X**  HEADER FILE FOR NEWS/MAIL GATEWAY CODE.
X**  $Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/gate.h,v 1.12 91/03/13 15:59:23 rsalz Exp $
X*/
X
X
X/*
X**  START OF CONFIGURATION SECTION
X*/
X
X
X/* Paths to some parts of your netnews installation.  Required. */
X#define INEWS		"/usr/lib/news/inews"
X#define ACTIVE		"/usr/lib/news/active"
X#define NGDELIM		','
X
X
X/* In writing Paths into From addresses, we can look at the output
X * of uuname from the L.sys file.  Define both or neither of the next
X * two.  We can also map some UUCP names to their domain names, if
X * the third line is enabled.  The UUCP_INET should match the value
X * in the Makefile.  This violates the "write once" rule, but it's
X * too much of a pain to fix for this one symbol. */
X#define UUNAME		"/usr/lib/news/.admin/uuname.out"
X#define L_SYS		"/usr/lib/uucp/L.sys"
X#define UUCP_INET	"/usr/lib/news/.admin/uucp-2-inet"
X
X
X/* Where does the control file for talk.foo live?
X *	IN_ONEPLACE:	specified filename
X *	IN_SPOOLDIR:	/usr/spool/news/talk/foo/recnews.cmd
X *	IN_CMDDIR:	/usr/lib/news/.admin/talk.foo
X * One of these is required, but you can set IN_ONEPLACE to /dev/null to
X * disable the newsgroup editing. */
X/*efine IN_ONEPLACE	"/dev/null"		/* .. */
X/*efine IN_SPOOLDIR	"/usr/spool/news"	/* .. */
X#define	IN_CMDDIR	"/usr/lib/news/.admin"	/* .. */
X
X
X/* What do you want to do with the Path: line?  Put in a fixed string
X * (such as pointing to a mail reflector saying "Don't trust Path:
X * lines as a way to reply").  Put a fixed "fake host" in the Path:,
X * or just put the user's host there.  The latter two can cause the
X * poster's site to never get the article.  Anyhow, pick one.  To pick
X * neither #define is to get the third behavior -- the user's host. */
X#define FIXED_PATH	"news-mail-gateway"	/* .. */
X/*efine	GATEWAY_NAME	"gateway"		/* .. */
X
X
X/* The code in hdr.c does lots of work to canonicalize addresses.  You
X * shouldn't disable it, but at least one beta-tester wanted to, sigh. */
X#define DO_ADDRESS_CLEANUP			/* .. */
X
X
X/* Are you running sendmail or MMDF?  Pick one.  If you believe in
X * trusted users (MMDF doesn't?) to lie to your mailer, set the
X * user-ID. */
X/*efine SENDMAIL	"/usr/lib/sendmail"	/* .. */
X#define	MMDF		"/usr/mmdf/lib/submit"	/* .. */
X/*efine TRUSTED		1			/* .. */
X
X
X/* Does your Sendmail mailer have the M flag on? */
X#ifdef	SENDMAIL
X#define REQUIRE_MESSAGE_ID			/* .. */
X#endif	/* SENDMAIL */
X
X
X/* I love how we all speak the same language. */
X/*efine CATCHER		int		/* Type of a signal-catcher	*/
X#define CATCHER		void		/* .. */
X#define IDX		index		/* .. */
X#define RDX		rindex		/* .. */
X/*efine IDX		strchr		/* .. */
X/*efine RDX		strrchr		/* .. */
Xtypedef int		*align_t;	/* Worst-case alignment, for lint */
X#define CHARSTAR_SPRINTF		/* Need extern char *sprintf();	*/
X#define VOID_EXIT			/* Need extern void exit();	*/
X#define HAVE_SYSEXITS			/* Have <sysexits.h>?		*/
X#define HAVE_TIMEB			/* Have <sys/timeb.h>?		*/
X#define HAVE_PUTENV			/* Have putenv(3)		*/
X/*efine HAVE_STRERROR			/* Have strerror(3)?		*/
X
X#define SM_SIZE		512		/* A smallish buffer size	*/
X#define LG_SIZE		1024		/* big buffer size		*/
X
X/* Error log (stderr) for news2mail. */
X#define ERR_LOG		"/usr/lib/news/.admin/news2mail.out"
X#define TEMPFILE	"/tmp/gateXXXXXX" /* Temporary file pattern	*/
X
X
X/* Enable debugging code? */
X#define STATIC		static
X#ifdef	lint
X#undef	RCSID
X#else
X#define RCSID
X#endif	/* lint */
X
X/*
X**  END OF CONFIGURATION SECTION.
X*/
X
X
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X
X#ifdef	HAVE_SYSEXITS
X#include <sysexits.h>
X#else
X#include "sysexits.h"
X#endif	/* HAVE_SYSEXITS */
X
X
Xtypedef struct _HBUF {
X    char	approved[SM_SIZE];	/* Approved:		*/
X    char	ctlmsg[LG_SIZE];	/* Control:		*/
X    char	subdate[SM_SIZE];	/* Date: (submission)	*/
X    char	distribution[SM_SIZE];	/* Distribution:	*/
X    char	expdate[SM_SIZE];	/* Expires:		*/
X    char	followto[SM_SIZE];	/* Followup-to:		*/
X    char	from[SM_SIZE];		/* From:		*/
X    char	followid[SM_SIZE];	/* References:		*/
X    char	keywords[SM_SIZE];	/* Keywords:		*/
X    char	ident[SM_SIZE];		/* Message-ID:		*/
X    char	nbuf[LG_SIZE];		/* Newsgroups:		*/
X    char	organization[SM_SIZE];	/* Organization:	*/
X    char	title[SM_SIZE];		/* Subject:		*/
X    char	replyto[SM_SIZE];	/* Reply-To:		*/
X    char	summary[SM_SIZE];	/* Summary:		*/
X    char	path[LG_SIZE];		/* Path:		*/
X    char	sender[SM_SIZE];	/* Sender:		*/
X} HBUF;
X
X
X/* String and memory manipulators. */
X#define APPEND(p, t)	strlen(strcpy((p), (t)))
X#define NEW(T, c)	(T *)MyAlloc((c) * sizeof (T))
X#define COPY(s)		((s) ? strcpy(NEW(char, strlen((s)) + 1), (s)) : NULL)
X#define REALLOC(p, s)	realloc((char *)(p), (unsigned int)(s))
X
X
X/* Array sizing. */
X#define SIZEOF(x)	(sizeof x / sizeof x[0])
X#define ENDOF(x)	(&x[SIZEOF(x)])
X
X
X/* String and character operations. */
X#define WHITE(c)	((c) == ' ' || (c) == '\t')
X#define EQ(a, b)	((a)[0] == (b)[0] && strcmp((a), (b)) == 0)
X#define EQn(a, b, n)	((a)[0] == (b)[0] && strncmp((a), (b), (n)) == 0)
X#define NETCHR(c)	((c) == '.' || (c) == '%' || (c) == '@' || (c) == '!')
X#define CHREQ(c, d)	((d) == (islower((c)) ? toupper((c)) : (c)))
X
X
X/* Fundamental constants of the universe. */
X#define TRUE		1
X#define FALSE		0
X#define FAIL		(-1)
X
X
X/* SHUT UP! */
X#ifdef	lint
X#undef	putc
X#undef	putchar
X#endif	/* lint */
X
X#define Close		(void)close
X#define Fflush		(void)fflush
X#define Fprintf		(void)fprintf
X#define Fputs		(void)fputs
X#define Signal		(void)signal
X#define Sprintf		(void)sprintf
X#define Strcpy		(void)strcpy
X#define Strcat		(void)strcat
X#define Strncpy		(void)strncpy
X
X
X/*
X**  External declarations.
X*/
X
X/* Program name; exists once for each main(). */
Xextern	char	*Pname;
X
X/* Routines we provide. */
Xextern align_t	MyAlloc();
Xextern int	Split();
Xextern int	CrackFrom();
Xextern void	re_modw();
Xextern void	SplitFree();
Xextern void	FreeFile();
Xextern char	**ReadFile();
Xextern char	*re_comp();
Xextern char	*HackHeader();
X
Xextern char	*strerror();
X
X/* Variables and routines that Unix(tm) provides. */
Xextern int	errno;
Xextern int	sys_nerr;
Xextern int	optind;
Xextern char	*sys_errlist[];
Xextern char	**environ;
Xextern char	*optarg;
X
Xextern FILE	*popen();
Xextern char	*IDX();
Xextern char	*RDX();
Xextern char	*ctime();
Xextern char	*malloc();
Xextern char	*mktemp();
Xextern char	*realloc();
Xextern char	*strcat();
Xextern char	*strncat();
Xextern char	*strcpy();
Xextern char	*strncpy();
X#ifdef	CHARSTAR_SPRINTF
Xextern char	*sprintf();
X#endif	/* CHARSTAR_SPRINTF */
X#ifdef	VOID_EXIT
Xextern void	exit();
X#endif	/* VOID_EXIT */
END_OF_FILE
  if test 6518 -ne `wc -c <'gate.h'`; then
    echo shar: \"'gate.h'\" unpacked with wrong size!
  fi
  # end of 'gate.h'
fi
if test -f 'hdr.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hdr.c'\"
else
  echo shar: Extracting \"'hdr.c'\" \(10652 characters\)
  sed "s/^X//" >'hdr.c' <<'END_OF_FILE'
X/*
X**  Header-cracking and address re-writing routines, with sincere
X**  apologies to Upas and Sendmail.
X*/
X#include "gate.h"
X#include <netdb.h>
X#ifdef	RCSID
Xstatic char RCS[] =
X	"$Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/hdr.c,v 1.15 91/03/15 16:39:42 rsalz Exp $";
X#endif	/* RCSID */
X
X
X#ifdef	TEST
X#ifndef	DO_FIX_ADDRESS
X#define DO_FIX_ADDRESS
X#endif	/* DO_FIX_ADDRESS */
X#define dprintf(string, buff)	(void)printf((string), (buff))
X#else
X#define dprintf(string, buff)	/* NULL */
X#endif	/* TEST */
X
X
X/*
X**  List of domains that we recognize.
X*/
XSTATIC char	RELAY_CS_NET[] = "RELAY.CS.NET";
XSTATIC char	*Domains[] = {
X    /* Don't undo the % hack for sites @RELAY.CS.NET, sigh #1. */
X	RELAY_CS_NET,
X    /* These aren't official domains, but we use them, sigh #2. */
X	".BITNET",
X	".UUCP",
X    /* Official organizational domains. */
X	".ARPA",	".COM",	".EDU",	".GOV",	".INT",	".MIL",
X	".NATO",	".NET",	".ORG",
X    /* Official natonal domans. */
X	".AR",	".AT",	".AU",	".BE",	".BR",	".CA",	".CH",	".CL",
X	".CN",	".CR",	".CS",	".DE",	".DK",	".EC",	".EG",	".ES",
X	".FI",	".FR",	".GR",	".HK",	".HU",	".IE",	".IL",	".IN",
X	".IS",	".IT",	".JP",	".KR",	".LK",	".MX",	".MY",	".NI",
X	".NL",	".NO",	".NZ",	".PH",	".PL",	".PR",	".PT",	".SE",
X	".SG",	".SU",	".TH",	".TR",	".TW",	".UK",	".US",	".UY",
X	".YU",	".ZA"
X};
X
X
X/*
X**  Local user?
X*/
XSTATIC int
XLocal(p)
X    register char	*p;
X{
X    for ( ; *p; p++)
X	if (NETCHR(*p))
X	    break;
X    return *p == '\0';
X}
X
X
X/*
X**  Case-insensitive strncmp.
X*/
XSTATIC char *
XFound(dp, p)
X    register char	*dp;
X    register char	*p;
X{
X    register char	*q;
X    register char	*r;
X
X    for ( ; *p; p++)
X	if (CHREQ(*p, *dp)) {
X	    for (q = p, r = dp; *r && CHREQ(*q, *r); q++, r++)
X		;
X	    if (*r == '\0')
X		return p;
X	}
X
X    return NULL;
X}
X
X
X/*
X**  Find all domain names and make them uppercase:
X**	joe%site.edu@relay.cs.net --> joe%site.EDU@RELAY.CS.NET
X*/
XSTATIC void
XCasify(p)
X    register char	*p;
X{
X    register char	*q;
X    register char	*r;
X    register char	**dp;
X
X    for (dp = Domains; dp < ENDOF(Domains); dp++)
X	for (q = p; q = Found(*dp, q); )
X	    for (r = *dp; *r; *q++ = *r++)
X		;
X}
X
X
X/*
X**  Handle route-addresses:
X**	@cruft:joe@site --> joe@site
X*/
XSTATIC void
XRouteAddr(p)
X    register char	*p;
X{
X    register char	*q;
X    register char	*r;
X
X    for (r = p; *p == '@'; p = q)
X	if ((q = IDX(p, ':')) && IDX(q, '@'))
X	    *q++ = '\0';
X	else
X	    break;
X    if (p > r)
X	/* Avoid overlaping strcpy() call. */
X	while (*r++ = *p++)
X	    ;
X}
X
X/*
X**  Does the pattern exist in the character range between p and end?
X*/
XSTATIC int
XBetween(p, pat, q)
X    register char	*p;
X    register char	*pat;
X    register char	*q;
X{
X    register char	*r;
X
X    for (r = pat + strlen(pat); p < q && r >= pat && *--q == *--r; )
X	;
X    return *r == '\0';
X}
X
X
X/*
X**  Handle the '%' syntax:
X**	joe%site.EDU@gateway.DOMAIN -> joe@site.EDU
X*/
XSTATIC void
XPercent(p)
X    register char	*p;
X{
X    register char	*q;
X    register char	*r;
X    register char	**dp;
X
X    while ((r = IDX(p, '%')) && (q = IDX(r, '@'))) {
X	for (dp = Domains; dp < ENDOF(Domains); dp++) {
X	    if (*dp == RELAY_CS_NET)
X		continue;
X	    if (Between(r, *dp, q)) {
X		*RDX(p, '@') = '\0';
X		*RDX(p, '%') = '@';
X		break;
X	    }
X	}
X	if (dp == ENDOF(Domains))
X	    break;
X    }
X}
X
X
X/*
X**  Handle CSNET, which is domainist except in some people's minds:
X**	joe%site@RELAY.CS.NET --> joe@site.CSNET
X**	joe%site.EDU@RELAY.CS.NET --> joe@site.EDU
X*/
XSTATIC void
XCsnet(p)
X    register char	*p;
X{
X    register char	*q;
X    register char	*r;
X
X    if ((q = RDX(p, '@'))
X     && strcmp(q, "@RELAY.CS.NET") == 0
X     && (r = RDX(p, '%'))) {
X	*RDX(p, '@') = '\0';
X	*r = '@';
X	if (IDX(r, '.') == NULL)
X	    Strcat(p, ".CSNET");
X    }
X}
X
X
X/*
X**  Handle hybrid "!" and "@" addresses:
X**	a!site!joe@site --> joe@site
X**	a!site!joe --> joe@site.UUCP
X**	a!site.EDU!joe --> joe@site.EDU
X*/
XSTATIC void
XHybrid(p)
X    register char	*p;
X{
X    register char	*q;
X    register char	*user;
X    char		buff[SM_SIZE];
X
X    if (user = RDX(p, '!')) {
X	*user++ = '\0';
X	if (q = IDX(user, '@'))
X	    *q = '\0';
X	q = (q = RDX(p, '!')) ? q + 1 : p;
X	Sprintf(buff, "%s@%s", user, q);
X	if (IDX(q, '.') == NULL)
X	    Strcat(buff, ".UUCP");
X	Strcpy(p, buff);
X    }
X}
X
X
X/*
X**  Handle special case for Australia:
X**	user@site.OZ --> user@site.OZ.AU
X*/
XSTATIC void
XEndpart(p)
X    register char	*p;
X{
X    register char	*r;
X
X    if ((r = RDX(p, '.')) && r[1] == 'O' && r[2] == 'Z' && r[3] == '\0')
X	Strcpy(&r[3], ".AU");
X}
X
X
X/*
X**  General address canonicalizer.
X*/
XSTATIC char *
XFixAddress(p)
X    register char	*p;
X{
X    static char		buff[1024];
X    char		host[128];
X
X    if (Local(p)) {
X	if (gethostname(host, sizeof host) < 0) {
X	    Fprintf(stderr, "%s:  Can't get my hostname, %s.\n",
X		    Pname, strerror(errno));
X	    exit(EX_TEMPFAIL);
X	}
X	Sprintf(buff, "%s@%s", p, host);
X    }
X    else {
X	Strcpy(buff, p);
X	Casify(buff);
X	dprintf("   Casify returns %s\n", buff);
X	RouteAddr(buff);
X	dprintf("RouteAddr returns %s\n", buff);
X	Percent(buff);
X	dprintf("  Percent returns %s\n", buff);
X	Csnet(buff);
X	dprintf("    Csnet returns %s\n", buff);
X	Hybrid(buff);
X	dprintf("   Hybrid returns %s\n", buff);
X	Endpart(buff);
X	dprintf("  Endpart returns %s\n", buff);
X    }
X    return buff;
X}
X
X
X/*
X**  This subroutine is a concession to the realities of the Internet and
X**  and the USENET. Much as the idea is distasteful and likely to get me
X**  in trouble, I have to hack message-ids into a format that the USENET
X**  won't choke on.  Pray that if we're doing multiple insertion point
X**  gatewaying that ALL the gateways mung exactly the same things.
X**
X**  (Death to HERMES! Death to UNIX/MM-11! Death to EAN!)
X*/
XSTATIC int
XFixMessageID(s)
X    register char	*s;
X{
X    register int	atdot;
X    register int	closed;
X
X    /* Quickie tests -- why waste time? */
X    if (*s != '<')
X	return FALSE;
X
X    for (atdot = FALSE, closed = FALSE; *++s; )
X	switch (*s) {
X	default:
X	    if (!isascii(*s) || iscntrl(*s) || isspace(*s))
X		return FALSE;
X	    break;
X	case '<':
X	    /* Already got one. */
X	    return FALSE;
X	case '>':
X	    /* I hope no one is stupid enough to quote this... */
X	    closed = TRUE;
X	    s[1] = '\0';
X	    break;
X	case '.':
X	case '@':
X	    /* We should check for a domain spec, not just either/or. */
X	    atdot = TRUE;
X	    break;
X	case '\t':
X	case ' ':
X	case '/':
X	    /* Avoid various problem characters. */
X	    *s = '.';
X	    break;
X	}
X
X    return atdot && closed;
X}
X
X
X/*
X**  Fix up the contents of In-Reply-To: fields and References: fields.
X*/
XSTATIC void
XFixReferences(hp)
X    register HBUF		*hp;
X{
X    register char		*cp;
X    register char		*ep;
X    register char		*p;
X    register char		*max;
X    char			scratch[LG_SIZE];
X
X    cp = hp->followid;
X    max = cp + strlen(cp);
X    for (p = scratch; cp = IDX(cp, '<'); ) {
X	if ((ep = IDX(cp, '>')) == NULL
X	 || ((ep - cp) + 1) > sizeof scratch - (p - scratch + 2))
X	    /* Unterminated ID, or no more room. */
X	    break;
X
X	if (FixMessageID(cp)) {
X	    if (p > scratch) {
X		*p++ = ' ';
X		*p++ = '\0';
X	    }
X	    p += APPEND(p, cp);
X	}
X	cp = ep + 2;
X	if (cp >= max)
X	    break;
X    }
X    Strcpy(hp->followid, scratch);
X}
X
X
X/*
X**  Count the number of '@' in the string.
X*/
XSTATIC int
XAtCount(s)
X    register char	*s;
X{
X    register int	n;
X
X    for (n = 0; *s; s++)
X	if (*s == '@')
X	    n++;
X    return n;
X}
X
X
X/*
X**  Canonicalize the "From:" line into the form
X**	From: local-part@domain (full-name)
X** RFC822 doesn't require the comment to be at the end of the string
X** like that.
X*/
XSTATIC void
XFixFrom(hp)
X    register HBUF		*hp;
X{
X    register char		*p;
X    register struct hostent	*host;
X    char			address[LG_SIZE];
X    char			fullname[LG_SIZE];
X    char			scratch[sizeof address];
X
X    /* We should handle "Full-Name:" too, but it doesn't get read by the
X     * news header reader. */
X    (void)CrackFrom(address, fullname, hp->from);
X#ifdef	DO_ADDRESS_CLEANUP
X    Strcpy(address, FixAddress(address));
X#endif	/* DO_ADDRESS_CLEANUP */
X
X    if (AtCount(address) != 1)
X	p = NULL;
X    else {
X	p = IDX(address, '@');
X	*p++ = '\0';
X
X#ifdef	DO_ADDRESS_CLEANUP
X	/* If we can find the host's official name use that. */
X	if (host = gethostbyname(p))
X	    p = host->h_name;
X#endif	/* DO_ADDRESS_CLEANUP */
X
X	/* We know have the canonical hostname; glue back together. */
X	Sprintf(scratch, "%s@%s", address, p);
X	Strncpy(address, scratch, sizeof address);
X	address[sizeof address - 1] = '\0';
X	p = IDX(address, '@');
X	*p++ = '\0';
X    }
X
X    /* Policy decision; what to put in the path? */
X#ifdef	FIXED_PATH
X    Strcpy(hp->path, FIXED_PATH);
X#else
X#ifdef	GATEWAY
X    Sprintf(scratch, "%s!%s!%s", GATEWAY, p, address);
X#else
X    Sprintf(scratch, "%s!%s", p, address);
X#endif	/* GATEWAY */
X    Strncpy(hp->path, scratch, sizeof hp->path);
X    hp->path[sizeof hp->path - 1] = '\0';
X#endif	/* FIXED_PATH */
X
X    /* Restore the @ if we took it out. */
X    if (p)
X	*--p = '@';
X
X    if (fullname[0]) {
X	p = address + strlen(address);
X	*p++ = ' ';
X	*p++ = '(';
X	p += APPEND(p, fullname);
X	*p++ = ')';
X	*p++ = '\0';
X    }
X
X    /* Stick the canonicalized From: back in. */
X    Strcpy(hp->from, address);
X}
X
X
X#define ERROR "\
XMessage-ID syntax error.\n\
X*** Please refer to page 23, paragraph 4.6.1. and Appendix D\n\
X*** of NIC RFC #822 for the correct syntax, and fix your mailer."
X
X/*
X** Check an RFC822 header for validity and hack it to RFC1036 spec.
X** returns NULL for everything OK, or a character pointer to an
X** error message.
X*/
Xchar *
XHackHeader(hp, SubjectRequired)
X    register HBUF		*hp;
X    int				SubjectRequired;
X{
X#ifdef	REQUIRE_MESSAGE_ID
X    /* Sendmail (almost) always has a Message-ID */
X    if (hp->ident[0] == '\0')
X	return "Message-ID header missing";
X    if (!FixMessageID(hp->ident))
X	return ERROR;
X#else
X    /* MMDF doesn't always have a Message-ID. */
X    if (hp->ident[0] && !FixMessageID(hp->ident))
X	return ERROR;
X#endif	/* REQUIRE_MESSAGE_ID */
X
X    /* Newsgroups */
X    if (hp->nbuf[0] == '\0')
X	return "Newsgroups header missing";
X
X    /* Subject */
X    if (hp->title[0] == '\0') {
X	if (SubjectRequired)
X	    return "Subject header missing";
X	Strcpy(hp->title, "(none)");
X    }
X
X    /* From */
X    if (hp->from[0] == '\0')
X	return "From header missing";
X    FixFrom(hp);
X
X    /* References and In-Reply-To */
X    if (hp->followid[0]) 
X	FixReferences(hp);
X
X    return NULL;
X}
X
X
X#ifdef	TEST
Xmain()
X{
X    char	buff[256];
X    int		i;
X
X    if (i = isatty(0))
X	(void)printf("Enter addresses:\n");
X    for ( ; ; ) {
X	if (i)
X	    (void)printf(">  ");
X	if (gets(buff) == NULL || buff[0] == '\0')
X	    break;
X	if (buff[0] != '#')
X	    (void)printf("\t%s -> %s\n\n", buff, FixAddress(buff));
X    }
X
X    exit(0);
X}
X#endif	/* TEST */
END_OF_FILE
  if test 10652 -ne `wc -c <'hdr.c'`; then
    echo shar: \"'hdr.c'\" unpacked with wrong size!
  fi
  # end of 'hdr.c'
fi
if test -f 'lex.l' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lex.l'\"
else
  echo shar: Extracting \"'lex.l'\" \(3664 characters\)
  sed "s/^X//" >'lex.l' <<'END_OF_FILE'
X/*
X**  Lexical analyzer for the gag language.  We have our own FSA
X**  for comments because it's much smaller and quicker that way.
X*/
X%{
X#include "gate.h"
X#include "gag.h"
X
X/* State of our automaton. */
Xtypedef enum _STATE {
X    S_STAR, S_NORMAL, S_END
X} STATE;
X
X/* Key-value pair. */
Xtypedef struct _PAIR {
X    char	*name;
X    int		value;
X} PAIR;
X
Xchar		yyfilename[SM_SIZE];
Xextern int	Errors;
X
X/* List of GAG's keywords. */
XSTATIC PAIR	Keywords[] = {
X    {	"default",		tDEFAULT	},
X    {	"directory",		tDIRECTORY	},
X    {	"distributions",	tDISTRIBUTIONS	},
X    {	"dotify",		tDOTIFY		},
X    {	"false",		tFALSE		},
X    {	"flags",		tFLAGS		},
X    {	"gateway",		tGATEWAY	},
X    {	"inews",		tINEWS		},
X    {	"mail2news",		tMAIL2NEWS	},
X    {	"mailcontact",		tMAILCONTACT	},
X    {	"mailhost",		tMAILHOST	},
X    {	"mailinglist",		tMAILINGLIST	},
X    {	"mailpost",		tMAILPOST	},
X    {	"moderator",		tMODERATOR	},
X    {	"news2mail",		tNEWS2MAIL	},
X    {	"organization",		tORGANIZATION	},
X    {	"owner",		tOWNER		},
X    {	"request_address",	tREQUESTADDR	},
X    {	"site",			tSITE		},
X    {	"true",			tTRUE		},
X    {	"user",			tUSER		},
X    {	NULL,			0		}
X};
X
X%}
X
X%%
X
X[-+0-9A-Za-z_.]+	{
X		    /* A simple tID or keyword. */
X		    register PAIR	*p;
X
X		    /* Keyword? */
X		    for (p = Keywords; p->name; p++)
X			if (EQ(p->name, yytext))
X			    return p->value;
X		    yylval.String = COPY(yytext);
X		    return tID;
X		}
X
X^#[ \t]+[0-9]+[ \t]+"[^\n]+"[^\n]*$	{
X		    /* C pre-processor control line. */
X		    register char	*p;
X		    char		*namep;
X
X		    /* Find the line number. */
X		    for (p = yytext; *p && !isdigit(*p); p++)
X			;
X		    /* Parse the number, find the start of the filename. */
X		    for (yylineno = atoi(p); *p && *p != '"'; p++)
X			;
X		    /* March down to the end of the filename. */
X		    for (namep = p; *++p && *p != '"'; p++)
X			;
X		    *p = '\0';
X		    (void)strncpy(yyfilename, namep, sizeof yyfilename - 1);
X		    yyfilename[sizeof yyfilename - 1] = '\0';
X		}
X
X\"[^"]*		{
X		    /* Quoted string. */
X		    int		c;
X
X		    /* See the Lex paper in Volume 2A or PS1:16
X		     * for details on this code. */
X		    if (yytext[yyleng - 1] == '\\')
X			yymore();
X		    else {
X			if ((c = input()) == '"') {
X			    yylval.String = COPY(&yytext[1]);
X			    return tID;
X			}
X			unput(c);
X			yyerror("Bad string");
X		    }
X		}
X
X"/*"	        {
X		    /* Comment. */
X		    register STATE	S;
X
X		    for (S = S_NORMAL; S != S_END; )
X			switch (input()) {
X			case '/':
X			    if (S == S_STAR) {
X				S = S_END;
X				break;
X			    }
X			    /* FALLTHROUGH */
X			default:
X			    S = S_NORMAL;
X			    break;
X			case '\0':
X			    S = S_END;
X			    break;
X			case '*':
X			    S = S_STAR;
X			    break;
X			}
X		}
X
X[ \t\n]		{
X		    /* Tasty whitespace. */
X#ifdef	lint
X		    /* I am compulsive about lint natterings. */
X		    yytext[0] = yyinput();
X		    yyoutput(yytext[0]);
X		    yyunput(yytext[0]);
X		    REJECT;
X#endif	/* lint */
X		}
X
X.		{
X		    /* Random special character. */
X		    return *yytext;
X		}
X
X%%
X
X
X/*
X**  Called by lex at end-of-stream.  Return one if no more input.
X*/
Xint
Xyywrap()
X{
X    return 1;
X}
X
X
Xyyopen(p)
X    char	*p;
X{
X    if (p == NULL)
X	(void)strcpy(yyfilename, "stdin");
X    else {
X	if ((yyin = fopen(p, "r")) == NULL) {
X	    Fprintf(stderr, "%s: Can't open \"%s\" for input, %s.\n",
X		    Pname, p, strerror(errno));
X	    exit(1);
X	}
X	(void)strcpy(yyfilename, p);
X    }
X}
X
X
X/*
X**  Write an error message.
X*/
Xyyerror(p)
X    char	*p;
X{
X    char	buff[SM_SIZE];
X
X    (void)strncpy(buff, yytext, sizeof buff);
X    buff[sizeof buff - 1] = '\0';
X    Fprintf(stderr, "\"%s\", line %d: %s (near \"%s\")\n",
X	    yyfilename, yylineno, p, buff);
X    Errors++;
X}
END_OF_FILE
  if test 3664 -ne `wc -c <'lex.l'`; then
    echo shar: \"'lex.l'\" unpacked with wrong size!
  fi
  # end of 'lex.l'
fi
if test -f 'mail-interface' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mail-interface'\"
else
  echo shar: Extracting \"'mail-interface'\" \(2823 characters\)
  sed "s/^X//" >'mail-interface' <<'END_OF_FILE'
X#! /bin/sh
X##  A portable mail interface program.  Original by Piete Brooks,
X##  modified by Rich $alz <rsalz@bbn.com>
X
Xmailer="${mailer-/usr/lib/sendmail}"
Xheaders=false
X
X##  Decode the arguments.
Xwhile test $# -gt 0 ; do
X    arg="$1"
X    shift
X    case "$arg" in
X    -A|-ASIS)
X	asis=true
X	;;
X    -b|-body)
X	body="$body
X$1"
X	shift
X	;;
X    -c|-cc)
X	cc="$cc, $1"
X	shift
X	headers=true
X	;;
X    -f|-from)
X	from="$1"
X	shift
X	headers=true
X	;;
X    -h|-help)
X	cat <<EOF
XUsage: $0 [flags] [recipients...]
X	-A -ASIS	Send text ASIS, i.e. headers are present in the input
X	-b -body	String which is to be the body of the message
X	-c -cc		Carbon Copy recipients
X	-f -from	From field
X	-h -help	This message
X	-r -recip	Recipient (passed on command line)
X	-s -subject	Set the subject field
X	-t -to		Main recipients
X	-*		ERROR
X	*		treat as recipients
XEOF
X	exit 1
X	;;
X    -r|-recip)
X	recip="$recip $1"
X	shift
X	;;
X    -s|-subject)
X	subject="$subject, $1"
X	shift
X	headers=true
X	;;
X    -t|-to)
X	to="$to, $1"
X	shift
X	headers=true
X	;;
X    -*)
X	echo $0: invalid argument \""$arg"\"
X	exit 1
X	;;
X    *)
X	to="$to, $arg"
X	;;
X    esac
Xdone
X
X##  If no recipients, send to postmaster.
Xcase "$to$cc$recip" in
X'')
X    recip=postmaster
X    ;;
Xesac
X
X##  If we got no headers on the command line, read them from the message.
Xcase $headers in
Xfalse)
X    asis=true
X    ;;
Xesac
X
X##  Strip off the spurious leading ", " in repeatable items
Xcase "$to" in
X', '*)
X    to=`expr "$to" : ", \(.*\)`
X    ;;
Xesac
Xcase "$cc"  in
X', '*)
X    cc=`expr "$cc" : ", \(.*\)`
X    ;;
Xesac
Xcase "$subject"	in ', '*)
X    subject=`expr "$subject" : ", \(.*\)`
X    ;;
Xesac
X
X##  Now do the business.
Xcase "$mailer" in
X*/sendmail|sendmail|*/sendmail' '*|sendmail' '*)
X    args="-oi"
X    if [ -z "$recip" ] ; then
X	args="$args -t"
X    fi
X    (
X	if [ ! -z "$subject" ] ; then
X	    echo "Subject: $subject"
X	fi
X	if [ ! -z "$from" ] ; then
X	    echo "From: $from"
X	fi
X	if [ ! -z "$to" ] ; then
X	    echo "To: $to"
X	fi
X	if [ ! -z "$cc" ] ; then
X	    echo "Cc: $cc"
X	fi
X	if [ -z "$asis" ] ; then
X	    echo ""
X	fi
X	if [ -z "$body" ] ; then
X	    cat
X	else
X	    echo "$body" | sed 1d
X	fi
X    ) | $debug $mailer $args $recip
X    ;;
X
X*/submit|submit|*/submit' '*|submit' '*)
X    args="-tsz"
X    case "$recip" in
X    if [ -z "$recip" ] ; then
X	if [ ! -z "$to" ] ; then
X	    args="${args}gto*"
X	fi
X	if [ ! -z "$cc" ] ; then
X	    args="${args}gcc*"
X	fi
X    fi
X    (
X	if [ ! -z "$subject" ] ; then
X	    echo "Subject: $subject"
X	fi
X	if [ ! -z "$from" ] ; then
X	    echo "From: $from"
X	fi
X	if [ ! -z "$to" ] ; then
X	    echo "To: $to"
X	fi
X	if [ ! -z "$cc" ] ; then
X	    echo "Cc: $cc"
X	fi
X	if [ -z "$asis" ] ; then
X	    echo ""
X	fi
X	if [ -z "$body" ] ; then
X	    cat
X	else
X	    echo "$body" | sed 1d
X	fi
X    ) | $debug $mailer $args $recip
X    ;;
X
X*)
X    echo Unknown mailer 1>&2
X    exit 1
X    ;;
X
Xesac
END_OF_FILE
  if test 2823 -ne `wc -c <'mail-interface'`; then
    echo shar: \"'mail-interface'\" unpacked with wrong size!
  fi
  # end of 'mail-interface'
fi
if test -f 'mail2news.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mail2news.1'\"
else
  echo shar: Extracting \"'mail2news.1'\" \(5516 characters\)
  sed "s/^X//" >'mail2news.1' <<'END_OF_FILE'
X.\" $Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/mail2news.1,v 1.11 91/02/12 14:46:17 rsalz Exp $
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.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(these flags are recognized by
X.I mail2news
Xbefore being passed on to
X.IR inews ).
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 options used by
X.I mail2news
Xare typically punctuation characters, to avoid conflicts with any letters
Xthat might be used by
X.I inews
Xor any other news-processing program.
X.PP
XAs 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 5516 -ne `wc -c <'mail2news.1'`; then
    echo shar: \"'mail2news.1'\" unpacked with wrong size!
  fi
  # end of 'mail2news.1'
fi
if test -f 'misc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'misc.c'\"
else
  echo shar: Extracting \"'misc.c'\" \(2955 characters\)
  sed "s/^X//" >'misc.c' <<'END_OF_FILE'
X/*
X**  Miscellaneous routines.
X*/
X#include "gate.h"
X#ifdef	RCSID
Xstatic char RCS[] =
X	"$Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/misc.c,v 1.7 91/02/12 14:47:53 rsalz Exp $";
X#endif	/* RCSID */
X
X
X/*
X**  Allocate memory.
X*/
Xalign_t
XMyAlloc(i)
X    int			i;
X{
X    align_t		p;
X
X    if ((p = (align_t)malloc((unsigned int)i)) == NULL) {
X	Fprintf(stderr, "%s:  Could not allocate %d bytes: %s\n",
X		Pname, i, strerror(errno));
X	exit(EX_OSERR);
X    }
X    return p;
X}
X
X
X#ifndef	HAVE_STRERROR
X/*
X**  Return a printable representation of errno.
X*/
Xchar *
Xstrerror(x)
X    int			x;
X{
X    static char		buff[20];
X
X    if (x >= 0 && x < sys_nerr)
X	return sys_errlist[x];
X    Sprintf(buff, "Error code %d", x);
X    return buff;
X}
X#endif	/* HAVE_STRERROR */
X
X
X/*
X**  Free up something that Split() made.
X*/
Xvoid
XSplitFree(p)
X    char	***p;
X{
X    if (p && *p) {
X	free((*p)[0]);
X	free((char *)*p);
X	*p = NULL;
X    }
X}
X
X
X/*
X**  This is an AWK-style split routine.  If the split character is NULL,
X**  we split on space or tab and eat long stretches of same (that is, no
X**  null fields will result).  Returns number of fields made.
X*/
Xint
XSplit(s, p, c)
X    register char	*s;
X    char		***p;
X    register char	c;
X{
X    register char	*cp;
X    register int	blank;
X    register int	n;
X    register int	i;
X
X    if (s == NULL || *s == '\0') {
X	Fprintf(stderr, "%s:  Someone was a pinhead!\n", Pname);
X	abort();
X    }
X
X    i = strlen(s) + 1;
X    cp = NEW(char, i);
X    /* This is too much -- we'll realloc what we really need later. */
X    *p = NEW(char*, i);
X
X    if (c == '\0') {
X	/* Eat multiple whitespace characters. */
X	for (blank = TRUE, n = 0; *s; s++)
X	    if (WHITE(*s)) {
X		if (!blank)
X		    *cp++ = '\0';
X		blank = TRUE;
X	    }
X	    else {
X		if (blank)
X		    (*p)[n++] = cp;
X		*cp++ = *s;
X		blank = FALSE;
X	    }
X
X	if (n == 0) {
X	    (*p)[0] = cp;
X	    SplitFree(p);
X	    return 0;
X	}
X    }
X    else
X	/* Normal case: find all occurrences of "c" and null them as field
X	 * terminators; add new string starts to the array of pointers to
X	 * strings. */
X	for ((*p)[0] = cp, n = 1; *s; s++)
X	    if (*s == c) {
X		*cp++ = '\0';
X		(*p)[n++] = cp;
X	    }
X	    else
X		*cp++ = *s;
X
X    /* Free up the excess space. */
X    *p = (char **)REALLOC(*p, (n + 1) * sizeof (char *));
X    (*p)[n] = NULL;
X    return n;
X}
X
X
X
X/*
X**  Read in a file of lines, dump it into an array of strings.
X*/
Xchar **
XReadFile(Name)
X    char		*Name;
X{
X    register FILE	*F;
X    register char	**v;
X    register char	*p;
X    register int	i;
X    char		**value;
X    char		buff[SM_SIZE];
X
X    /* Open file, count number of lines therein. */
X    if ((F = fopen(Name, "r")) == NULL) {
X	perror(Name);
X	exit(EX_OSFILE);
X    }
X    for (i = 1; fgets(buff, sizeof buff, F); i++)
X	;
X
X    rewind(F);
X    for (v = value = NEW(char*, i); fgets(buff, sizeof buff, F); ) {
X	if (p = IDX(buff, '\n'))
X	    *p = '\0';
X	if (buff[0] && buff[0] != '#')
X	    *v++ = COPY(buff);
X    }
X
X    *v = NULL;
X    return value;
X}
END_OF_FILE
  if test 2955 -ne `wc -c <'misc.c'`; then
    echo shar: \"'misc.c'\" unpacked with wrong size!
  fi
  # end of 'misc.c'
fi
if test -f 'news2mail.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'news2mail.1'\"
else
  echo shar: Extracting \"'news2mail.1'\" \(5362 characters\)
  sed "s/^X//" >'news2mail.1' <<'END_OF_FILE'
X.\" $Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/news2mail.1,v 1.7 91/02/12 14:49:43 rsalz Exp $
X.TH NEWS2MAIL 1 LOCAL
X.SH NAME
Xnews2mail \- feed a Usenet news article to mail
X.SH SYNOPSIS
X.B news2mail
X[
X.BI \-E var=value
X] [
X.B \-.
X]
Xlistname listaddr listadmin host [ article ]
X.SH DESCRIPTION
X.PP
X.I News2mail
Xreads a news article edits the headers to make it look like an RFC822 mail
Xmessage.
XIt feeds the results to
Xand feeds it to
X.IR sendmail (8),
X.IR MMDF (8),
Xor a shell script.
XIf the ``\-.'' flag is used, then the results are sent to standard output,
Xfor debugging.
X.PP
XThe article is taken from the file specified on the command line, or read
Xfrom standard input if not specified.
X.PP
XThe ``\-E'' flag can be used to set environment variables for the delivery
Xprogram that is invoked.
XThey should be of the form ``name=value'' as in
X\&``\-E mailer=/usr/lib/pp/cmds/submit''.
X.PP
X.I News2mail
Xswallows control messages,
Xremoves all but the last three entries from the ``References'' header,
Xand merges the ``From'' and ``Path'' headers to build a single
X``From'' header for the mail message (see below).
XThe ``Date,'' ``Subject,'' ``Reply\-To,'' ``Message\-ID,'' and ``Organization''
Xheaders are passed through unchanged.
XAny other headers in the original input are removed.
X.PP
XTo ``To,'' and ``Sender'' headers and the SMTP destination envelope are set
Xaccording to the parameters specified on the command line.
XThe ``To'' header is set to be \fIlistname\fP, with \fI@host\fP appended if there
Xis no atsign in \fIlistname\fP.
XThis should contain the name of the mailing list.
XThe mail is actually sent to \fIlistaddr\fP by specifying it as the
Xrecipient in the mailer command line.
X(If \fIlistaddr\fP does not have an atsign, then \fIlistaddr@host\fP is used.)
XBy treating the ``To'' header and the destination envelope separately,
Xother members of the mailing list can properly reply to the messages that
Xpass through
X.IR news2mail .
XIn addition, \fIlistaddr\fP can be an address that sends to everyone on
Xthe list except the gateway.
XThis is an optimization, however, because
X.IR inews (8)
Xwill reject any Message-ID that it has already seen.
X.PP
XThe ``Sender'' header is set to be \fIlistadmin\fP, with \fI@host\fP
Xappended if there is no atsign in \fIlistadmin\fP.
XThese values should correspond to the maintainer of the mailing list.
XSome mailers will also automatically fill in the ``Return-Path'' header
Xwith this value.
X.PP
XThe goal of the header manipulations is to send the news article to all
Xrecipient of the mailing list, but with the headers set so that delivery
Xerrors are sent to the list maintainer for disposition.
X.PP
X.IR Mail2news
Xtries to shrink the ``Path'' header by taking note of neighboring
X\s-2UUCP\s0 hosts and scanning for other Internet hosts that it can
Xshort\-circuit to.
X.IR Uuname (1)
Xis used to check for \s-2UUCP\s0 neighbors; the file
X.I uucp\-2\-inet
Xis read to map \s-2UUCP\s0 names to Internet domain names.
XFor efficiency,
X.I mail2news
Xstores
X.IR uuname 's
Xoutput in a private file, and will only call
X.I uuname
Xif the file is older than the \s-2UUCP\s0
X.IR L.sys (5)
Xfile.
X.PP
XIn order to have
X.I sendmail
Xbelieve that the headers it has created are valid,
X.I news2mail
Xmust be able to 
X.IR setuid (2)
Xto one of
X.IR sendmail 's
Xtrusted users.
X.SH EXAMPLE
X.PP
XSuppose the following article arrives
X.IR comp.sources.unix\| :
X.RS
X.nf
XPath: bbn!husc6!uunet!rsalz
XFrom: rsalz@uunet.UU.NET (Rich Salz)
XNewsgroups: comp.sources.unix
XSubject: v10INF1:  Introduction to comp.sources.unix
XMessage\-ID: <830@uunet.UU.NET>
XDate: 10 Aug 87 21:57:17 GMT
XExpires: 10 Nov 87 22:17:17 GMT
XOrganization: UUNET Communications Services, Arlington, VA
XLines: 189
XApproved: rsalz@uunet.UU.NET
X
XThis is the first of two introductory articles about comp.sources.unix.
X\&...
X.fi
X.RE
XAlso, assume the following line is in your news sys file (see
X.IR news (5)):
X.RS
X.nf
Xgateway:world,comp.sources.unix::\e
X.ti +.5i
Xnews2mail unix\-sources rsalz unix\-sources\-request bbn.com
X.fi
X.RE
XThe
X.I news2mail
Xprogram would generate the following article and feed it to
X.IR sendmail :
X.RS
X.nf
XReceived: from USENET by bbn.com with netnews
X	for rsalz@bbn.com (unix\-sources@bbn.com);
X	contact usenet@bbn.com if you have questions.
XTo: unix\-sources@bbn.com
XDate: 10 Aug 87 21:57:17 GMT
XFrom: rsalz@uunet.uu.net (Rich Salz)
XSender: unix\-sources\-request@bbn.com
XSubject: v10INF1:  Introduction to comp.sources.unix
XMessage\-ID: <830@uunet.UU.NET>
XOrganization: UUNET Communications Services, Arlington, VA
X
XThis is the first of two introductory articles about comp.sources.unix.
X\&...
X.fi
X.RE
X.SH DIAGNOSTICS
XMost return values from I/O routines are checked; if anything goes
Xwrong, a diagnostic message is sent to a log file.
XThis file will occasionally have to be examined and trimmed.
X.SH FILES
X.ta \w'news2mail.out  'u
Xnews2mail.out	Error log (standard error)
X.br
Xuucp\-2\-inet	Mapping of ``Path'' names to Internet hostnames.
X.br
Xuuname.out	Last output from
X.IR uuname .
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), mail2news(1L).
X.SH AUTHOR
XRich $alz <rsalz@bbn.com>, after a set of scripts and a excellent manpage by
X.br
XErik E. Fair <fair@apple.com>.
X.br
XPiete Brooks <pb@computer-lab.cambridge.ac.uk> helped write the option-handling.
END_OF_FILE
  if test 5362 -ne `wc -c <'news2mail.1'`; then
    echo shar: \"'news2mail.1'\" unpacked with wrong size!
  fi
  # end of 'news2mail.1'
fi
if test -f 'signoff.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'signoff.c'\"
else
  echo shar: Extracting \"'signoff.c'\" \(3327 characters\)
  sed "s/^X//" >'signoff.c' <<'END_OF_FILE'
X/*
X**  SIGNOFF
X**  Quickie hack to see of a mail message is a "please drop me" request.
X**  Exit's with status 0 if it is a regular message, or 1 if it appears to
X**  be a one of this administrative requests that annoy everyone when
X**  they're sent to the whole list.  Optional first argument is the name
X**  of the input file, or stdin is read if none is given.
X**
X**  Original program written by Russ Nelson, <nelson@clutx.clarkson.edu>.
X**  Severely hacked on by Rich $alz, <rsalz@bbn.com>.  This program is
X**  basically incorporated into mail2news; it's pulled out here for
X**  demonstration.
X**
X**  For example, here is a script that could be used as a wrapper around
X**  the mail2news program:
X**	#! /bin/sh
X**	cat >/tmp/signoff$$
X**	if /usr/lib/news/signoff </tmp/signoff$$ ; then
X**	    /usr/ucb/Mail -s "Sign on/off request" usenet </tmp/signoff$$
X**	else
X**	    /usr/lib/news/mail2news.real </tmp/signoff$$
X**	fi
X**	exec rm /tmp/signoff$$
X**
X**  Russ ran the following test for a month and got only one real message
X**  (a gnu.gcc bug report) treated as a subscription request.  He doesn't
X**  know how many subs made it out to the net, tho:  that would entail
X**  reading ALL netnews articles -- yuck!
X**
X**  Put the following line in your news sys file:
X**	allmail:all::/usr/lib/news/ts
X**  here is the "ts" script:
X**	#! /bin/sh
X**	cat >/tmp/ts.$$
X**	/usr/lib/news/signoff </tmp/ts.$$ || mail usenet </tmp/ts.$$
X**	exec rm /tmp/ts.$$
X**
X**  Perhaps a better way to test is to make the test less conservative,
X**  and see what "real" articles get caught, and make adjustments then?
X**  Comments solicited.
X*/
X#include <stdio.h>
X#include <ctype.h>
X
X#define EQ(a, b)		(strcmp((a), (b)) == 0)
X
X
Xmain(ac, av)
X    int			ac;
X    char		*av[];
X{
X    register FILE	*F;
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[128];
X
X    /* Get input. */
X    if (ac < 2)
X	F = stdin;
X    else if ((F = fopen(av[1], "r")) == NULL) {
X	(void)fprintf(stderr, "%s: cannot open %s\n", av[0], av[1]);
X	exit(1);
X    }
X
X    /* Skip headers by waiting for the first blank line. */
X    while (fgets(word, sizeof word, F) && *word != '\n')
X	    ;
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	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
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
X#ifdef	DEBUG
X    printf("%s: %d words, %d drop, %d mail --> %s\n",
X	   av[1] ? av[1] : "<stdin>",
X	   count, drop_or_add, mail_word,
X	   c ? "yes" : "no");
X#endif	/* DEBUG */
X
X    /* Exit appropriately. */
X    exit(c ? 0 : 1);
X}
END_OF_FILE
  if test 3327 -ne `wc -c <'signoff.c'`; then
    echo shar: \"'signoff.c'\" unpacked with wrong size!
  fi
  # end of 'signoff.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
if test -f 'uucp-2-inet' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'uucp-2-inet'\"
else
  echo shar: Extracting \"'uucp-2-inet'\" \(3301 characters\)
  sed "s/^X//" >'uucp-2-inet' <<'END_OF_FILE'
X##  This file maps UUCP names to Internet names.
X##  Blank lines, and lines starting with # are ignored.
X##  $Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/uucp-2-inet,v 1.2 87/10/01 14:06:50 rsalz Release1 $
X##  (I added uunet and bbn.)
Xames			ames.arpa
Xaurora			ames-aurora.arpa
Xkestrel			kestrel.arpa
Xlll-crg			lll-crg.arpa
Xlll-lcc			lll-lcc.arpa
Xstyx			lll-tis-b.arpa
Xcmcl2			nyu.arpa
Xyale			yale.arpa
Xrandvax			rand-unix.arpa
Xosu-eddie		ohio-state.arpa
Xuunet			uunet.uu.net
X
Xcunyvm.bitnet		cunyvm.bitnet
Xpsuvm.bitnet		psuvm.bitnet
X
Xmcnc			mcnc.org
X
Xbrl-smoke		smoke.brl.mil
Xbrl-sem			sem.brl.mil
Xbrl-adm			adm.brl.mil
Xcod			cod.nosc.mil
Xmarlin			marlin.nosc.mil
Xmplvax			mplvax.nosc.mil
Xnoscvax			bass.nosc.mil
X
Xelroy			elroy.jpl.nasa.gov
Xseismo			seismo.css.gov
Xlanl			lanl.gov
Xmordor			mordor.s1.gov
Xhc			hc.dspo.gov
X
Xbbn			bbn.com
Xhplabs			hplabs.hp.com
Xdecwrl			decwrl.dec.com
Xbellcore		bellcore.bellcore.com
Xpetrus			petrus.bellcore.com
XDiamond			diamond.bbn.com
Xsun			sun.com
Xsri-spam		spam.istc.sri.com
Xsri-unix		unix.sri.com
Xthink			think.com
Xburdvax			burdvax.prc.unisys.com
Xbigburd			bigburd.prc.unisys.com
Xparcvax			parcvax.xerox.com
X
Xarizona			arizona.edu
XShasta			shasta.stanford.edu
Xacf2			acf2.nyu.edu
Xacf3			acf3.nyu.edu
Xacf4			acf4.nyu.edu
Xcsd2			csd2.nyu.edu
Xmit-athena		athena.mit.edu
Xathena.mit.edu		athena.mit.edu
Xbatcomputer		tcgould.tn.cornell.edu
Xbu-cs			bu-cs.bu.edu
Xbucsb.bu.edu		bucsb.bu.edu
Xcaip			caip.rutgers.edu
Xcit-vax			csvax.caltech.edu
Xcit-vlsi		vlsi.caltech.edu
Xcornell			cu-arpa.cs.cornell.edu
Xcvl			cvl.umd.edu
Xendor			endor.harvard.edu
Xeneevax			eneevax.umd.edu
Xernie.Berkeley.EDU	ernie.berkeley.edu
Xglacier			glacier.stanford.edu
Xgvax			gvax.cs.cornell.edu
Xgymble			gymble.umd.edu
Xh.cc.purdue.edu		h.cc.purdue.edu
Xprinceton		princeton.edu
Xharvard			harvard.harvard.edu
Xhusc4			husc4.harvard.edu
Xhusc6			husc6.harvard.edu
Xoddjob			oddjob.uchicago.edu
Xgargoyle		gargoyle.uchicago.edu
Xsphinx			sphinx.uchicago.edu
Xim4u			im4u.utexas.edu
Xius2.cs.cmu.edu		ius2.cs.cmu.edu
Xsei.cmu.edu		sei.cmu.edu
Xj.cc.purdue.edu		j.cc.purdue.edu
Xll-xn			xn.ll.mit.edu
Xmimsy			mimsy.umd.edu
Xmit-amt			media-lab.media.mit.edu
Xmit-athena		athena.mit.edu
Xmit-eddie		eddie.mit.edu
Xmit-hermes		hermes.ai.mit.edu
Xmit-prep		prep.ai.mit.edu
Xmit-trillian		trillian.mit.edu
Xprep.ai.mit.edu		prep.ai.mit.edu
Xpt.cs.cmu.edu		pt.cs.cmu.edu
Xpur-ee			ee.ecn.purdue.edu
Xpurdue			purdue.edu
Xriacs			icarus.riacs.edu
Xrice			rice.edu
Xrochester		cs.rochester.edu
Xrover.ri.cmu.edu	rover.ri.cmu.edu
Xrutgers			rutgers.rutgers.edu
Xsdcsvax			sdcsvax.ucsd.edu
Xspice.cs.cmu.edu	spice.cs.cmu.edu
Xsvax			svax.cs.cornell.edu
Xtalcott			talcott.harvard.edu
Xtopaz			topaz.rutgers.edu
Xucbcad			cad.berkeley.edu
Xucbvax			ucbvax.berkeley.edu
Xucla-cs			locus.ucla.edu
Xuiucdcs			a.cs.uiuc.edu
Xuiucdcsb		b.cs.uiuc.edu
Xuiucdcsc		c.cs.uiuc.edu
Xuiucdcsm		m.cs.uiuc.edu
Xuiucdcsp		p.cs.uiuc.edu
Xuiucuxc			uxc.cso.uiuc.edu
Xumcp-cs			mimsy.umd.edu
Xumd5			umd5.umd.edu
Xoberon			usc-oberon.usc.edu
Xut-ngp			ngp.utexas.edu
Xut-sally		sally.utexas.edu
Xutah-cs			cs.utah.edu
Xutah-gr			gr.utah.edu
Xutastro			astro.as.utexas.edu
Xuw-beaver		beaver.cs.washington.edu
Xuw-june			june.cs.washington.edu
Xuwmacc			unix.macc.wisc.edu
Xuwvax			rsch.wisc.edu
Xwjh12			wjh12.harvard.edu
X
XCS.UCL.AC.UK		cs.ucl.ac.uk
Xcs.ucl.ac.uk		cs.ucl.ac.uk
END_OF_FILE
  if test 3301 -ne `wc -c <'uucp-2-inet'`; then
    echo shar: \"'uucp-2-inet'\" unpacked with wrong size!
  fi
  # end of 'uucp-2-inet'
fi
echo shar: End of archive 3 \(of 4\).
cp /dev/null ark3isdone
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...
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.