4.3BSD/usr/contrib/news/src/ifuncs.c

Compare this file to the similar file:
Show the results in this format:

/*
 * This software is Copyright (c) 1986 by Rick Adams.
 *
 * Permission is hereby granted to copy, reproduce, redistribute or
 * otherwise use this software as long as: there is no monetary
 * profit gained specifically from the use or reproduction or this
 * software, it is not sold, rented, traded or otherwise marketed, and
 * this copyright notice is included prominently in any copy
 * made.
 *
 * The author make no claims as to the fitness or correctness of
 * this software for any use whatsoever, and it is provided as is. 
 * Any use of this software is at the user's own risk.
 *
 * ifuncs - functions used by inews.
 */

#ifdef SCCSID
static char	*SccsId = "@(#)ifuncs.c	2.51	3/19/86";
#endif /* SCCSID */

#include "iparams.h"
#include <errno.h>
#include <ctype.h>

/*LINTLIBRARY*/

#define AFSIZ	4000	/* size of text in the active file for initial malloc */

/*
 * Transmit this article to all interested systems.
 */

#ifdef u370
static struct srec srec;
#endif /* u370 */

static struct hbuf h, hh;

#ifdef MULTICAST
#define	MAXMCAST	20
#define	MAXMCS		10

struct multicast {
	char mc_name[SBUFLEN];		/* "multi-cast" name */
	short mc_syscnt;
	char mc_tosys[MAXMCAST][SBUFLEN];
} mcast[MAXMCS];

static int mccount;
#endif /* MULTICAST */

#ifndef DBM
char *histfile();
#endif /* !DBM */

broadcast()
{
	register char *hptr;
	register char *sptr;
	register FILE *fp;
#ifndef u370
	struct srec srec;
#endif
	char sentbuf[LBUFLEN];
	int nsent = 0;
	char *sentsys;

	/* h is a local copy of the header we can scribble on */
	fp = xfopen(ARTICLE, "r");
	if (hread(&h, fp, TRUE) == NULL)
		xerror("Cannot reread article");
	(void) fclose(fp);

	(void) strcpy(sentbuf, h.ident);
	(void) strcat(sentbuf, " sent to ");
	sentsys = index(sentbuf, 0);
	nsent = 0;
	/* break path into list of systems. */
	sptr = hptr = h.path;
	while ((hptr=strpbrk(hptr, NETCHRS)) != NULL) {
		*hptr++ = '\0';
		sptr = hptr;
	}
	*sptr = '\0';

#ifdef MULTICAST
	mccount = 0;
#endif /* MULTICAST */

	/* loop once per system. */
	s_openr();
	while (s_read(&srec)) {
#ifdef HIDDENNET
		if (strncmp(srec.s_name, LOCALSYSNAME, SNLN) == 0)
			continue;
#endif /* HIDDENNET */
		if (strncmp(srec.s_name, FULLSYSNAME, SNLN) == 0)
			continue;
		if (sptr = srec.s_nosend) {
			while (*sptr) {
				while (*sptr && *sptr != ',')
					sptr++;
				if (*sptr == ',')
					*sptr++ = '\0';
			}
			*++sptr = '\0';
		}
		hptr = h.path;
		while (*hptr != '\0') {
			if (strncmp(srec.s_name, hptr, SNLN) == 0)
				goto contin;
			if (sptr = srec.s_nosend) {
				while (*sptr != '\0') {
					if (strncmp(sptr, hptr, SNLN) == 0)
						goto contin;
					while (*sptr++)
						;
				}
			}
			while (*hptr++ != '\0')
				;
		}
		if (!ngmatch(h.nbuf, srec.s_nbuf))
			continue;
		if (h.distribution[0] != '\0' &&
			!ngmatch(h.distribution, srec.s_nbuf) &&
			!ngmatch(srec.s_nbuf, h.distribution))
			    continue;
		if (nsent) {
			hptr = sentsys;
			while ((sptr = index(hptr, ',')) != NULL) {
				*sptr = '\0';
				if (strcmp(hptr, srec.s_name) == 0) {
					*sptr = ',';
					goto contin;
				}
				*sptr++ = ',';
				for (hptr = sptr; isspace(*hptr); hptr++)
					;
			}
			if (strcmp(hptr, srec.s_name) == 0)
				continue;
		}
		/* now we've found a system to send this article to */
#ifdef MULTICAST
		if (index(srec.s_flags, 'M')) {
			/* do a "multi-cast" transmit */
			register struct multicast *m;

			if (strlen(srec.s_name) >= SBUFLEN ||
			    strlen(srec.s_xmit) >= SBUFLEN)
				xerror("system name too long for multicast");
			for (m = mcast; m < &mcast[mccount]; m++)
				if (strcmp(srec.s_xmit, m->mc_name) == 0)
					break;
			if (m >= &mcast[MAXMCS])
				xerror("Too many multicasts");
			if (m == &mcast[mccount]) {
				mccount++;
				m->mc_syscnt = 0;
				strcpy(m->mc_name, srec.s_xmit);
			}
			if (m->mc_syscnt >= MAXMCAST)
				xerror("Too many systems for multicast");
			strcpy(m->mc_tosys[m->mc_syscnt++], srec.s_name);
		} else {
			register struct multicast *m;
			register char **yptr;
			char *sysptrs[MAXMCAST];
			int mc;

			mc = 0;
			for (m = mcast; m < &mcast[mccount]; m++)
				if (strcmp(m->mc_name, srec.s_name) == 0) {
					yptr = sysptrs;
					while (mc < m->mc_syscnt)
						*yptr++ = m->mc_tosys[mc++];
					break;
				}
			if (!transmit(&srec,xfopen(ARTICLE,"r"),1,sysptrs,mc))
				continue;
		}
#else /* !MULTICAST */
		if (!transmit(&srec, xfopen(ARTICLE, "r"), 1, (char **)0, 0))
			continue;
#endif /* !MULTICAST */
		if (nsent)
			(void) strcat(sentbuf, ", ");
		(void) strcat(sentbuf, srec.s_name);
		nsent++;
	contin:;
	}
	if (nsent)
		log(sentbuf);
	s_close();
}

