/*
 *	newsspool - copy incoming news into incoming directory
 *
 *	The -i option relies on the parent setting (and exporting) $PATH.
 *
 *	$Id: rnews.c,v 1.2 90/01/16 10:27:09 crash Exp Locker: crash $
 */

#ifndef lint
static char RCSid[] = "$Id: rnews.c,v 1.2 90/01/16 10:27:09 crash Exp Locker: crash $";
#endif /* lint */

#ifdef AMIGA
	/*
	 *	If being compiled for an Amiga, then this code should be linked into
	 *	the executable for UUPC to replace the built-in "rnews" command.
	 *	Note that this bypasses the "rnews.{batch,immed}" script.
	 */
#  define MAIN		rnews
#  define LOCAL		static
#else
#  define MAIN		main
#  define LOCAL
#endif

#include <stdio.h>

#ifndef AMIGA
#  include <sys/types.h>
#  include <sys/stat.h>
#else
#  include <time.h>
#  include <libraries/dos.h>
#  define stat		FileInfoBlock
#  define st_mode	fib_DirEntryType
extern struct FileLock *Lock();
#endif /* AMIGA */

#include <string.h>
#include <errno.h>
#include "libc.h"
#include "news.h"
#include "config.h"

#ifndef lint
static char RCSid[] = "$Id: rnews.c,v 1.2 90/01/16 10:27:09 crash Exp Locker: crash $";
#endif

#ifndef MAXTRIES
#  define	MAXTRIES	100	/* limit on attempts to make links */
#endif

extern int debuglevel;
int debug = 0;
char *progname;

extern void error(), exit();
#ifdef UTZOOERR
extern char *mkprogname();
#else
#define	mkprogname(a)	(a)
#endif

char buf[BUFSIZ*16];	/* try to get a batch in a few gulps */
int immed = 0;			/* try an immediate newsrun? */

void process();
FILE *outopen();
void outclose();
extern time_t time();
char *outname();

/*
 * main - parse arguments and handle options
 *		when used with UUPC, this becomes "rnews( argc, argv )"
 *		which is called by RNews() in the ulib.c module
 */
MAIN (argc, argv)
int argc;
char *argv[];
{
	int c;
	int errflg = 0;
	FILE *in;
	struct stat statbuf;
	extern int optind;
	extern char *optarg;
	extern FILE *efopen();
	void process();

	printmsg(3, "calling mkprogname(\"%s\")", *argv);
	progname = mkprogname(argv[0]);

	printmsg(3, "parsing command line");
	while ((c = getopt(argc, argv, "id")) != EOF)
		switch (c) {
		case 'i':	/* try immediate newsrun */
			immed++;
			break;
		case 'd':	/* Debugging. */
			debug++;
			setbuf(stderr, (char *)NULL);
			break;
		case '?':
		default:
			errflg++;
			break;
		}
	if (errflg) {
		fprintf(stderr, "Usage:  %s [file] ...\n", progname);
		exit(2);
	}
	/* probe to get unprivileged() called if necessary */
	(void) ctlfile((char *)NULL);

	/* mktemp() uses access(2) [ARGH!] so minimize chances of trouble */
	printmsg(3, "doing setuid()/getuid() funniness...");
	(void) setgid(getegid());
	(void) setuid(geteuid());
	(void) umask(newsumask());

	if (optind >= argc)
		process(stdin, "stdin");
	else
		for (; optind < argc; optind++)
			if (STREQ(argv[optind], "-"))
				process(stdin, "-");
			else {
#ifdef AMIGA
				struct FileLock *lock;

				printmsg(2, "checking input file's mode");
				if (lock = Lock(argv[optind], ACCESS_READ)) {
					Examine(lock, &statbuf);
					UnLock(lock);
				} else
					statbuf.st_mode = -1;
				in = efopen(argv[optind], "r");
				if (statbuf.st_mode > 0)			/* AmigaDOS directory */
					error("`%s' is directory!", argv[optind]);
#else
				in = efopen(argv[optind], "r");
				if (fstat(fileno(in), &statbuf) < 0)
					error("can't fstat `%s'", argv[optind]);
				if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
					error("`%s' is directory!", argv[optind]);
#endif /* AMIGA */
				process(in, argv[optind]);
				(void) fclose(in);
			}
#ifndef AMIGA
	if (immed) {
		execlp("newsrun", "newsrun", (char *)NULL);
		error("attempt to run newsrun failed!", "");
	}
	exit(0);
#else /* AMIGA */
	return( 0 );			/* exit() isn't called from anywhere else... */
#endif /* AMIGA */
}

/*
 * process - process input file
 */

