/* SAMHAIN file system integrity testing                                   */
/* Copyright (C) 1999, 2000 Rainer Wichmann                                */
/*                                                                         */
/*  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., 675 Mass Ave, Cambridge, MA 02139, USA.              */

#include "config_xor.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>


#include "samhain.h"
#include "sh_error.h"
#include "sh_getopt.h"
#include "sh_files.h"
#include "sh_utils.h"
#include "sh_mail.h"
#include "sh_forward.h"
#include "sh_hash.h"

#if defined(WITH_EXTERNAL)
#include "sh_extern.h"
#endif

extern int      sh_calls_set_bind_addr (const char *);

#undef  FIL__
#define FIL__  _("sh_getopt.c")

#define HAS_ARG_NO  0
#define HAS_ARG_YES 1
#define DROP_PRIV_NO  0
#define DROP_PRIV_YES 1


typedef struct options {
  char * longopt;
  const char   shortopt;
  char * usage;
  int          hasArg;
  int (*func)(const char * opt);
} opttable_t;

/*@noreturn@*/
static int sh_getopt_usage (const char * dummy);
#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) 
static int sh_getopt_forever (const char * dummy);
#endif
static int sh_getopt_copyright (const char * dummy);

static opttable_t op_table[] = {

#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
  { N_("set-checksum-test"),  
    't', 
    N_("Set checksum testing to 'init', 'update', or 'check'"),  
    HAS_ARG_YES, 
    sh_util_setchecksum },
  { N_("interactive"),  
    'i', 
    N_("Run update in interactive mode"),  
    HAS_ARG_NO, 
    sh_util_set_interactive },
#endif
#if defined(SH_WITH_SERVER) || defined(SH_WITH_CLIENT)
  { N_("server-port"),  
    '-', 
    N_("Set the server port to connect to"),  
    HAS_ARG_YES, 
    sh_forward_server_port },
#endif
#ifdef SH_WITH_SERVER
  { N_("server"),  
    'S', 
    N_("Run as log server (obsolete)"),  
    HAS_ARG_NO, 
    sh_util_setserver },
  { N_("qualified"),  
    'q', 
    N_("Log fully qualified name of client host"),  
    HAS_ARG_NO, 
    sh_forward_set_strip },
  { N_("chroot"),  
    '-', 
    N_("Chroot to specified directory"),  
    HAS_ARG_YES, 
    sh_unix_set_chroot },
#endif
  { N_("daemon"),  
    'D', 
    N_("Run as daemon"),  
    HAS_ARG_NO, 
    sh_unix_setdeamon },
  { N_("foreground"),  
    '-', 
    N_("Stay in the foreground"),  
    HAS_ARG_NO, 
    sh_unix_setnodeamon },
  { N_("bind-address"),  
    '-', 
    N_("Bind to this address (interface) for outgoing connections"),  
    HAS_ARG_YES, 
    sh_calls_set_bind_addr },
#if defined(SH_WITH_SERVER) || defined(SH_WITH_CLIENT)
  { N_("set-export-severity"),  
    'e', 
    N_("Set severity threshold for export to remote log server"),  
    HAS_ARG_YES, 
    sh_error_setexport },
#endif
  { N_("set-syslog-severity"),  
    's', 
    N_("Set severity threshold for syslog"),  
    HAS_ARG_YES, 
    sh_error_set_syslog },
#ifdef WITH_EXTERNAL
  { N_("set-extern-severity"),  
    'x', 
    N_("Set severity threshold for logging by external program(s)"),  
    HAS_ARG_YES, 
    sh_error_set_external },
#endif
#ifdef HAVE_LIBPRELUDE
  { N_("set-prelude-severity"),  
    '-', 
    N_("Set severity threshold for logging to prelude"),  
    HAS_ARG_YES, 
    sh_error_set_prelude },
#endif
#if defined(WITH_DATABASE)
  { N_("set-database-severity"),  
    '-', 
    N_("Set severity threshold for logging to RDBMS"),  
    HAS_ARG_YES, 
    sh_error_set_database },
#endif
  { N_("set-log-severity"),  
    'l', 
    N_("Set severity threshold for logfile"),  
    HAS_ARG_YES, 
    sh_error_setlog },
#if defined(SH_WITH_MAIL)
  { N_("set-mail-severity"),  
    'm', 
    N_("Set severitythreshold  for e-mail"),  
    HAS_ARG_YES, 
    sh_error_setseverity },
#endif
  { N_("set-print-severity"),  
    'p', 
    N_("Set the severity threshold for terminal/console log"),  
    HAS_ARG_YES, 
    sh_error_setprint },
#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) 
  { N_("recursion"),  
    'r', 
    N_("Set recursion level for directories"),  
    HAS_ARG_YES, 
    sh_files_setrecursion },
#endif
  { N_("verify-log"),  
    'L', 
    N_("Verify the audit trail"),  
    HAS_ARG_YES, 
    sh_error_logverify },
  { N_("just-list"),  
    'j', 
    N_("Modify -L to just list the audit trail"),  
    HAS_ARG_NO, 
    sh_error_logverify_mod },
#if defined(SH_WITH_MAIL)
  { N_("verify-mail"),  
    'M', 
    N_("Verify the mailbox"),  
    HAS_ARG_YES, 
    sh_mail_sigverify 
  },
#endif
  { N_("add-key"),  
    'V', 
    N_("Add key for the mail/log signature"),  
    HAS_ARG_YES, 
    sh_util_set_newkey
  },
  { N_("hash-string"),  
    'H', 
    N_("Print the hash of a string"),  
    HAS_ARG_YES, 
    sh_error_verify },
#if defined (SH_WITH_SERVER) 
  { N_("password"),  
    'P', 
    N_("Compute a client registry entry for password"),  
    HAS_ARG_YES, 
    sh_forward_make_client },
  { N_("gen-password"),  
    'G', 
    N_("Generate a random password"),  
    HAS_ARG_NO, 
    sh_forward_create_password },
#endif

#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) 
  { N_("forever"),  
    'f', 
    N_("Loop forever, even if not daemon"),  
    HAS_ARG_NO, 
    sh_getopt_forever},
  { N_("full-detail"),  
    'a', 
    N_("Modify -d to list full details"),  
    HAS_ARG_NO, 
    set_full_detail},
  { N_("delimited"),  
    '-', 
    N_("Modify -d to list full details, comma delimited"),  
    HAS_ARG_NO, 
    set_list_delimited},
  { N_("list-database"),  
    'd', 
    N_("List database content (like ls -l)"),  
    HAS_ARG_YES, 
    sh_hash_list_db},
  { N_("init2stdout"),  
    '-', 
    N_("Write database to stdout on init"),  
    HAS_ARG_NO, 
    sh_hash_pushdata_stdout},
#endif
  { N_("trace-logfile"),  
    '-', 
    N_("Logfile for trace"),  
    HAS_ARG_YES, 
    sl_trace_file },
  { N_("trace-enable"),  
    '-', 
    N_("Enable tracing"),  
    HAS_ARG_NO, 
    sl_trace_use },
  { N_("copyright"),  
    'c', 
    N_("Print copyright information"),  
    HAS_ARG_NO, 
    sh_getopt_copyright },
  { N_("help"),  
    'h', 
    N_("Print usage information"),  
    HAS_ARG_NO, 
    sh_getopt_usage },
#if defined(HAVE_LIBPRELUDE) && defined(HAVE_LIBPRELUDE_9)
  /* need to skip over these */
  { N_("prelude"),  
    '-', 
    N_("Prelude generic options"),  
    HAS_ARG_NO, 
    NULL },
  { N_("profile"),  
    '-', 
    N_("Profile to use for this analyzer"),  
    HAS_ARG_YES, 
    NULL },
  { N_("heartbeat-interval"),  
    '-', 
    N_("Number of seconds between two heartbeats"),  
    HAS_ARG_YES, 
    NULL },
  { N_("server-addr"),  
    '-', 
    N_("Address where this sensor should report to"),  
    HAS_ARG_YES, 
    NULL },
  { N_("analyzer-name"),  
    '-', 
    N_("Name for this analyzer"),  
    HAS_ARG_YES, 
    NULL },
#endif
  /* last entry -- required !! -- */
  { NULL, 
    '\0',     
    NULL,  
    HAS_ARG_NO, 
    NULL }
};

