/*
   Copyright (c) 1995-8,2001  Martin Schulze <joey@infodrom.org>

   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.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*/

/*
**  Read batchfiles and configs and batch them via uucp
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include "paths.h"
/*#include "libinn.h"*/
#include "log.h"

#define LINELENGTH	255
#define DELIMITER	':'
#define UUXFLAGS	"- -gd"
#define DEFAULT_SITE	"/default/"
#define SPOOLFREE	50 * 1024

struct s_site {
  char	*name;
  int	maxsize;
  int	queuesize;
  char	*header;
  char	*compressor;
  char	*uuxargs;
  struct s_site *next;
};

char arg_ctlfile[255] = PATH_UUCPCTL; 
char arg_ctlappendix[255] = ""; 
char arg_sites[1024] = "";
struct s_site *sites = NULL;

int
makelock(what)
char	*what;
{
  char	path[255];
  FILE	*lock;
  struct stat stats;

  if (what[0] == '/')
    sprintf(path, "%s", what);
  else
    sprintf(path, "%s/LOCK.%s", _PATH_LOCKS, what);

  if (stat(path, &stats) == 0) {
    filelog("Lock file `%s' already exists.", path);
    return 0;
  }
  if (errno == ENOENT) {
    if (!(lock = fopen(path, "w"))) {
      filelog("Can't open lock file `%s'.", path);
      return 0;
    }
    fprintf(lock, "%10d\n", getpid());
    fclose (lock);
    return 1;
  }
  return 0; /* not reached */
}

void
removelock(what)
char	*what;
{
  char	path[255];
  struct stat stats;

  if (what[0] == '/')
    sprintf(path, "%s", what);
  else
    sprintf(path, "%s/LOCK.%s", _PATH_LOCKS, what);

  if (stat(path, &stats) == 0) 
    unlink(path);
}

/*
 * Parse the arguments and fill global data structures
 */
static void
parse_args(argc, argv)
     int	argc;
     char	*argv[];
{
  int	argch;
  
  while (EOF != (argch = getopt(argc, argv, "f:c:"))) {
    switch (argch) {
    default:
      printf ("usage();\n");
    case 'c':
      strncpy (arg_ctlfile, optarg, sizeof (arg_ctlfile));
      break;
    case 'f':
      strncpy (arg_ctlappendix, optarg, sizeof (arg_ctlappendix));
      break;
    }
  }
  if (optind < argc) {
    while (optind < argc) {
      strcat(arg_sites, argv[optind++]);
      sprintf(arg_sites, "%s%c", arg_sites, DELIMITER);
    }
    arg_sites[strlen(arg_sites)-1] = '\0';
  }
}

/* Parse the uucpsend.ctl files
 * Format:
 *   site:queuesize:max_size:header:compressor:[<args...>]
 *
 */
