/* Handle getting a message from email into GNATS.
   Copyright (C) 1993 Free Software Foundation, Inc.
   Contributed by Brendan Kehoe (brendan@cygnus.com).

This file is part of GNU GNATS.

GNU GNATS 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, or (at your option)
any later version.

GNU GNATS 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 GNU GNATS; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "config.h"

#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>

#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <sys/wait.h> /* FIXME */
#include <sys/param.h> /* FIXME: gets MAXBSIZE */

#include "gnats-dirs.h"
#include "globals.h"
#include "gnats.h"
#include "pathmax.h"

#ifndef MAXBSIZE /* FIXME - from params.h */
#define MAXBSIZE 8192
#endif

/* The name this program was run with.  */
char *program_name;

/* Queue the incoming message.  */
int queue_msg = 0;

/* Run file-pr on each message in the queue.  */
int run_queue = 0;

/* If 1, emit debugging information.  */
int flag_debug = 0;

/* Which file (defaults to null, aka stdin) to use to read as the message to
   queue for GNATS.  */
char *queue_file = NULL;

/* Where the GNATS queue directory is.  Default is GNATS_ROOT/gnats-queue.  */
char *queue_dir = NULL;

/* A safe environment for execle().  */
static char *safe_env[] = { "USER=gnats", "PATH=/bin:/usr/bin", NULL };

struct option long_options[] =
{
  {"directory", 1, NULL, 'd'},
  {"file", 1, NULL, 'f'},
  {"debug", 0, NULL, 'D'},
  {"queue", 0, NULL, 'q'},
  {"run", 0, NULL, 'r'},
  {"help", 0, NULL, 'h'},
  {"version", 0, NULL, 'V'},
  {NULL, 0, NULL, 0}
};

void usage ();

int
fork_gnats (filename)
     char *filename;
{
  int pid; /* pid_t */
  int status;

  errno = 0;
  pid = fork();
  if (pid < 0)
    punt (1, "%s: could not fork file-pr: %s\n", program_name,
	  strerror (errno));
  else if (pid == 0)
    {
      char *gnats_bin;
      int fd;

      gnats_bin = (char *) xmalloc (strlen (bindir) + 9);
      strcpy (gnats_bin, bindir);
      strcat (gnats_bin, "/file-pr");

      if (! flag_debug)
	{
	  /* Redirect stderr to /dev/null, since `at' will give you info about
	     the job you've queued.  If opening /dev/null fails, just ignore
	     it and go on.  */
	  fd = open ("/dev/null", O_RDWR);
	  if (fd > 0)
	    {
	      /* Do a dup2.  */
	      close (2);
	      fcntl (fd, F_DUPFD, 2);
	    }
	}

      errno = 0;
      if (flag_debug)
	{
	  if (execle (gnats_bin, "file-pr", "-f", filename,
		      "-D", NULL, safe_env) < 0)
	    punt (1, "%s: execle of gnats failed: %s\n", program_name,
		  strerror (errno));
	}
      else
	{
	  if (execle (gnats_bin, "file-pr", "-f", filename, NULL, safe_env) < 0)
	    punt (1, "%s: execle of gnats failed: %s\n", program_name,
		  strerror (errno));
	}

      exit (-1); /* XXX */
    }

  waitpid (pid, &status, 0); /* FIXME: not portable */

#ifndef WEXITSTATUS
#define WEXITSTATUS(x) (((union wait*)&(x))->w_retcode)
#endif
  return WEXITSTATUS (status);
}

/* Run a gnats process on each message in QUEUE_DIR.  */
void
run_gnats ()
{
  register DIR *d;
  register struct dirent *next;
  register int i;
  int nfiles = 0;
  int maxfiles = 10;
  struct file {
    char *name;
  } *files = (struct file *) xmalloc (sizeof (struct file) * maxfiles);

  if (chdir (queue_dir) < 0)
    punt (1, "can not open queue directory: %s", queue_dir);

  errno = 0;
  d = opendir (".");
  if (! d)
    {
      log_msg (LOG_INFO, 1, "can't open: ", queue_dir);
      return;
    }

  while ((next = readdir (d)))
    if (next->d_name[0] != '.')
      {
	if (nfiles == maxfiles)
	  {
	    maxfiles *= 2;
	    files = (struct file *) xrealloc ((char *) files,
					      sizeof (struct file) * maxfiles);
	  }
	files[nfiles++].name = (char *) strdup (next->d_name);
      }

  if (CLOSEDIR (d))
    log_msg (LOG_INFO, 1, "can not close: ", queue_dir);

  /* Run a gnats process for each file in the directory.  */
  for (i = 0; i < nfiles; i++)
    {
      int child_status;
      
      if (flag_debug)
	fprintf (stderr, "%s: running %s\n", program_name, files[i].name);

      child_status = fork_gnats (files[i].name);

      /* If gnats ran okay, then we can unlink the queue file.  Otherwise,
	 keep it around so we can give it another try whenever the problems
	 have been taken care of.
	 Exit status of 1: Simple failure, try again
	                2: Unexpected failure, probably case-specific
			3: Unexpected failure, probably general  */
      if (child_status == 0)
	{
	  if (unlink (files[i].name))
	    log_msg (LOG_INFO, 1, "cannot remove: ", files[i].name);
	}
      else if (child_status == 2)
	{
	  char *name2 = xmalloc (strlen (files[i].name) + 2);
	  strcpy (name2, ".");
	  strcat (name2, files[i].name);
	  rename (files[i].name, name2);
	  punt (0, "renamed `%s' to `%s' pending human intervention.\n",
		files[i].name, name2);
	  free (name2);
	  if (gnats_locked ()) unlock_gnats ();
	}
      else if (child_status == 3)
	{
	  punt (0, "file-pr complained of a (probably) general problem.\n");
	}
    }
}