/*
 * Transmit file to system.
 */
#define PROC 0004
#ifndef MULTICAST
/* ARGSUSED */
#endif /* !MULTICAST */
transmit(sp, ifp, maynotify, sysnames, mc)
register struct srec *sp;
register FILE *ifp;
int maynotify;
char **sysnames;
int mc;
{
	register FILE *ofp;
	register int c;
	register char *ptr;
	char TRANS[BUFLEN];
	char *argv[20];
	register int pid;
	extern char firstbufname[];

/* A:	afmt: the other machine runs an A news, so we xmit in A format */
	int afmt = (index(sp->s_flags, 'A') != NULL);
/* B:	use B format (this is the default - don't use this letter elsewise). */
/* F:	append name to file */
	int appfile = (index(sp->s_flags, 'F') != NULL);
/* L:	local: don't send the article unless it was generated locally */
	int local = ((ptr = index(sp->s_flags, 'L')) != NULL);
/* H:	interpolate history line into command, use existing file */
	int history = (index(sp->s_flags, 'H') != NULL);
/* M:	multi-cast: this is taken care of above, but don't reuse flag */
#ifdef MULTICAST
/* O:	multi-cast only, don't send article if not multicast hosts */
	int multisend = (index(sp->s_flags, 'O') != NULL);
#endif /* MULTICAST */
/* N:	notify: don't send the article, just tell him we have it */
	int notify = maynotify && (index(sp->s_flags, 'N') != NULL);
/* S:	noshell: don't fork a shell to execute the xmit command */
	int noshell = (index(sp->s_flags, 'S') != NULL);
/* U:	useexist: use the -c option to uux to use the existing copy */
	int useexist = (index(sp->s_flags, 'U') != NULL);

	if (local && mode == PROC) {
		local = 0;
		while (isdigit(*++ptr))
			local = local * 10 + *ptr - '0';
		for (ptr = h.path; *ptr != '\0' && local >= 0; local--)
			while (*ptr++ != '\0')
				;
		if (local < 0) {
			(void) fclose(ifp);
			return FALSE;
		}
	}

#ifdef DEBUG
	printf("Transmitting to '%s'\n", sp->s_name);
#endif /* DEBUG */

#ifdef MULTICAST
	if (multisend && mc == 0) {
		(void) fclose(ifp);
		return FALSE;
	}
#endif /* MULTICAST */

	if (!appfile && !useexist && !history) {
		if (!hread(&hh, ifp, TRUE)) {
			logerr("Bad header, not transmitting %s re %s to %s",
				hh.ident, hh.title, sp->s_name);
			(void) fclose(ifp);
			return FALSE;
		}
		if (hh.nbuf[0] == '\0') {
			fprintf(stderr, "Article not subscribed to by %s\n", sp->s_name);
			(void) fclose(ifp);
			return FALSE;
		}
		(void) sprintf(TRANS, "%s/trXXXXXX", SPOOL);
	}

	if (notify) {
		char oldid[50];
		(void) sprintf(hh.title, "ihave %s %s", hh.ident, FULLSYSNAME);
		(void) sprintf(hh.nbuf, "to.%s.ctl", sp->s_name);
		(void) strcpy(oldid, hh.ident);
		getident(&hh);
		log("tell %s about %s, notif. id %s",
			sp->s_name, oldid, hh.ident);
	}

	if (appfile) {
		if (firstbufname[0] == '\0') {
			extern char histline[];
			localize("junk");
			savehist(histline);
			xerror("No file name to xmit from");
		}
		if (sp->s_xmit[0] == '\0')
			sprintf(sp->s_xmit, "%s/%s", BATCHDIR, sp->s_name);
#ifdef IHCC
		(void) sprintf(TRANS, "%s/%s/%s", logdir(HOME), BATCHDIR, sp->s_xmit);
		ofp = fopen(TRANS, "a");
#else /* !IHCC */
		ofp = fopen(sp->s_xmit, "a");
#endif /* !IHCC */
		if (ofp == NULL)
			xerror("Cannot append to %s", sp->s_xmit);
#ifdef MULTICAST
		fprintf(ofp, "%s", firstbufname);
		while (--mc >= 0)
			fprintf(ofp, " %s", *sysnames++);
		fprintf(ofp, "\n");
#else /* !MULTICAST */
		fprintf(ofp, "%s\n", firstbufname);
#endif /* !MULTICAST */
		(void) fclose(ofp);
		(void) fclose(ifp);
		return TRUE;
	}
	else if (useexist) {
		if (firstbufname[0] == '\0')
			xerror("No file name to xmit from");
		if (*sp->s_xmit == '\0')
#ifdef UXMIT
			(void) sprintf(bfr, UXMIT, sp->s_name, firstbufname);
#else
			xerror("UXMIT not defined for U flag");
#endif
		else
#ifdef MULTICAST
			makeargs(bfr, sp->s_xmit, firstbufname, sysnames, mc);
#else
			(void) sprintf(bfr, sp->s_xmit, firstbufname);
#endif
		(void) fclose(ifp);
	} else if (history) {
		extern char histline[];

		if (*sp->s_xmit == '\0')
			xerror("no xmit command with H flag");
#ifdef MULTICAST
		makeargs(bfr, sp->s_xmit, histline, sysnames, mc);
#else
		(void) sprintf(bfr, sp->s_xmit, histline);
#endif
	} else {
		ofp = xfopen(mktemp(TRANS), "w");
		if (afmt) {
#ifdef OLD
			fprintf(ofp, "A%s\n%s\n%s!%s\n%s\n%s\n", oident(hh.ident), hh.nbuf, FULLSYSNAME,
				hh.path, hh.subdate, hh.title);
#else /* !OLD */
			logerr("Must have OLD defined to use A flag for xmit");
			return FALSE;
#endif /* !OLD */
		} else
			hwrite(&hh, ofp);
		if (!notify)
			while ((c = getc(ifp)) != EOF)
				putc(c, ofp);
		if (ferror(ofp))
			xerror("write failed on transmit");
		(void) fclose(ifp);
		(void) fclose(ofp);
		if (*sp->s_xmit == '\0')
			(void) sprintf(bfr, DFTXMIT, sp->s_name, TRANS);
		else
#ifdef MULTICAST
			makeargs(bfr, sp->s_xmit, TRANS, sysnames, mc);
#else /* !MULTICAST */
			(void) sprintf(bfr, sp->s_xmit, TRANS);
#endif /* !MULTICAST */
	}

	/* At this point, the command to be executed is in bfr. */
	if (noshell) {
		if (pid = fork())
			fwait(pid);
		else {
			(void) close(0);
			(void) open(TRANS, 0);
			ptr = bfr;
			for (pid = 0; pid < 19; pid++) {
				while (isspace(*ptr))
					*ptr++ = 0;
				argv[pid] = ptr;
				while (!isspace(*++ptr) && *ptr)
					;
				if (!*ptr)
					break;
			}
			argv[++pid] = 0;
			(void) setgid(gid);
			(void) setuid(uid);
			execv(argv[0], argv);
			xerror("Can't execv %s", argv[0]);
		}
	} else {
		if (!history && sp->s_xmit[0] && !index(bfr, '<')) {
			char newcmd[LBUFLEN];

			(void) sprintf(newcmd, "(%s) <%s", bfr,
			    useexist ? firstbufname : TRANS);
			system(newcmd);
		} else
			system(bfr);
	}
	if (!appfile && !useexist && !history)
		(void) unlink(TRANS);
	(void) fclose(ifp);
	return TRUE;
}

