/* 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 <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 "config.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'},
  {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 (GNATS_ROOT) + strlen (BINDIR) + 9);
      strcpy (gnats_bin, GNATS_ROOT);
      strcat (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 */
  return 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 child_status = 0;
  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++)
    {
      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.  */
      if (child_status == 0)
	{
	  if (unlink (files[i].name))
	    log_msg (LOG_INFO, 1, "cannot remove: ", files[i].name);
	}
    }
}

/* 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);
    }
}

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':
	  /* Fall through.  */

	default:
	  usage ();
	}
    }

  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: %s [-f filename] [-d directory] [-q] [-r]\n\
       [--file=filename] [--directory=directory] [--queue] [--run]\n", program_name);
  exit (1);
}