/* Drop the message in QUEUE_FILE (or, failing that, stdin) into the
   QUEUE_DIR.  */
void
drop_msg ()
{
  int fd[2];
  char *tmpdir;
  char *bug_file = (char *) xmalloc (PATH_MAX);
  int r; /* XXX ssize_t */
  char *buf = (char *) xmalloc (MAXBSIZE);
  char *base, *new_name;

  if (queue_file)
    {
      fd[0] = open (queue_file, O_RDONLY);
      if (fd[0] < 0)
	punt (1, "%s: can't open queue file %s for reading: %s\n",
	      program_name, queue_file, strerror (errno));
    }
  else
    fd[0] = 0;

  tmpdir = getenv ("TMPDIR");
  if (tmpdir == NULL)
    tmpdir = "/tmp"; /* FIXME */
  sprintf (bug_file, "%s/gnatsXXXXXX", tmpdir);
  mktemp (bug_file);
  
  fd[1] = open (bug_file, O_WRONLY|O_CREAT, 0664);
  if (fd[1] < 0)
    punt (1, "%s: can't open queue file %s for writing: %s\n",
	  program_name, bug_file, strerror (errno));
  
  while ((r = read (fd[0], buf, MAXBSIZE)) > 0)
    if (write (fd[1], buf, r) < 0)
      {
	if (fd[0])
	  close (fd[0]);
	close (fd[1]);
	punt (1, "%s error in writing %s: %s\n", program_name,
	      bug_file, strerror (errno));
      }
  
  if (r < 0)
    {
      if (queue_file)
	punt (1, "%s: error reading queue file %s: %s\n",
	      program_name, queue_file, strerror (errno));
    }
  
  if (fd[0])
    close (fd[0]);
  close (fd[1]);

  errno = 0;
  base = basename (bug_file);
  new_name = (char *) xmalloc (strlen (queue_dir) + 1 + strlen (bug_file) + 1);
  sprintf (new_name, "%s/%s", queue_dir, base);
  if (rename (bug_file, new_name) < 0)
    {
      if (errno != EXDEV)
	punt (1, "%s: could not rename %s into the queue dir: %s\n",
	      program_name, bug_file, strerror (errno));

      copy_file (bug_file, new_name);

      if (unlink (bug_file))
	punt (1, "cannot remove `%s'", bug_file);
    }
}

static char USAGE[] = "\
Usage: %s [-f filename] [-d directory] [-q] [-r]\n\
       [--file=filename] [--directory=directory] [--queue] [--run]\n";

void
main (argc, argv)
     int argc;
     char **argv;
{
  int optc;

  program_name = basename (argv[0]);

  /* FIXME: handle signals */

  while ((optc = getopt_long (argc, argv, "d:f:VqrDh",
			      long_options, (int *) 0)) != EOF)
    {
      switch (optc)
	{
	case 'd':
	  if (optarg[0] == '\0')
	    punt (1, "%s: directory must be non-null\n", program_name);
	  queue_dir = optarg;
	  break;

	case 'f':
	  if (optarg[0] == '\0')
	    punt (1, "%s: filename must be non-null\n", program_name);
	  queue_file = optarg;
	  break;

	case 'r':
	  if (queue_msg)
	    punt (1, "%s: -q and -r can't be specified at the same time.\n",
		   program_name);
	  run_queue = 1;
	  break;

	case 'q':
	  if (run_queue)
	    punt (1, "%s: -q and -r can't be specified at the same time.\n",
		   program_name);
	  queue_msg = 1;
	  break;

	case 'D':
	  flag_debug = 1;
	  break;

	case 'h':
	  fprintf (stderr, USAGE, program_name);
	  exit (0);

	case 'V':
	  fprintf (stderr, "queue-pr version %s.\n", version_string);
	  exit (0);

	default:
	  punt (1, USAGE, program_name);
	}
    }

  configure ();
  umask (022);

  if (! queue_dir)
    {
      queue_dir = (char *) xmalloc (strlen (gnats_root) + 13);
      strcpy (queue_dir, gnats_root);
      strcat (queue_dir, "/gnats-queue");
    }

  if (queue_msg)
    drop_msg ();
  if (run_queue)
    run_gnats ();

  exit (0);
}

void
usage ()
{
  fprintf (stderr, USAGE, program_name);
  exit (1);
}
