/*
 * W-NEWS       A simple NEWS processing package.
 *              The 'sendbatch' program is used to create new batches that
 *              can be transported to other systems via the UUCP network.
 *              It gathers all files in the OUTGOING queue, and creates
 *              batches of up to LIM bytes.  Optionally, the generated batch
 *              is compressed using the 'compress(1)' program.  The final
 *              result will be handed to 'uux(1)' for transportation.
 *
 * Usage:       sendbatch [-c|-z|-p prog] [-d] [-s maxsize] sysname
 *
 * Version:     @(#)sendbatch.c	4.43	28-Feb-1994
 *
 * Authors:     Fred N. van Kempen, <waltje@Infomagic.COM>
 *              Edvard G.J. Tuinder <ed@ow.org> 
 *		Miquel van Smoorenburg, <miquels@drinkel.ow.org>
 *		Copyright 1988-1993 MicroWalt Corporation
 *		Copyright 1994,1995 Cistron Software C.V.
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 */
#include "wnews.h"


char *Release = VERSION;
char *Version = "@(#) sendbatch 4.43 (28-Feb-1994)";


char *remote;				/* name of remote system        */
char batchname[128];                    /* path name of current batch   */
char *program;				/* Program name			*/
long maxsize = (long) BATCHLIM;         /* maximum size of a batch      */
long cursize = (long) 0;                /* current size of batch        */
FILE *bfp = (FILE *)NULL;               /* FILE ptr of current batch    */
char *comp_prog;			/* Compression program		*/
char *comp_hdr;				/* Compression header		*/


_PRO( int send_it, (char *name)						);
_PRO( int cr_batch, (void)						);
_PRO( int cl_batch, (void)						);
_PRO( int add_art, (char *name)						);
_PRO( int mk_batch, (char *who)						);


/* Send a completed, news batch to a host via UUCP. */
int
send_it(name)
char *name;
{
  char cmd[256];
  struct stat stb;

  if ((stat(name, &stb)) == ERROR) {
	logmsg(LOGERR, "cannot stat(%s)\n", name);
	return(ERROR);
  }
  if (stb.st_size == 0L) return(OK);     /* nothing to do! */

  sprintf(cmd, "%s %s!rnews < %s\n", UUX, remote, batchname);
  if ((wsystem(cmd) != OK)) {
	logmsg(LOGERR|LOGSYS, "cannot exec \"%s\"\n", cmd);
  	return(ERROR);
  }
  return(OK);
}


/* Create a batch of news articles. */
int
cr_batch()
{
  strcpy(batchname, "/tmp/sb.XXXXXX");
  (void)mktemp(batchname);

  if ((bfp = fopen(batchname, "w")) == (FILE *)NULL) {
	logmsg(LOGERR, "cannot create %s\n", batchname);
	return(ERROR);
  }
  cursize = 0L;
  return(OK);
}


/*
 * Close up the current batch, and (optionally) run it through compress(1).
 * Then, ask UUX to send it to the UUCP host.
 */
int
cl_batch()
{
  char iscomp[128], command[128];
  int status, out;

  (void) fflush(bfp);
  (void) fclose(bfp);
  if (comp_prog) {
	strcpy(iscomp, "/tmp/sbZ.XXXXXX");
	mktemp(iscomp);

	/* Write the '#! cunbatch' header */
	if ((out = creat(iscomp, 0644)) < 0) {
		logmsg(LOGERR, "cannot creat(%s)\n", iscomp);
		unlink(iscomp);
		return(ERROR);
	}
	(void) write(out, comp_hdr, strlen(comp_hdr));
	close(out);

	/* And execute the compression program. */
	sprintf(command, "%s < %s >> %s 2>/dev/null",
		comp_prog, batchname, iscomp);
        if ((wsystem(command)) != 0) {
	    logmsg(LOGERR, "cannot exec(%s,%s)\n", comp_prog, iscomp);
            return(ERROR); 
        }
	(void) unlink(batchname);
	strcpy(batchname, iscomp);
  }
  if ((status = send_it(batchname)) == 0) {
	(void) unlink(batchname);
  	if (comp_prog) (void) unlink(iscomp);
  }
  return(status);
}


/* Add an article to the current batch. */
int
add_art(name)
char *name;
{
  char buff[1024];
  struct stat stb;
  register FILE *fp;

  /* Fetch some file info. */
  if (stat(name, &stb) < 0) {
	logmsg(LOGERR, "cannot stat(%s)\n", name);
	return(0);
  }
  if ((fp = fopen(name, "r")) == (FILE *)NULL) {
	logmsg(LOGERR, "cannot open(%s)\n", name);
	return(0);
  }

  /*
   * Check to see if the article still fits in the current batch.
   * Note, that it is FORCED to fit if the batch is still empty.
   * This is needed to prevent the program from looping over an
   * article that is larger than 'maxsize' bytes...
   */
  if (cursize > 0L && (cursize + stb.st_size) > maxsize) {
	if (cl_batch() < 0) {
		(void) fclose(fp);
		return(ERROR);
	}

	if (cr_batch() < 0) {
		(void) fclose(fp);
		return(ERROR);
	}
  }

  /* Add size of this article to current size. */
  cursize += stb.st_size;

  /* Create an article-separation header. */
  fprintf(bfp, "%s%ld\n", RNEWSHDR, (long) stb.st_size);

  /* Copy the article into the batch. */
  while (fgets(buff, 1024, fp) != (char *)NULL) fputs(buff, bfp);
  (void) fflush(bfp);
  (void) fclose(fp);

  return(OK);
}