/* ARGSUSED */
LOCAL void process(in, inname)
FILE *in;
char *inname;
{
	register int count;
	register int firstblock;
	FILE *out;
	register char *p;
	register int n;
	char *name;

	name = outname();
	out = outopen(name);

	/* do the copying */
	printmsg(4, "performing file copy...");
	firstblock = 1;
	while ((count = fread(buf, sizeof(char), sizeof(buf), in)) > 0) {
		if (firstblock) {
			n = cunskip(buf, count);
			p = buf + n;
			count -= n;
			firstblock = 0;
		} else
			p = buf;
		n = fwrite(p, sizeof(char), count, out);
		if (n != count)
			error("write error in output to `%s'", name);
	}
	outclose(out, name);
}

/*
 * outname - construct name for the temporary output file
 */
LOCAL char *outname()
{
	register char *p;

	p = strsave(fullartfile("in.coming/nspool.XXXXXX"));
	mktemp(p);
	printmsg(5, "temporary output file is '%s'", p);
	return(p);
}

/*
 * outopen - acquire an output file
 */
LOCAL FILE *outopen(name)
char *name;
{
	FILE *f;

	f = fopen(name, "w");
	if (f == NULL)
		error("unable to create temporary `%s'", name);
	printmsg(4, "output into '%s'", name);

	return(f);
}

/*
 * outclose - close output file, moving it to the right place
 *
 * Names are based on the current time in hopes of keeping input in order.
 */
LOCAL void outclose(f, tmpname)
FILE *f;
char *tmpname;
{
	register char *p;
	register char *name;
	register int ntries;
	time_t now;
	extern int errno;

	if (fclose(f) == EOF)
		error("fclose error on file `%s'", tmpname);

	p = fullartfile("in.coming/");
	name = emalloc(strlen(p) + 20);			/* plenty for a number */
	(void) strcpy(name, p);
	p = name + strlen(name);

	ntries = 0;
	for (;;) {
		now = time((time_t *)NULL);
#ifdef AMIGA
		sprintf(p, "%ld.z", now);
		printmsg(7, "\ttrying '%s'", name);
		/*
		 *	Try the normal "rename(from, to)" function...
		 *	This *should* be okay since the directory is
		 *	the same (because "fullartfile()" was used) so that
		 *	only possible error is EEXIST (?).
		 */
		if (rename(tmpname, name) == 0)
			break;
#else
		sprintf(p, "%ld", now);
		if (debug)
			fprintf(stderr, "trying to rename to %s\n", name);
		if (link(tmpname, name) >= 0)
			break;				/* NOTE BREAK OUT */
#endif /* AMIGA */
		if (errno != EEXIST)	/* something strange is wrong */
			error("unable to link `%s'", tmpname);
		errno = 0;
		if (ntries > MAXTRIES)	/* sanity check */
			error("too many attempts to link `%s'", tmpname);
		if (debug)
			fprintf(stderr, "failed\n");
		sleep(2);	/* avoid rumored race in 1-sec sleep */
		ntries++;
	}
	printmsg(6, "destination file is '%s'", name);
	if (debug)
		fprintf(stderr, "succeeded\n");
	/*
	 *	If running on an Amiga, we wouldn't be here unless the
	 *	rename() function, above, worked and so we *know* that
	 *	this unlink() is going to fail.  ...who cares?
	 */
	(void) unlink(tmpname);
}

/*
 * cunskip - inspect block for silly #! cunbatch headers
 *
 * number of chars at start to skip
 */
LOCAL int cunskip(bufp, count)
char *bufp;
int count;
{
	static char goop[] = "cunbatch";
#	define	GOOPLEN	(sizeof(goop)-1)	/* strlen(goop) */
	static char goop2[] = "c7unbatch";
#	define	GOOP2LEN	(sizeof(goop2)-1)	/* strlen(goop2) */
	register char *p;
	register int nleft;

	nleft = count;
	p = bufp;

	if (nleft < 2)				/* no room for a header */
		return(0);
	if (*p++ != '#' || *p++ != '!')		/* doesn't start with #! */
		return(0);
	nleft -= 2;

	/* skip space */
	while (nleft > 0 && (*p == ' ' || *p == '\t')) {
		p++;
		nleft--;
	}
	/* recognize headers (the +1s ensure room for the newline) */
	if (nleft >= GOOPLEN+1 && STREQN(p, goop, GOOPLEN)) {
		p += GOOPLEN;
		nleft -= GOOPLEN;
	} else if (nleft >= GOOP2LEN+1 && STREQN(p, goop2, GOOP2LEN)) {
		p += GOOP2LEN;
		nleft -= GOOP2LEN;
	} else								/* no header */
		return(0);

	/* skip more space */
	while (nleft > 0 && (*p == ' ' || *p == '\t')) {
		p++;
		nleft--;
	}
	if (nleft == 0 || *p++ != '\n')		/* didn't end properly */
		return(0);
	return(p - bufp);
}

/*
 * unprivileged - drop setuid-ness if configuration is overridden
 */
void unprivileged()
{
	setgid(getgid());
	setuid(getuid());
}