#ifdef MULTICAST
makeargs(buf, cmd, arg2, sysargs, sac)
char *buf;
char *cmd;
char *arg2;
register char **sysargs;
int sac;
{
	register char *p = cmd;
	register char *q;
	register ac = 0;
	register char *b = buf;

	q = p;
	do {
		if (q = index(q, ' '))
			*q = '\0';
		if (index(p, '%')) {
			switch (++ac) {
			case 1:
				while (--sac >= 0) {
					sprintf(b, p, *sysargs++);
					b = index(b, '\0');
				}
				break;
			case 2:
				sprintf(b, p, arg2);
				b = index(b, '\0');
				break;
			default:
				if (q)
					*q = ' ';
				xerror("badly formed command: %s", cmd);
			}
		} else {
			strcpy(b, p);
			b = index(b, '\0');
		}
		if (q) {
			*q = ' ';
			p = q;
			while (isspace(*q))
				q++;
		}
	} while (q != NULL);
}
#endif /* MULTICAST */

typedef struct {
	char *dptr;
	int dsize;
} datum;

/*
 * Return TRUE if we have seen this file before, else FALSE.
 */
history(hp)
struct hbuf *hp;
{
#ifdef DBM
	datum lhs, rhs;
	datum fetch();
#else /* !DBM */
	register FILE *hfp;
	register char *p;
#endif /* !DBM */
	char lcident[BUFLEN];
	extern char histline[];

#ifdef DEBUG
	fprintf(stderr,"history(%s)\n", hp->ident);
#endif /* DEBUG */
	/*
	 * Make the article ID case insensitive.
	 */
	(void) strcpy(lcident, hp->ident);
	lcase(lcident);