int
parse_ctlfile(fname)
char	*fname;
{
  FILE	*ctlfile;
  char	line[LINELENGTH], field[LINELENGTH];
  char	*cp = line, *cp2;
  int	lcount = 0;
  struct s_site *tmpsite = NULL;

  if ((ctlfile = fopen(fname, "r")) == NULL) {
    fprintf(stderr, "Can't open file `%s' for reading.\n", fname);
    return -1;
  }

  cp = fgets(line, LINELENGTH, ctlfile);
  for (; cp != NULL ; cp = fgets(line, LINELENGTH, ctlfile) ) {
    lcount++;

    if (line[strlen(line)-1] == '\n')
      line[strlen(line)-1] = '\0';

    /* Skip the rest of the line if there's a comment sign */
    if ((cp2 = strchr (line, '#')))
      *cp2 = '\0';

    /* Remove trailing whitespace characters */
    for (cp2 = line + strlen(line)-1; cp2 >= line && (*cp2 == ' ' || *cp2 == '\t'); cp2--)
      if (*cp2 == ' ' || *cp2 == '\t')
	*cp2 = '\0';

    /* Skip comments */
    if (!strlen(line))
      continue;


    if ((tmpsite = (struct s_site *)malloc(sizeof (struct s_site))) == NULL) {
      fprintf(stderr, "Can't malloc.\n");
      return -2;
    }
    tmpsite->next = NULL;

    /* Fill the structure, one by one */
    if ((cp2 = index(cp, DELIMITER)) == NULL) {
      fprintf(stderr, "%s:%d invalid line.\n", fname, lcount);
      continue;
    }
    strncpy(field, cp, (int)cp2 - (int)cp);
    field[(int)cp2 - (int)cp] = '\0';
    if ((tmpsite->name = (char *)malloc(strlen(field)+1)) == NULL) {
      fprintf(stderr, "Can't malloc.\n");
      return -2;
    }
    strcpy(tmpsite->name, field);
    cp += strlen(field) +1;


    if ((cp2 = index(cp, DELIMITER)) == NULL) {
      fprintf(stderr, "%s:%d invalid line.\n", fname, lcount);
      continue;
    }
    strncpy(field, cp, (int)cp2 - (int)cp);
    field[(int)cp2 - (int)cp] = '\0';
    tmpsite->maxsize = atoi(field);
    cp += strlen(field) +1;

    
    if ((cp2 = index(cp, DELIMITER)) == NULL) {
      fprintf(stderr, "%s:%d invalid line.\n", fname, lcount);
      continue;
    }
    strncpy(field, cp, (int)cp2 - (int)cp);
    field[(int)cp2 - (int)cp] = '\0';
    tmpsite->queuesize = atoi(field);
    cp += strlen(field) +1;


    if ((cp2 = index(cp, DELIMITER)) == NULL) {
      fprintf(stderr, "%s:%d invalid line.\n", fname, lcount);
      continue;
    }
    strncpy(field, cp, (int)cp2 - (int)cp);
    field[(int)cp2 - (int)cp] = '\0';
    if ((tmpsite->header = (char *)malloc(strlen(field)+1)) == NULL) {
      fprintf(stderr, "Can't malloc.\n");
      return -2;
    }
    strcpy(tmpsite->header, field);
    cp += strlen(field) +1;


    if ((cp2 = index(cp, DELIMITER)) == NULL) {
      fprintf(stderr, "%s:%d invalid line.\n", fname, lcount);
      continue;
    }
    strncpy(field, cp, (int)cp2 - (int)cp);
    field[(int)cp2 - (int)cp] = '\0';
    if ((tmpsite->compressor = (char *)malloc(strlen(field)+1)) == NULL) {
      fprintf(stderr, "Can't malloc.\n");
      return -2;
    }
    strcpy(tmpsite->compressor, field);
    cp += strlen(field) +1;


    if ((tmpsite->uuxargs = (char *)malloc(strlen(cp)+1)) == NULL) {
      fprintf(stderr, "Can't malloc.\n");
      return -2;
    }
    strcpy(tmpsite->uuxargs, cp);


    /* Now that we've got an entry, we have to insert it into the
     * data structure
     */
    if (!strcmp(tmpsite->name, DEFAULT_SITE)) {
      if (sites) { /* missing: replacement of existing /default/ */
	tmpsite->next = sites;
	sites = tmpsite;
	tmpsite = NULL;
      } else {
	sites = tmpsite;
	tmpsite = NULL;
      }
    } else {
      if (!sites) {
	sites = tmpsite;
	tmpsite = NULL;
      } else {
	if (sites->next) {
	  tmpsite->next = sites->next;
	  sites->next = tmpsite;
	  tmpsite = NULL;
	} else {
	  sites->next = tmpsite;
	  tmpsite = NULL;
	}
      }
    }
  }

  fclose (ctlfile);
  return 0;
}

void
free_sites(root)
struct s_sites *root;
{
  struct s_site *act;
  struct s_site *tmp = NULL;

  (struct s_sites *)act = (struct s_sites *)root;

  while (act) {
    tmp = act;
    act = act->next;

    tmp->next = NULL;
    free(tmp->name);
    free(tmp->header);
    free(tmp->compressor);
    free(tmp->uuxargs);
    free(tmp);
  }
}

struct s_site *
copy_site(site, name)
struct s_site *site;
char *name;
{
  struct s_site *tmpsite = NULL;

  if ((tmpsite = (struct s_site *)malloc(sizeof (struct s_site))) == NULL) {
    fprintf(stderr, "Can't malloc.\n");
    return NULL;
  }
  tmpsite->next = NULL;
  
  if ((tmpsite->name = (char *)malloc(strlen(name)+1)) == NULL) {
    fprintf(stderr, "Can't malloc.\n");
    return NULL;
  }
  strcpy(tmpsite->name, name);
  tmpsite->queuesize = site->queuesize;
  tmpsite->maxsize = site->maxsize;
  
  if ((tmpsite->header = (char *)malloc(strlen(site->header)+1)) == NULL) {
    fprintf(stderr, "Can't malloc.\n");
    return NULL;
  }
  strcpy(tmpsite->header, site->header);
  
  if ((tmpsite->compressor = (char *)malloc(strlen(site->compressor)+1)) == NULL) {
    fprintf(stderr, "Can't malloc.\n");
    return NULL;
  }
  strcpy(tmpsite->compressor, site->compressor);
  
  if ((tmpsite->uuxargs = (char *)malloc(strlen(site->uuxargs)+1)) == NULL) {
    fprintf(stderr, "Can't malloc.\n");
    return NULL;
  }
  strcpy(tmpsite->uuxargs, site->uuxargs);

  return tmpsite;
}