static int sh_getopt_copyright (const char * dummy)
{
  fprintf (stdout, 
	   _("Copyright (C) 1999-2005 Rainer Wichmann"\
	     " (http://la-samhna.de).\n\n"));

  fprintf (stdout, 
	   _("This program is free software; "\
	     "you can redistribute it and/or modify\n"));
  fprintf (stdout, _("it under the terms of the GNU General "\
		     "Public License as published by\n"));
  fprintf (stdout, _("the Free Software Foundation; either version 2 "\
		     "of the License, or\n"));
  fprintf (stdout, _("(at your option) any later version.\n\n"));

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

  fprintf (stdout, _("You should have received a copy of the "\
		     "GNU General Public License\n"));
  fprintf (stdout, _("along with this program; "\
		     "if not, write to the Free Software\n"));
  fprintf (stdout, _("Foundation, Inc., 59 Temple Place - Suite 330, "\
		     "Boston, MA  02111-1307, USA.\n\n"));

  fprintf (stdout, _("This product makes use of the reference implementation "\
		     "of the TIGER message\n"));
  fprintf (stdout, _("digest algorithm. This code is copyright Eli Biham "\
		     "(biham@cs.technion.ac.il)\n"));
  fprintf (stdout, _("and Ross Anderson (rja14@cl.cam.ac.uk). It can be used "\
		     "freely without any\n"));
  fprintf (stdout, _("restrictions.\n"));
#if defined(USE_SRP_PROTOCOL) && !defined(SH_STANDALONE)
#if (!defined(HAVE_LIBGMP) || !defined(HAVE_GMP_H))
  fprintf (stdout, _("This product makes use of the 'bignum' library by "\
		     "Henrik Johansson\n"));
  fprintf (stdout, _("(Henrik.Johansson@Nexus.Comm.SE). If you are including "\
		     "this library in a\n"));
  fprintf (stdout, _("commercial product, be sure to distribute ALL of"\
		     " it with the product.\n"));
#endif
  fprintf (stdout, _("This product uses the 'Secure Remote Password' "\
		     "cryptographic\n"));
  fprintf (stdout, _("authentication system developed by Tom Wu "\
		     "(tjw@CS.Stanford.EDU).\n"));
#endif
  fprintf (stdout, _("\nPlease refer to the file COPYING in the source "\
		     "distribution for a"));
  fprintf (stdout, _("\nfull list of incorporated code and associated "\
		     "licenses.\n"));

  if (dummy)
    _exit (EXIT_SUCCESS);
  else
    _exit (EXIT_SUCCESS);
  /*@notreached@*/
  return 0; /* make compilers happy */
}