	idlock(lcident);
#ifdef DBM
	initdbm(ARTFILE);
	lhs.dptr = lcident;
	lhs.dsize = strlen(lhs.dptr) + 1;
	rhs = fetch(lhs);
	if (rhs.dptr)
		return(TRUE);
#else /* !DBM */
	hfp = xfopen(histfile(lcident), "r");
	while (fgets(bfr, BUFLEN, hfp) != NULL) {
		p = index(bfr, '\t');
		if (p == NULL)
			p = index(bfr, '\n');
		if (p != NULL)	/* can happen if nulls in file */
			*p = 0;
		lcase(bfr);

		if (strcmp(bfr, lcident) == 0) {
			(void) fclose(hfp);
			idunlock();
#ifdef DEBUG
			fprintf(stderr,"history returns true\n");
#endif /* DEBUG */
			return TRUE;
		}
	}
	(void) fclose(hfp);
#endif /* !DBM */
	histline[0] = '\0';
	addhist(hp->ident);
	addhist("\t");
#ifdef DEBUG
	fprintf(stderr,"history returns false\n");
#endif
	return FALSE;
}

char histline[PATHLEN];

addhist(msg)
char *msg;
{
	(void) strcat(histline, msg);
}

savehist(hline)
char *hline;
{
	register FILE *hfp;
	datum lhs, rhs;
	long fpos;
	register char *p;

	hfp = xfopen(ARTFILE, "a");
	fpos = ftell(hfp);
	fprintf(hfp, "%s\n", hline);
	(void) fclose(hfp);
#ifdef DBM
	/* We assume that history has already been called, calling dbminit. */
	p = index(hline, '\t');
	if (p)
		*p = 0;
	lcase(hline);
	lhs.dptr = hline;
	lhs.dsize = strlen(lhs.dptr) + 1;
	rhs.dptr = (char *)&fpos;
	rhs.dsize = sizeof fpos;
	store(lhs, rhs);
#else /* !DBM */
	/* also append to proper history subfile */
	hfp = xfopen(histfile(hline), "a");
	fprintf(hfp, "%s\n", hline);
	(void) fclose(hfp);
#endif /* !DBM */
	idunlock();
}