char *
extract_site(keep, site)
char	*keep;
char	*site;
{
  char	tempo1[1024], tempo2[1024];
  char	*cp1, *cp2;

/*  printf ("extract keep is '%s'\n", keep);
  printf ("extract site is '%s'\n", site);*/
  memset(tempo1, 0, sizeof(tempo1));
  memset(tempo2, 0, sizeof(tempo2));

  cp1 = keep;
  if (!(cp2 = strstr(keep, site)))
    return keep;

  strncpy(tempo1, keep, (int)cp2 - (int)cp1);
/*  printf ("extract cp2 is '%s'\n", cp2);
  printf ("extract cp2 is '%d'\n", cp2);
  printf ("extract cp1 is '%d'\n", cp1);
  printf ("extract cp2-1 is '%d'\n", (int)cp2 - (int)cp1);*/
  if (tempo1[strlen(tempo1)-1] == DELIMITER) tempo1[strlen(tempo1)-1] = '\0';
/*  printf ("extract tempo1 is '%s'\n", tempo1);*/
  
  cp2 += strlen(site);
  if (cp2[0] == DELIMITER) cp2++;
  sprintf(tempo2, "%s", cp2);
/*  printf ("extract tempo2 is '%s'\n", tempo2);*/

  if (strlen(tempo1) > 0) {
    if (strlen(tempo2) > 0) 
      sprintf(keep, "%s%c%s", tempo1, DELIMITER, tempo2);    
    else
      sprintf(keep, "%s", tempo1);
  }
  else {
    if (strlen(tempo2) > 0) 
      sprintf(keep, "%s", tempo2);    
    else
      keep[0] = '\0';
  }
/*  printf ("extract tempo1 is '%s'\n", tempo1);
  printf ("extract tempo2 is '%s'\n", tempo2);
  printf ("extract -> '%s'\n", keep);*/
  return keep;
}

void
trimm_sites(keep)
char	*keep;
{
  struct s_site *newsites = NULL, *tmp = NULL, *act;
  char	*kp = keep, *cp, *cp2;
  char	site[20];

  for (act = sites->next; act; act = act->next) {
    if (strstr(keep, act->name)) {
      tmp = copy_site(act, act->name);
      kp = extract_site(kp, act->name);
    }

    /* copy the entry */
    if (tmp) {
      tmp->next = newsites;
      newsites = tmp;
      tmp = NULL;
    }
  }


  if (strlen(keep) > 0) {
    for (cp = strchr(keep, DELIMITER); cp ; cp = strchr(keep, DELIMITER)) {
      cp2 = keep;
      strncpy(site, keep, (int)cp - (int)cp2);
      extract_site(keep, site);
      tmp = copy_site(sites, site);

      /* copy the entry */
      if (tmp) {
	tmp->next = newsites;
	newsites = tmp;
	tmp = NULL;
      }
    }
  }

  free_sites(sites);
  sites = newsites;
}