/*@noreturn@*/
static int sh_getopt_usage (const char * dummy)
{
  int  i;
  char fmt[64];

  char opts[64];

  for (i = 0; i < 64; ++i) /* splint does not grok char opts[64] = { '\0' }; */
    opts[i] = '\0';

  fprintf (stdout,
	   _("This is samhain (%s), "\
	     "(c) 1999-2005 Rainer Wichmann (http://la-samhna.de).\n"),
	   VERSION);
  fprintf (stdout, _("This software comes with ABSOLUTELY NO WARRANTY. "));
  fprintf (stdout, _("Use at own risk.\n"));

  fprintf (stdout, _("Usage:\n\n"));

  for (i = 0; op_table[i].longopt != NULL; ++i) {

    if (i == 63)
      break;

    if (op_table[i].shortopt != '-' && 
	strchr(opts, op_table[i].shortopt) != NULL)
      fprintf (stdout, _("Short option char collision !\n"));
    opts[i] = op_table[i].shortopt;


    if (op_table[i].hasArg == HAS_ARG_NO) {
      if (sl_strlen(op_table[i].longopt) < 10) 
	sl_strlcpy(fmt,_("%c%c%c        --%-s,\t\t\t %s\n"), sizeof(fmt));
      else if (sl_strlen(op_table[i].longopt) < 17)
	sl_strlcpy(fmt, _("%c%c%c        --%-s,\t\t %s\n"), sizeof(fmt));
      else 
	sl_strlcpy(fmt, _("%c%c%c        --%-s,\t %s\n"), sizeof(fmt));
      /* flawfinder: ignore */
      fprintf (stdout, fmt,
	       (op_table[i].shortopt == '-') ? ' ' : '-',
	       (op_table[i].shortopt == '-') ? ' ' : op_table[i].shortopt,
	       (op_table[i].shortopt == '-') ? ' ' : ',',
	       _(op_table[i].longopt),
	       _(op_table[i].usage));
    } else {
      if (sl_strlen(op_table[i].longopt) < 12) 
	sl_strlcpy(fmt, _("%c%c %s  --%-s=<arg>,\t\t %s\n"), sizeof(fmt));  
      else 
	sl_strlcpy(fmt, _("%c%c %s  --%-s=<arg>,\t %s\n"), sizeof(fmt));   
      /* flawfinder: ignore */
      fprintf (stdout, fmt,
	       (op_table[i].shortopt == '-') ? ' ' : '-',
	       (op_table[i].shortopt == '-') ? ' ' : op_table[i].shortopt,
	       (op_table[i].shortopt == '-') ? _("      ") : _("<arg>,"),
	       _(op_table[i].longopt),
	       _(op_table[i].usage));
    }
  }

  fprintf (stdout, 
	   _("\nPlease report bugs to support@la-samhna.de.\n"));

  (void) fflush(stdout);

  if ( dummy != NULL) 
    {
      if (sl_strcmp( dummy, _("fail")) == 0 ) 
	  _exit (EXIT_FAILURE);
    }

  _exit (EXIT_SUCCESS);
  /*@notreached@*/
  return 0; /* make compilers happy */
}