/*
 * Save partial news.
 */
/* ARGSUSED */
newssave(fd, dummy)
FILE *fd;
char *dummy;
{
	register FILE *tofd, *fromfd;
	char sfname[BUFLEN];
	register int c;
	time_t tim;

	if (fd == NULL)
		fromfd = xfopen(INFILE, "r");
	else
		fromfd = fd;
	(void) umask(savmask);
	(void) setgid(gid);
	(void) setuid(uid);

	(void) sprintf(sfname, "%s/%s", userhome, PARTIAL);
	if ((tofd = fopen(sfname, "a")) == NULL)
		xerror("Cannot save partial news in %s", sfname);
	(void) time(&tim);
	fprintf(tofd, "----- News saved at %s\n", arpadate(&tim));
	while ((c = getc(fromfd)) != EOF)
		putc(c, tofd);
	(void) fclose(fromfd);
	(void) fclose(tofd);
	printf("News saved in %s\n", sfname);
	xxit(1);
}

/*
 * Handle dates in header.
 */

dates(hp)
struct hbuf *hp;
{
	time_t edt;

	if (*hp->subdate) {
		if (cgtdate(hp->subdate) < 0) {
			xerror("Cannot parse submittal date '%s'", hp->subdate);
		}
	} else {
		(void) time(&edt);
		(void) strcpy(hp->subdate, arpadate(&edt));
	}
}