/* Scan the batch list file for articles, and add them to the batch. */
int
mk_batch(who)
char *who;
{
  char fname[128], temp[128], buff[128];
  register char *sp = (char *)NULL;
  register int i;
  SYS *ap;
  FILE *fp;

  remote = who;
  for(ap = sys; ap; ap = ap->next) {
	if (strcmp(ap->name, who) == 0) {
		sp = who;
		break;
	}
  }

  if (sp == (char *)NULL) {
	logmsg(LOGERR, "unknown target %s\n", who);
	return(ERROR);
  }

  wmkspooldir(BATCHDIR, 0);
  if (ap->file && ap->file[0]) strcpy(fname, ap->file);
    else sprintf(fname, "%s/%s", BATCHDIR, who);
  sprintf(temp, "%s.work", fname);

  /* Does a batch file exist? */
  if ((access(fname, F_OK)) == ERROR ) return(0);

  /* Does a work file exist? */
  if ((access(temp, F_OK)) == ERROR) {
	/* Try to link the batch list to a "work file". */
	i = 0;
	while (i < 10) {
		if (link(fname, temp) == OK) break;
		if (++i >= 10) {
			logmsg(LOGERR, "cannot link(%s,%s)\n", fname, temp);
			return(ERROR);
		}
		sleep(6);
	}
	(void) unlink(fname);
  }

  /* Open the batch list file. */
  if ((fp = fopen(temp, "r")) == (FILE *)NULL) {
	logmsg(LOGERR, "cannot open %s\n", temp);
	return(ERROR);
  }
  logmsg(LOGINFO, "checking files for %s in %s\n", who, fname);

  /* Create the news batch. */
  if (cr_batch() < 0) {
	(void) fclose(fp);
	return(ERROR);
  }

  /* Now read article names from the list file. */
  while (fgets(buff, 128, fp) != (char *)NULL) {
	if (buff[0] == '0' || buff[0] == '\n') continue;

	if ((sp = strchr(buff, '\n')) != (char *)NULL) *sp = '\0';
	if ((sp = strchr(buff, ' ')) != (char *)NULL) *sp = '\0';
	if (add_art(buff) < 0) {
		(void) fclose(fp);
		return(ERROR);
	}
  }
  (void) fclose(fp);

  /* Close up any pending batch file. */
  if (cursize > 0L) {
	if (cl_batch() < 0) return(ERROR);
  }
  (void) unlink(temp);
  return(OK);
}


void
usage()
{
  fputs("Usage: sendbatch [-c|-z|-p prog] [-d] ", stderr);
  fputs("[-s maxsize] sysname\n", stderr);
  exit(ERROR);
}


int
main(argc, argv)
int argc;
char *argv[];
{
  char lock[128];
  register int i;
  char *comp;

  program = strrchr(*argv, '/');
  if (program == (char *)NULL) program = *argv;
    else program++;

#ifndef COHERENT
  opterr = 0;
#endif
  comp = NULL;
  while ((i = getopt(argc, argv, "zcdp:s:")) != EOF) switch(i) {
	case 'c':
		comp = "compress";
		break;

	case 'z':
		comp = "compgzip";
		break;

	case 'p':
		comp = optarg;
		break;

	case 'd':
		debug = 1;
		break;

	case 's':
		maxsize = atol(optarg);
		break;

	default:
		usage();
  }

  /* At least one more argument needed. */
  if (optind >= argc) usage();

  /* Now get compression program info. */
  if (comp) {
	if ((comp_prog = p_comp(comp)) == (char *)NULL) {
		logmsg(LOGERR, "unknown packer %s\n", comp);
		exit(1);
	}
	maxsize *= p_ratio(comp);
	comp_hdr = p_header(comp);
  }

  (void) umask(033);                    /* set protection to rw-r--r-- */

  /* Read the sys file. */
  if (sys_rd() < 0) exit(1);

  /* Check if we are already (or still?) running. */
  sprintf(lock, "%s/%s", VARDIR, BATLOCK);
  if (open(lock, (O_CREAT|O_WRONLY|O_EXCL), 0) < 0) {
	logmsg(LOGERR, "locked (lockfile %s)\n", lock);
	exit(ERROR);
  }

  i = 0;
  while (optind < argc) {
	if (mk_batch(argv[optind++]) < 0) i = 1;
  }

  /* Remove the lockfile. */
  (void) unlink(lock);

  exit(i);
}