void
loopthrough()
{
  struct s_site *site;
  char	batchfile[255];
  char	batchtmp[255];
  char	cmd[1024];
  char	uuxcmd[1024];
  char	line[1024];
  char	*cp;
  int	ret;
  int	freespace;
  int	maxsize;
  struct stat stats;
  FILE	*pcmd;

  for (site = sites;site; site=site->next) {
    filelog("start %s", site->name);
    if (makelock(site->name)) {

/* 1st step - Check free space on the partition
 */     
      sprintf(cmd, "%s -P %s ", PATH_DF, PATH_UUSPOOL);
      pcmd = popen(cmd, "r");

      cp = fgets(line, 1024, pcmd); /* skip first line */
      cp = fgets(line, 1024, pcmd); 
      sscanf(line, "%*s %*d %*d %d", &freespace);
      pclose(pcmd);

      if (freespace < SPOOLFREE) {
	filelog("No space left on uucp spool device (%s, %d)", PATH_UUSPOOL, freespace);
	removelock(site->name);
	filelog("end %s", site->name);
	continue;
      }

/* 2nd step - get the batchfile 
 */
      sprintf(batchfile, "%s/%s.uucp", _PATH_BATCHDIR, site->name);
      sprintf(batchtmp, "%s/%s.work", _PATH_BATCHDIR, site->name);
      if (stat(batchtmp, &stats) == 0) {
	sprintf(cmd, "%s %s >> %s; %s -f %s", PATH_CAT, batchtmp, batchfile, PATH_RM, batchtmp);
	ret = system(cmd);
      }
      sprintf(cmd, "%s -s -t30 flush %s", PATH_CTLINND, site->name);
      if ((ret = system (cmd))) {
	filelog("Can't flush %s", site->name);
	if ((stat(batchfile, &stats) == 0) && (stats.st_size == 0)) {
	  sprintf(cmd, "%s -f %s", PATH_RM, batchfile);
	  ret = system(cmd);
	}
	removelock(site->name);
	filelog("end %s", site->name);
	continue;
      }
      sprintf(cmd, "%s %s/%s %s; %s -s -t30 flush %s", PATH_MV, _PATH_BATCHDIR, site->name, batchtmp, PATH_CTLINND, site->name);
      ret = system(cmd);
      if (stat(batchfile, &stats) == 0) {
	sprintf(cmd, "%s %s >> %s", PATH_CAT, batchfile, batchtmp);
	ret = system(cmd);
      }
      sprintf(cmd, "%s %s | %s > %s; %s -f %s", PATH_CAT, batchtmp, PATH_SORT, batchfile, PATH_RM, batchtmp);
      ret = system(cmd);

      if ((stat(batchfile, &stats) == 0) && (stats.st_size == 0)) {
	filelog("No articles for %s.", site->name);
	sprintf(cmd, "%s -f %s", PATH_RM, batchfile);
	ret = system(cmd);
	removelock(site->name);
	filelog("end %s", site->name);
	continue;
      }


/* 3rd step - Check the site's queue size      

  if [ -n "${QUEUE}" ] ; then

    # Get queue size from directory size
    #
    KBYTESQUEUED=`du -s "$UUSPOOL/${SITE}"| ${AWK} '{print $1}'`
    if [ "${QUEUE}" -lt ${KBYTESQUEUED} ] ; then
      echo "`date +"%b %d %T"` ${PROGNAME}[$$]: ${SITE} has ${KBYTESQUEUED} kbytes queued." 1>&2
      rm -f ${LOCK}
      echo "`date +"%b %d %T"` ${PROGNAME}[$$]: end ${SITE}" 1>&2
      continue
    fi
  fi

 */

      sprintf(cmd, "%s -s %s/%s ", PATH_DU, PATH_UUSPOOL, site->name);
      pcmd = popen(cmd, "r");

      cp = fgets(line, 1024, pcmd); 
      sscanf(line, "%d", &freespace);
      pclose(pcmd);

      if (freespace >= site->maxsize) {
	filelog("No space left for %s", site->name);
	removelock(site->name);
	filelog("end %s", site->name);
	continue;
      }

      maxsize = site->maxsize - freespace;

/* 4th step - Build the command
 */
      sprintf(uuxcmd, "( echo \\\"#! %s\\\" ; %s ) | %s %s %s %s!rnews", 
	      site->header, site->compressor, PATH_UUX, UUXFLAGS, site->uuxargs, site->name);

/* 6th step - Create the batches

${NEWSBIN}/feeds/batcher -B ${BATCHBYTES} -b ${MAXBYTES}000 -p "${UUXCOM}" \
        ${SITE} ${BATCHFILE}
 */

      sprintf(cmd, "%s/batcher -B%d -b%d -p \"%s\" %s %s", 
	      _PATH_NEWSBIN, maxsize * 1024, site->queuesize * 1024, uuxcmd, site->name, batchfile);
      ret = system(cmd);

      if ((stat(batchfile, &stats) == 0) && (stats.st_size == 0)) {
        sprintf(cmd, "%s -f %s;", PATH_RM, batchfile);
        ret = system(cmd);
      }

      removelock(site->name);
      filelog("end %s", site->name);
    }

    sleep(5);
  }
}



/*
 * --------------- Main routine ---------------
*/

int
main(argc, argv)
	int	argc;
	char	*argv[];
{
  char s[255];

  parse_args(argc, argv);
  if (parse_ctlfile (arg_ctlfile) < 0)
    exit (-1);

  sprintf(s, "%s/%s", _PATH_MOST_LOGS, "uucpsend.log");
  openlogfile(s);

  if (strcmp(sites->name,DEFAULT_SITE)) {
    filelog("No default line given in control file.");
    exit (-1);
  }

  if (strlen(arg_ctlappendix) > 0) {
    if (strlen(arg_sites) == 0) {
      free_sites (sites->next);
      sites->next = NULL;
    }
    sprintf(s, "%s-%s", arg_ctlfile, arg_ctlappendix);
    if (parse_ctlfile (s) < 0)
      exit (-1);
  }

  if (strlen(arg_sites) > 0) {
    trimm_sites(arg_sites);
  } else {
    /* site has to be freed, normally ;( */
    sites = sites->next;
  }

  loopthrough();
  free_sites(sites);
  closelogfile();

  exit (0);
}
