[news.software.b] New! Improved! recnews.

matthew@sunpix.East.Sun.COM (Matthew Stier - Sun Visualization Products) (10/30/90)

New?  Yes.
Improved?  Well I think so.

Enclosed is a major revision of the Bnews `recnews' program.  I went thru
the hassle of revising it after upgrading my site to use an nntp feed, and
found out that the mini-inews program provided with nntp does not know
command-line arguments.

The enclosed shar contains a new recnews.c and new man-page (recnews.8).

The old recnews works by stripping the mail header off incoming articles,
and feeding the body of the article to inews with command-line arguments 
for header lines such as "From: ", "Subject: " and "Newsgroups: ".  

The new recnews works by stripping the mail header, and replacing it with
a header constructed of just "From: ", "Subject: ", "Newsgroups: " and
"Distribution: " lines.  This new header, and the body of the article are
feed to inews.

 
---- Cut Here and unpack ----
#!/bin/sh
# shar:	Shell Archiver  (v1.25)
#	Packed Mon Oct 29 15:51:29 EST 1990 by matthew@east.sun.com
#	from directory /home/matthew
#
#	Run the following text with /bin/sh to create:
#	  recnews.8
#	  recnews.c
#
echo "x - extracting recnews.8 (Text)"
sed 's/^X//' << 'SHAR_EOF' > recnews.8 &&
X.TH RECNEWS 8 "October 29, 1990"
X.SH NAME
Xrecnews \- receive unprocessed articles via mail
X.SH SYNOPSIS
X.BR recnews " [ "
X.IR newsgroups " [ " distribution " [ " from " ] ] ] "
X.SH DESCRIPTION
X.I Recnews
Xreads a letter from the standard input; determines the letter's sender,
Xsubject, newsgroup and distribution; and feeds the body of the letter, 
Xwith a new header, to inews for insertion.
X.PP
XIf
X.I newsgroup
Xis omitted, the letter's "Newsgroups:" line, and then the "To:" line  
Xwill be used to determine the destination newsgroup(s).
X.I Newsgroups
Xmay be a single, or comma seperated line of newsgroups.
X.PP
XIf
X.I distribution
Xis omitted, the letter's "Distribution:" line will be used to determine
Xthe articles scope of distribution. 
X.PP
XIf
X.I from
Xis omitted, the letter's "From:" line, and then "From" or ">From" line 
Xwill be used to determine the originator of the article.
X.PP
X.SH SEE ALSO
Xinews(8),
Xsendnews(8),
SHAR_EOF
chmod 0644 recnews.8 || echo "restore of recnews.8 fails"
echo "x - extracting recnews.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > recnews.c &&
X/*
X * recnews [newsgroup] [distribution] [from]
X *
X * Process a news article submitted via mail. Such articles are in 
X * normal mail format and have never seen the insides of netnews.  
X *
X * If "newsgroup" is included, the article is posted to this newsgroup 
X * instead of trying to intuit it from the headers. This argument is 
X * postions dependent.  If you want to specify "distribution" or "from"
X * and not "newsgroup" this postion will need to be filled with a null 
X * string. (either ""  or '')
X *
X * If "distribution" is included, the article is posted with this 
X * distribution, vice the distribution listed in the article header.
X * This argument is postions dependent. If you want to specify "from"
X * and not "distribution" this postion will need to be filled with a null 
X * string. (either ""  or '')
X *
X * If "from" is included, the return address is forged to look like that
X * user instead of what the "From:" line says.
X *
X * It is recommended that you always include the "newsgroup", since the
X * intuition code can be flakey.  The "from" is probably appropriate
X * in some circumstances to control e-mail replies.
X *
X * Sample lines in /etc/aliases
X *
X *	worldnews: "|/usr/lib/news/recnews general"
X *		Allows you to mail to worldnews rather than using inews.
X *		Intended for humans to mail to.
X *
X *	post-unix-wizards: "|/usr/lib/news/recnews ba.unix-wizards ba"
X *		Causes mail to post-unix-wizards to be fed into ba.unix-wizards
X *		and the distribution limited to 'ba'.
X *
X *	in-gamemasters: "|/usr/lib/news/recnews mail.gamemasters '' dm"
X *		Causes mail to in-gamemasters to be fed into mail.gamemasters
X *		and the return address to be changed to user 'dm'.
X *
X * Recnews is primarily useful in remote places on the usenet which collect
X * mail from mailing lists and funnel them into the network.  It is also
X * useful if you like to submit articles by e-mail (Many mailers give
X * you nice facilities for editing the message.)  It is not, however,
X * essential to use recnews to be able to join usenet.
X *
X * WARNING: recnews disables the "recording" check - it has to because
X * by the time inews is run, it's in the background and too late to
X * ask permission.  If you depend heavily on recordings you probably
X * should not allow recnews (and thus the mail interface) to be used.
X *
X * 1) The from, to and subject lines are left alone - except for double quotes
X *    and backslashes which are escaped to protect them from shell parsing
X * 2) We give precedence to "From:" over "From" or ">From" in determining
X *    who the article is really from.
X *
X *    Modifications by rad@tek
X *
X * mstier@east.sun.com: (02/09/90)
X *	fixed the double_quote_at_end_of_line and unprotected backslash
X *	problems. fixed error that assumed a minimum length of data read
X *	by fgets(). removed optional third and fourth arguments added by 
X *	John@ODU.EDU, and pleasant@rutgers; recnews now passes all other 
X *	command-line arguments to inews. consolidated redundant code and
X *	stripped unnecessary code.
X *
X * mstier@east.sun.com: (10/29/90)
X *	fixed recnews to work with the nntp mini-inews. another large 
X *	change to direct feed the header as part of the article, vice
X *	command-line arguments.
X *
X */
X
X/*
X * mstier@sun.com: (02/09/90)
X *	A major hack of recnews.c not really v2.20 anymore, (may be 2.30, 
X *	or 3.00) but I don't control the revision number.
X * mstier@sun.com: (02/09/90)
X *	ditto
X *
X * static char	*SccsId = "@(#)recnews.c	2.20	1/17/89";
X */
X
X#ifdef SCCSID
Xstatic char	*SccsId = "@(#)recnews.c  mstier@sun.com  ?.??  10/29/90";
X#endif /* SCCSID */
X
X#include "params.h"
X
X/*
X * Note: we assume there are 2 kinds of hosts using recnews:
X * Those that have delivermail (and hence this program will never
X * have to deal with more than one message at a time) and those on the arpanet
X * that do not (and hence all messages end with a sentinel).  It is
X * supposed that regular v7 type systems without delivermail or some
X * other automatic forwarding device will just use rnews.  We do
X * not attempt to tell where a message ends on all systems due to the
X * different conventions in effect.  (This COULD be fixed, I suppose.)
X */
X
X/*
X * Function declarations
X */
Xint  main();
Xint  type();
Xvoid argcpy();
XFILE *pipeopen();
Xint  pipeclose();
X
X/*
X * Kinds of lines in a message.
X */
X#define TO		0	/* To: line */
X#define FROM		1	/* From: line */
X#define SENDER		2	/* From  line */
X#define SUBJECT		3	/* Subject: line */
X#define NEWSGROUPS	4	/* Newsgroups: line */
X#define DISTRIBUTION	5	/* Distribution: line */
X#define HEADER		6	/* any unrecognized header */
X#define BLANK		7	/* blank line */
X#define TEXT		8	/* anything unrecognized */
X#define EOM		9	/* End of message (4 ctrl A's) */
X
X/*
X * Define 'state' and possible states program can be in.
X */
X#define SKIPPING	1	/* In header of message */
X#define READING		2	/* In body of message */
X
X#define BFSZ 256
X
X#define EOT	'\004'
X
X/*
X * Header buffers
X */
Xchar	to[BFSZ];
Xchar	from[BFSZ];
Xchar	sender[BFSZ];
Xchar	subject[BFSZ];
Xchar	newsgroups[BFSZ];
Xchar	distribution[BFSZ];
X
Xextern char *strcat(), *strcpy(), *strpbrk(), *strchr(), *sprintf();
X
Xint
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	char buf[BFSZ], inews[BFSZ];
X	FILE *pipe = NULL;
X	int state;
X
X
X	/* build inews command */
X#ifdef LOGDIR
X	(void)sprintf(inews, "%s/%s/%s", logdir(HOME), LIBDIR, "inews -h");
X#else	/* !LOGDIR */
X	(void)sprintf(inews, "%s/%s", LIBDIR, "inews -h");
X#endif	/* !LOGDIR */
X
X	/* Parse the command-line args */
X	if (argc > 1 && argv[1][0])
X		(void)strcpy(newsgroups, argv[1]);
X	else
X		(void)strcpy(newsgroups, "");
X		
X	if (argc > 2 && argv[2][0])
X		(void)strcpy(distribution, argv[2]);
X	else
X		(void)strcpy(distribution, "");
X		
X	if (argc > 3 && argv[3][0])
X		(void)strcpy(from, argv[3]);
X	else
X		(void)strcpy(from,"");
X
X	/* Initialize the remaining buffers */
X	(void)strcpy(to, "");
X	(void)strcpy(sender, "");
X	(void)strcpy(subject, "");
X
X	/* Parse the article */
X	state = SKIPPING;
X	while (fgets(buf, BFSZ, stdin) != NULL) {
X		if (state == READING) {
X			fputs(buf,pipe);
X			continue;
X		}
X		switch (type(buf)) {
X
X		case TO:
X			if (!to[0])
X				argcpy(to, buf, 3);
X			break;
X
X		case FROM:
X			if (!from[0])
X				argcpy(from, buf, 5);
X			break;
X
X		case SENDER:
X			if (!sender[0])
X				argcpy(sender, buf, 4);
X			break;
X
X		case SUBJECT:
X			if (!subject[0])
X				argcpy(subject, buf, 8);
X			break;
X
X		case NEWSGROUPS:
X			if (!newsgroups[0])
X				argcpy(newsgroups, buf, 11);
X			break;
X
X		case DISTRIBUTION:
X			if (!distribution[0])
X				argcpy(distribution, buf, 13);
X			break;
X
X		case HEADER:
X			break;
X
X		case BLANK:
X			state = BLANK;
X		case TEXT:
X#ifdef debug
X			pipe = stdout;
X#else
X			pipe = pipeopen(inews);
X			if (pipe == NULL) {
X				perror("recnews: pipeopen failed");
X				exit(1);
X			}
X#endif
X			(void)fprintf(pipe, "From: %s\n",
X				from[0] ? from : sender);
X			(void)fprintf(pipe, "Subject: %s\n",
X				subject[0] ? subject : "(none)");
X			(void)fprintf(pipe, "Newsgroups: %s\n",
X				newsgroups[0] ? newsgroups : to);
X			(void)fprintf(pipe, "Distribution: %s\n",
X				distribution[0] ? distribution : "");
X			if (state != BLANK)
X				fputs(buf,pipe);
X			state = READING;
X			break;
X		}
X	}
X	(void)pipeclose(pipe);
X	exit(0);
X	/*NOTREACHED*/
X}
X
X/*
X * What type of line was just read by 'fgets()'.
X *
X */
Xint
Xtype(p)
Xregister char *p;
X{
X	char *firstbl;
X	static char lasthdr = 1;		/* prev line was a header */
X
X	if ((*p == ' ' || *p == '\t') && lasthdr)
X		return(HEADER);		/* continuation line */
X	firstbl = strpbrk(p, " \t");
X	while (*p == ' ' || *p == '?' || *p == '\t')
X		++p;
X
X	if (*p == '\n' || *p == 0)
X		return(BLANK);
X	if (STRNCMP(p, "To", 2)==0)
X		return(TO);
X	if (STRNCMP(p, "From:", 5) == 0)
X		return(FROM);
X	if (STRNCMP(p, ">From", 5) == 0 || STRNCMP(p, "From", 4) == 0)
X		return(SENDER);
X	if (STRNCMP(p, "Subject:", 8) == 0 || STRNCMP(p, "Subj", 4) == 0 || 
X	    STRNCMP(p, "Re:", 3) == 0 || STRNCMP(p, "re:", 3) == 0)
X		return(SUBJECT);
X	if (STRNCMP(p, "Distribution:", 13)==0)
X		return(DISTRIBUTION);
X	if (STRNCMP(p, "\1\1\1\1", 4)==0)
X		return(EOM);
X	if (firstbl && firstbl[-1] == ':' && isalpha(*p))
X		return(HEADER);
X	lasthdr = 0;
X	return(TEXT);
X}
X
X/*
X * Copy the src to dest, modifying as necessary.
X *
X */
Xvoid
Xargcpy(dest, src, index)
Xchar *dest;
Xchar *src;
Xint index;
X{
X	char *orig_src  = src;
X	char *orig_dest = dest;
X
X	/* Search for the first whitespace */
X	while (*src != ' ' && *src != '\t' && *src != NULL)
X		src++;
X
X	/* Search for the next non-whitespace */
X	while (*src == ' ' || *src == '\t')
X		src++;
X
X	/* If whitespace not found, use index and guess at start of line */
X	if (src == NULL) 
X		if (strlen(orig_src) > index)
X			src = orig_src + index;
X		else
X			src = orig_src + strlen(src);
X			
X	/* Copy the src to the dest adding backspaces where needed */
X	do {
X		if (*src == '"' || *src == '\\')
X			*dest++ = '\\';
X	} while (*dest++ = *src++);
X
X	/* and null terminate the string */
X	if (src = strchr(orig_dest, '\n'))
X		*src = '\0';
X}
X
X/*
X *	This is similar to popen(), but made more secure.  Rather 
X *	than forking off a shell, you get a bare process.
X *	You can use "" to get white space into an argument, but 
X *	nothing else is recognized
X */
X
X#define	RDR	0
X#define	WTR	1
X#define MAXARGS	20
Xstatic	int	mopen_pid[20];
X
XFILE *
Xpipeopen(cmd)
Xregister char *cmd;
X{
X	int p[2];
X	register myside, hisside, pid;
X
X	if(pipe(p) < 0)
X		return(NULL);
X	myside = p[WTR];
X	hisside = p[RDR];
X	if ((pid = vfork()) == 0) {
X		char *args[MAXARGS];
X		register char **ap = args;
X
X		/* myside and hisside reverse roles in child */
X		(void) close(myside);
X		(void) close(0);
X		(void) dup(hisside);
X		(void) close(hisside);
X		(void) setgid(getgid());
X		(void) setuid(getuid());
X
X		while (isspace(*cmd))
X			cmd++;
X
X		while (*cmd != '\0') {
X			*ap++ = cmd;
X			if (ap >= &args[MAXARGS]) {
X				(void)fprintf(stderr, "Too many args to %s", 
X					args[0]);
X				_exit(2);
X			}
X			while (*cmd && !isspace(*cmd)) {
X				if (*cmd++ == '"') {
X					register char *bcp = cmd-1;
X					while (*cmd) {
X						if(*cmd == '\\') {
X							cmd++;
X						} else if (*cmd == '"')
X							break;
X						*bcp++ = *cmd++;
X					}
X					*bcp = '\0';
X					cmd++;
X				}
X			}
X			if (*cmd)
X				*cmd++ = '\0';
X			while (isspace(*cmd))
X				cmd++;
X		}
X		*ap = (char *)NULL;
X
X		execv(args[0], args);
X		perror("pipeopen exec:");
X		_exit(1);
X	}
X
X	if(pid == -1)
X		return(NULL);
X
X	mopen_pid[myside] = pid;
X	(void) close(hisside);
X	return(fdopen(myside, "w"));
X}
X
X/*
X * Close the pipe opened by 'pipeopen()'.
X *
X */
Xint
Xpipeclose(ptr)
XFILE *ptr;
X{
X	register int f, r;
X	SIGNAL_TYPE hstat, istat, qstat;
X	int status;
X
X	f = fileno(ptr);
X	(void) fclose(ptr);
X	istat = signal(SIGINT, SIG_IGN);
X	qstat = signal(SIGQUIT, SIG_IGN);
X	hstat = signal(SIGHUP, SIG_IGN);
X	while((r = wait(&status)) != mopen_pid[f] && r != -1)
X		;
X	if(r == -1)
X		status = -1;
X	(void)signal(SIGINT, istat);
X	(void)signal(SIGQUIT, qstat);
X	(void)signal(SIGHUP, hstat);
X	return(status);
X}
X
SHAR_EOF
chmod 0755 recnews.c || echo "restore of recnews.c fails"
exit 0

-- 
Matthew Lee Stier    (mstier@east.Sun.COM)   |
Sun Microsystems ---  RTP, NC  27709-3447    |     "Wisconsin   Escapee"
uucp:  sun!mstier or mcnc!rti!sunpix!matthew |
phone: (919) 469-8300 fax: (919) 460-8355    |