#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) 
static int sh_getopt_forever (const char * dummy)
{
  dummy = (void *) dummy;
  SL_ENTER(_("sh_getopt_forever"));
  sh.flag.loop = S_TRUE;
  SL_RETURN(0, _("sh_getopt_forever"));
}
#endif  

int sh_getopt_get (int argc, char * argv[])
{
  int           count   = 0;
  size_t        len     = 0;
  int           foundit = 0;
  int           i;
  size_t        k;
  char        * theequal;

  SL_ENTER(_("sh_getopt_get"));

  /* -- Return if no args. --
   */
  if (argc < 2) 
    SL_RETURN(0, _("sh_getopt_get"));
 
  while (argc > 1  && argv[1][0] == '-') 
    {

      /* Initialize
       */
      foundit = 0;
      len     = sl_strlen (argv[1]);
    
      /* a '-' with no argument: error
       */
      if (len == 1)
	(void) sh_getopt_usage(_("fail"));

      /* a '--' with no argument: stop argument processing
       */
      if (len == 2 && argv[1][1] == '-') 
	SL_RETURN( count, _("sh_getopt_get"));

      /* a short option: process it
       */
      if (len >= 2 && argv[1][1] != '-') 
	{
	  for (k = 1; k < len; ++k)
	    {
	      for (i = 0; op_table[i].shortopt != '\0'; ++i) 
		{
		  
		  if ( op_table[i].shortopt == argv[1][k] ) 
		    {
		      foundit = 1;
		      if ( op_table[i].hasArg == HAS_ARG_YES ) 
			{
			  if (k != (len - 1))
			    {
			      /* not last option
			       */
			      fprintf (stderr, 
				       _("Error: short option with argument is not last in option string\n"));
			      (void) sh_getopt_usage(_("fail"));
			    }
			  if (argc < 3) 
			    { 
			      /* argument required, but no avail 
			       */
			      fprintf (stderr, _("Error: missing argument\n"));
			      (void) sh_getopt_usage(_("fail"));
			    } 
			  else 
			    {
			      /* call function with argument */
			      --argc; ++argv;
			      if (NULL != op_table[i].func &&
				  0 != (* op_table[i].func )(argv[1]))
				fprintf (stderr, 
					 _("Error processing option -%c\n"),
					 op_table[i].shortopt);
			      break;
			    }
			} 
		      else 
			{
			  if (NULL != op_table[i].func &&
			      0 != (* op_table[i].func )(NULL))
			    fprintf (stderr, 
				     _("Error processing option -%c\n"),
				     op_table[i].shortopt);
			  break;
			}
		    }
		}
	    }

	  /* 'break' should get here 
	   */
	  if (foundit == 1) 
	    {
	      --argc; ++argv;
	      continue;
	    } 
	  else 
	    {
	      /* unrecognized short option */
	      fprintf (stderr, _("Error: unrecognized short option\n"));
	      (void) sh_getopt_usage(_("fail"));
	    }
	}

      /* a long option: process it
       */
      if (len > 2) 
	{

	  for (i = 0; op_table[i].longopt != NULL; ++i) 
	    {

	      if (sl_strncmp(_(op_table[i].longopt), 
			     &argv[1][2], 
			     sl_strlen(op_table[i].longopt)) == 0 ) 
		{
		  foundit = 1; 
		  if ( op_table[i].hasArg == HAS_ARG_YES ) 
		    {
		      theequal = strchr(argv[1], '=');
		      if (theequal == NULL) 
			{ 
			  if (argc < 3) 
			    { 
			      /* argument required, but no avail 
			       */
			      fprintf (stderr, _("Error: missing argument\n"));
			      (void) sh_getopt_usage(_("fail"));
			    } 
			  else 
			    {
			      /* call function with argument */
			      --argc; ++argv;
			      if (NULL != op_table[i].func &&
				  0 != (* op_table[i].func )(argv[1]))
				fprintf (stderr, 
					 _("Error processing option -%s\n"),
					 op_table[i].longopt);
			      break;
			    }
			} 
		      else 
			{
			  if (sl_strlen (theequal) > 1) 
			    {
			      ++theequal;
			      /* call function with argument */
			      if (NULL != op_table[i].func &&
				  0 != (* op_table[i].func )(theequal))
				fprintf (stderr, 
					 _("Error processing option -%s\n"),
					 op_table[i].longopt);
			      break;
			    } 
			  else 
			    {
			      fprintf (stderr, _("Error: invalid argument\n"));
			      /* argument required, but no avail */
			      (void) sh_getopt_usage(_("fail"));
			    }
			}
		    } 
		  else 
		    {
		      if (NULL != op_table[i].func && 
			  0 != (* op_table[i].func )(NULL))
			fprintf (stderr, 
				 _("Error processing option -%s\n"),
				 op_table[i].longopt);
		      break;
		    }
		}
	    }

	  /* 'break' should get here */
	  if (foundit == 1) 
	    {
	      ++count;
	      --argc; 
	      ++argv;
	      continue;
	    } 
	  else 
	    {
	      /* unrecognized long option */
	      fprintf (stderr, _("Error: unrecognized long option\n"));
	      (void) sh_getopt_usage(_("fail"));
	    }
	}
    }

  SL_RETURN( count, _("sh_getopt_get"));
}