char lockname[80];
idlock(str)
char *str;
{
	register int i;
	char tempname[80];
	time_t now;
	struct stat sbuf;
	extern int errno;
#ifdef	VMS
	int fd;

	(void) sprintf(lockname, "/tmp/%s.l.1", str);
	if ((fd = creat(lockname, 0444)) < 0) {
#else /* !VMS */
	(void) strcpy(tempname, "/tmp/LTMP.XXXXXX");
	(void) mktemp(tempname);
	(void) sprintf(lockname, "/tmp/L%s", str);
#ifdef FOURTEENMAX
	lockname[5 /* /tmp/ */ + 14] = '\0';
#endif
	i = creat(tempname, 0666);
	if (i < 0)
		xerror("Cannot creat %s: errno %d", tempname, errno);
	(void) close(i);
	while (link(tempname, lockname)) {
#endif /* !VMS */
		(void) time(&now);
		i = stat(lockname, &sbuf);
		if (i < 0) {
			xerror("Directory permission problem in /tmp");
		}
		if (sbuf.st_mtime + 10*60 < now) {
			(void) unlink(lockname);
			logerr("Article %s locked up", str);
			break;
		}
		log("waiting on lock for %s", lockname);
		sleep((unsigned)60);
	}
#ifdef VMS
	(void) close(fd);
#endif
	(void) unlink(tempname);
}

idunlock()
{
	(void) unlink(lockname);
}

/*
 * Put a unique name into header.ident.
 */
getident(hp)
struct hbuf *hp;
{
	long seqn;
	register FILE *fp;

	lock();
	fp = xfopen(SEQFILE, "r");
	(void) fgets(bfr, BUFLEN, fp);
	(void) fclose(fp);
	seqn = atol(bfr) + 1;
#ifdef	VMS
	(void) unlink(SEQFILE);
#endif /* VMS */
	fp = xfopen(SEQFILE, "r+w");
	fprintf(fp, "%ld\n", seqn);
	(void) fclose(fp);
	unlock();
#ifdef HIDDENNET
	if (strcmp(LOCALSYSNAME, FULLSYSNAME))
		(void) sprintf(hp->ident, "<%ld@%s.%s%s>", seqn, LOCALSYSNAME, FULLSYSNAME,
		MYDOMAIN);
	else
#endif /* !HIDDENNET */
	(void) sprintf(hp->ident, "<%ld@%s%s>", seqn, FULLSYSNAME, MYDOMAIN);
}

/*
 * Check that header.nbuf contains only valid newsgroup names;
 * exit with error if not valid.
 *
 */
ngfcheck(isproc)
int isproc;
{
	register char *s1, *s2;
	register FILE *f;
	register char *os1;
	int ngroups = 1;
	unsigned int ngsize = AFSIZ;
	char tbuf[BUFLEN], *ngcheck;

	f = xfopen(ACTIVE, "r");
	ngcheck = malloc(ngsize);
	if (ngcheck == NULL)
		xerror("Can't malloc the active file");
	s1 = ngcheck;
	while (fgets(bfr, BUFLEN, f) != NULL) {
		os1 = s1;
		for(s2 = bfr; *s2 != '\0' && *s2 != ' ';) {
			if (s1 >= &ngcheck[ngsize-2]) {
				unsigned int offs = s1 - ngcheck;
				ngsize += LBUFLEN;
				ngcheck = realloc(ngcheck, ngsize);
				if (ngcheck == NULL)
					xerror("Can't realloc active file");
				s1 = ngcheck + offs;
			}
			*s1++ = *s2++;
		}
		*s1++ = '\0';
		if (isproc) /* don't check to see if can post to this group */
			continue;
		while (*s2++ != '\0' && *s2 != ' ')
			;	/* skip max article number */
		while (*s2++ != '\0' && *s2 != ' ')
			;	/* skip min article number */
		if (*s2++ != '\0' && *s2 == 'n')
			s1 = os1;	/* can't post to this group */
	}
	*s1++ = '\0';
	*s1 = '\0';
	(void) fclose(f);

	s1 = header.nbuf;
	s2 = nbuf;
	while (*s1 == NGDELIM || *s1 == ' ')
		s1++;	/* skip leading junk */
	do {
		/* there shouldn't be blanks, but give the jerk a break */
		if (*s1 == NGDELIM || *s1 == ' ') {
			*s2++ = '\0';
			while (*++s1 == NGDELIM || *s1 == ' ')
				;	/* remove extra commas */
			if (*s1 != '\0')
				ngroups++;
		} else
			*s2++ = *s1++;
	} while (*s1 != '\0');
	if (s2[-1] == NGDELIM)	/* strip trailing commas */
		s2--;
	*s2 = '\0';

	s1 = nbuf;
	while (*s1 != '\0') {	/* for each newsgroup in header */
		s2 = ngcheck;
		while (*s2 != '\0') { /* for each newsgroup in active file */
			if (strcmp(s1, s2) == 0)
				break;
			while (*s2++ != '\0')
				;
		}
		if (*s2 == '\0') {	/* not found. remove it */
			if (!isproc) {
				logerr("Invalid news group '%s'", s1);
				newssave(stdin, (char *)NULL);
			}
			/* See if it's in our alias list */
			f = xfopen(ALIASES,"r");
			while (fscanf(f,"%s %s", tbuf, bfr) == 2
				&& strcmp(s1, tbuf))
				;
			(void) fclose(f);
			if (strcmp(s1, tbuf) == 0) {
				logerr("Aliased newsgroup '%s' to '%s'", s1, bfr);
				os1 = s1;
				s1 = nbuf;
				s2 = tbuf;
				while (s1 < os1) /* copy left part */
					*s2++ = *s1++;
				s1 = bfr;
				while (*s1 != '\0') /* copy alias */
					*s2++ = *s1++;
				*s2++ = '\0';
				s1 = os1;
				os1 = nbuf + (s2 - tbuf);
				while (*s1++ != '\0') /* skip old group */
					;
				/* copy right part */
				tbufcpy(s2, s1);
				/* copy back to original buffer */
				tbufcpy(nbuf, tbuf);
				s1 = os1;
			} else {
				logerr("Unknown newsgroup '%s' removed", s1);
				s2 = s1;
				while (*s2++ != '\0')	/* skip the bad one */
					;
				tbufcpy(s1, s2);
			}
		} else { /* It's in our active file */
			os1 = s1;
			while (*s1++ != '\0')
				;
			/* check for local only distribution on incoming
			   newsgroups. This might occur if someone posted to
			   general,net.unix */
			if(isproc && ngroups > 1 && index(os1, '.') == NULL
				&& index(header.nbuf, '.') != NULL) {
				logerr("Local group '%s' removed", os1);
				tbufcpy(os1, s1);
				s1 = os1;
			}
		}
	}
	/*  remove any duplicates */
	os1 = s1 = nbuf;
	for(;;) {
		if (*s1++ == '\0') {
			if (*s1 == '\0')
				break;
			s2 = s1;
			while (*s2 != '\0') {
				if (strcmp(os1, s2) == 0) {
					logerr("Duplicate '%s' removed",os1);
					os1 = s2;
					while (*s2++ != '\0') /* skip it */
						;
					tbufcpy(os1, s2);
				} else
					while (*s2++ != '\0')
						;
			}
			os1 = s1;
			s1[-1] = '\0';
		}
	}
	if (nbuf[0] != '\0') {
		s1 = header.nbuf;
		s2 = nbuf;
		do {
			while (*s2 != '\0')
				*s1++ = *s2++;
			*s1++ = NGDELIM;
		} while (*++s2 != '\0');
		*--s1 = '\0';
		(void) free(ngcheck);
		return FALSE;
	}
	(void) free(ngcheck);
	return TRUE;
}

tbufcpy(s1, s2)
register char *s1, *s2;
{
	do {
		while (*s2 != '\0')
			*s1++ = *s2++;
		*s1++ = '\0';
	} while (*++s2 != '\0');
	*s1 = '\0';
}


/*
 * Figure out who posted the article (which is locally entered).
 * The results are placed in the header structure hp.
 */
gensender(hp, logname)
struct hbuf *hp;
char *logname;
{
	register char *fn, *p;
	char buf[BUFLEN];
	char *fullname(), *getenv();
	int fd, n;

	fn = getenv("NAME");

	if (fn == NULL) {
		(void) sprintf(buf, "%s/%s", userhome, ".name");
		fd = open(buf, 0);
		if (fd >= 0) {
			n = read(fd, buf, sizeof buf);
			(void) close(fd);
			if (n > 0 && buf[0] >= 'A') {
				fn = buf;
				for (p=fn; *p; p++)
					if (*p < ' ')
						*p = '\0';
			}
		}
	}

	if (fn == NULL)
		fn = fullname(logname);

	(void) sprintf(hp->path, "%s", logname);
	(void) sprintf(hp->from, "%s@%s%s (%s)", logname, FULLSYSNAME, MYDOMAIN, fn);
}

/*
 * Trap interrupts.
 */
onsig(n)
int n;
{
	static int numsigs = 0;
	/*
	 * Most UNIX systems reset caught signals to SIG_DFL.
	 * This bad design requires that the trap be set again here.
	 * Unfortunately, if the signal recurs before the trap is set,
	 * the program will die, possibly leaving the lock in place.
	 */
	if (++numsigs > 100) {
		logerr("inews ran away looping on signal %d", n);
		xxit(1);
	}
	(void) signal(n, onsig);
	SigTrap = n;
}

#ifdef BATCH
/*
 * If the stdin begins with "#", we assume we have been fed a batched
 * shell script which looks like this:
 *	#! rnews 1234
 *	article with 1234 chars
 *	#! rnews 4321
 *	article with 4321 chars
 *
 * In this case we just exec the unbatcher and let it unpack and call us back.
 *
 * Note that there is a potential security hole here.  If the batcher is
 * /bin/sh, someone could ship you arbitrary stuff to run as shell commands.
 * The main protection you have is that the effective uid will be news, not
 * uucp and not the super user.  (That, plus the fact that BATCH is set to
 * "unbatch" as the system is distributed.)  If you want to run a batched link
 * and you are security conscious, do not use /bin/sh as the unbatcher.
 * the thing to do is to change BATCH in your localize.sh file from /bin/sh
 * to some restricted shell which can only run rnews.
 */
checkbatch()
{
	int c;

	c = getc(stdin);
	if (c != EOF)
		(void) ungetc(c, stdin);
	clearerr(stdin);
	if (c == '#') {
		char unbatcher[BUFLEN];

		(void) sprintf(unbatcher, "%s/%s", LIB, BATCH);
		reset_stdin();
		execl(unbatcher, "news-unpack", (char *)0);
		xerror("Unable to exec shell to unpack news.");
	}
}

/*
 * We've already done a read on stdin, and we want to seek back to the
 * beginning.  We want the real file descriptor (beyond buffers) to
 * reflect the true beginning.  Do whatever is necessary.
 */
reset_stdin()
{
	register FILE *ofd;
	register int c;
	char *ofdname;
	long lseek();

	/* First try to seek back - if so, it's a cheap way back. */
	if (lseek(0, 0L, 0) == 0L)
		return;

	/* Can't seek, so have to copy input to a file and use that. */
	ofdname = "/tmp/inewsXXXXXX";
	(void) mktemp(ofdname);
	ofd = fopen(ofdname, "w");
	while ((c=getc(stdin)) != EOF)
		putc(c, ofd);
	if (ferror(ofd))
		xerror("write failed on temp file %s", ofdname);
	(void) fclose(stdin);
	(void) fclose(ofd);

	/* Now for a few lower level hacks to reopen stdin and make
	 * absolutely sure that the right fd's are done for the exec.
	 */
	(void) close(0);		/* make sure stdin is really closed. */
	(void) open(ofdname, 0);	/* should return zero */
	(void) unlink(ofdname);		/* to avoid cleaning it up later. */
}
#endif /* BATCH */

/*
 *	Exit and cleanup.
 */
xxit(status)
int status;
{
	(void) unlink(INFILE);
	(void) unlink(ARTICLE);
	while (lockcount > 0)
		unlock();
	idunlock();
	exit(status);
}

rwaccess(fname)
char *fname;
{
	int fd;

	fd = open(fname, 2);
	if (fd < 0)
		return 0;
	(void) close(fd);
	return 1;
}

exists(fname)
char *fname;
{
	int fd;

	fd = open(fname, 0);
	if (fd < 0)
		return 0;
	(void) close(fd);
	return 1;
}

int	lockcount = 0;			/* no. of times we've called lock */

#ifdef	VMS

#define	SUBLOCK	"/tmp/netnews.lck.1"

/*
 * Newsystem locking.
 * These routines are different for VMS because we can not
 * effectively simulate links, and VMS supports multiple
 * version numbers of files
 */
lock()
{
	register int i;
	register int fd;

	if (lockcount++ == 0) {
		i = DEADTIME;
		while ((fd = creat(SUBLOCK, 0444)) < 0) {
			if (--i < 0) {
				(void) unlink(SUBLOCK);
				logerr("News system locked up");
			}
			if (i < -3)
				xerror("Unable to unlock news system");
			sleep((unsigned)1);
		}
		(void) close(fd);
	}
}

unlock()
{
	if (--lockcount == 0)
		(void) unlink(SUBLOCK);
}

#else /* !VMS */

/*
 * Newsystem locking.
 */

#ifdef BSD4_2
#include <sys/file.h>
static int LockFd = -1;
lock()
{
	LockFd = open(SUBFILE,0);
	/* This will sleep until the other program releases the lock */
	/* We may need to alarm out of this, but I don't think so */
	(void) flock(LockFd, LOCK_EX);
}

unlock()
{
	(void) flock(LockFd, LOCK_UN);
	(void) close(LockFd);
}
#else /* !BSD4_2 */
/* Why doesn't USG unix have file locking????? */
lock()
{
	register int i;
	extern int errno;

	if (lockcount++ == 0) {
		i = DEADTIME;
		while (link(SUBFILE, LOCKFILE)) {
			if (errno != EEXIST)
				break;
			if (--i < 0)
				xerror("News system locked up");
			sleep((unsigned)1);
		}
	}
}

unlock()
{
	if (--lockcount == 0)
		(void) unlink(LOCKFILE);
}
#endif /* !BSD4_2 */
#endif /* !VMS */

/*
 * Generate the name of the person responsible for posting this article,
 * in order to check that two articles were posted by the same person.
 */
char *
senderof(hp)
struct hbuf *hp;
{
	char *q, *tp;
	char *tailpath();

	if (hp->sender[0])
		tp = hp->sender;
	else if (hp->from[0])
		tp = hp->from;
	else
		tp = tailpath(hp);

	/* Remove full name */
	q = index(tp, ' ');
	if (q)
		*q = '\0';

	q = malloc((unsigned)(strlen(tp) + 1));
	(void) strcpy(q, tp);
	return q;
}