/* config.C
 * John Viega
 *
 * Jan 28-29 2000
 */

#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#else
extern "C" {
  // Hack for MS Windows
#ifndef __cplusplus
#define __cplusplus
#endif
#include "getopt.h"
}
#endif
#include "config.H"
#include "vulninfo.H"
#include "formatter.H"
#include "vulndb.H"
#include "query.H"
#include "fatal.H"
#include "vulninfo.H"
#include "strpool.H"

static int show_version = 0;
static unsigned int STR_UDE = 0;
static unsigned int STR_REC = 0;



void MakeUserDefinedStringsShared()
{
  STR_UDE = AddStringToPool("User defined error.");
  STR_REC = AddStringToPool("Add this to your vuln. database for more "
			    "flexibility.");
}

static struct option long_opts[] = {
  {"add",             1, 0,             'a'},
  {"severity-cutoff", 1, 0,             'c'},
  {"no-commands",     0, 0,             'C'},
  {"no-descriptions", 0, 0,             'D'},
  {"no-handlers",     0, 0,             'H'},
  {"ignore",          1, 0,             'i'},
  {"ignore-file",     1, 0,             'I'},
  {"limit",           1, 0,             'l'},
  {"input-mode",      0, 0,             'm'},
  {"output",          1, 0,             'o'},
  {"paranoid",        0, 0,             'p'},
  {"query",           1, 0,             'q'},
  {"quiet",           0, 0,             'Q'},
  {"reverse",         0, 0,             'r'},
  {"sort",            1, 0,             's'},
  {"no-solutions",    0, 0,             'S'},
  {"db-location",     1, 0,             'v'},
  {"no-severity",     0, 0,             'V'},
  {"width",           1, 0,             'w'},
  {"mvs-reports",     0, 0,             'W'},  
  {"help",            0, 0,             '?'},
  {"version",         0, &show_version,  1 },
  {0,                 0, 0,              0 }
};


static char *vuln_db        = 0;
static int argless_okay     = 0;
static int show_severity    = 1;
static int use_handlers     = 1;
static int input_scanning   = 0;
static int paranoid         = 0;
static int show_description = 1;
static int show_solution    = 1;
const char *DB_LOCATION     = EX2(DATA_DIR) "/" DB_FILE_NAME;

static const char *usage_string = "[OPTION]... [FILE]...";
static const char *help_string =
"Scan C and C++ source code for potential security problems." NEWLINE
"File argument is required, unless either --query or --version" NEWLINE
" option is used." NEWLINE NEWLINE
"  -a, --add=name" NEWLINE
"         Add a new function name to the database for this scan only." NEWLINE
"         Modify the database file or create a new database file for" NEWLINE
"         a more permanent solution." NEWLINE
"  -c, --severity-cutoff={0,1,2,3,4,5}" NEWLINE
"         Set severity cutoff.  Default is 2." NEWLINE
"         Lower numbers generally give more warnings." NEWLINE
"  -C, --no-commands" 
NEWLINE
"         Ignore commands to ITS4 that are embedded in comments." NEWLINE
"         See the man page for more details on ITS4 commands." NEWLINE
"  -D, --no-descriptions" 
NEWLINE
"         Don't display descriptions of potential problems." NEWLINE
"  -H, --no-handlers" 
NEWLINE
"         Don't use any clever tricks, just match token names." NEWLINE
"         This gives more warnings, because all of the sudden" NEWLINE
"             strcpy(dst, \"\\n\");" NEWLINE
"         will start giving errors." NEWLINE
"  -i, --ignore=function" NEWLINE
"         Ignore instances of a particular function name." NEWLINE
"         This flag can be used as many times as you like." NEWLINE
"  -I, --ignore-file=filename" NEWLINE
"         Specify a file to read ignore info from, causing ITS4 to not"NEWLINE
"         report instances of those functions.  Each function to ignore"NEWLINE
"         should be on its own line." NEWLINE
"  -l, --limit=function" NEWLINE
"         Tells ITS4 not to scan for any functions, except those" NEWLINE
"         passed in with this flag.  You can use this flag as many" NEWLINE
"         times as you want." NEWLINE
"  -m, --input-mode"
NEWLINE
"         Tells ITS4 to print out all non-argv spots at which input can"NEWLINE
"         enter.  This option causes some other options to be ignored." NEWLINE
"         Most importantly, the regular scan does not happen, no" NEWLINE
"         severities are visibly reported, and the cutoff is ignored." NEWLINE
"         Also, the default sorting value changes to 0, from 2." NEWLINE
"  -o, --output=filename" NEWLINE
"         Direct output to a given filename instead of stdout." NEWLINE
"  -p, --paranoid" NEWLINE
"         By default, ITS4 ignores identifiers that don't look like functions." NEWLINE
"         If you are afraid the code you're scanning abuses the preprocessor" NEWLINE 
"         heinously, you may catch some extra stuff by turning this on." NEWLINE
"         You will probably up the noise level significantly, though." NEWLINE
"  -q, --query=function" NEWLINE
"         Show database record for the given function name." NEWLINE
"         This flag can be used as many times as you like." NEWLINE
"  -Q, --quiet"
NEWLINE
"         Same as using both -D and -S." NEWLINE
"  -r, --reverse"
NEWLINE
"         Sort output in reverse order." NEWLINE
"  -s, --sort={0,1,2,3,4,5,6}" NEWLINE
"         Sort output.  Takes integer from 0-6.  Default is 2, unless" NEWLINE
"         the -m flags is also set, in which case the default is 0." NEWLINE
"                   0 = No sort, report in order scanned." NEWLINE
"                   1 = Sort by most severe, then group by location." NEWLINE
"                   2 = Sort by most severe, then group by vulnerability." NEWLINE
"                   3 = Sort by vulnerability, then severity." NEWLINE
"                   4 = Sort by vulnerability, then location." NEWLINE
"                   5 = Sort by file, then by severity." NEWLINE
"                   6 = Sort by file, then by vulnerability." NEWLINE
"  -S, --no-solutions"
NEWLINE
"         Don't display solution guidelines for potential problems." NEWLINE
"  -v, --db-location=file" NEWLINE
"         Set the location of the vulnerability database to use." NEWLINE
"  -V, --no-severity"
NEWLINE
"         Don't display the severity." NEWLINE
"  -w, --width=cols" NEWLINE
"         Set terminal width (for output wrapping)." NEWLINE
"  -W, --mvs-reports" NEWLINE
"         Format output for use with Microsoft Visual Studio." NEWLINE
"  -?, -h"
", --help"
NEWLINE
"         This help screen." NEWLINE
"  --version" NEWLINE
"         Show version information at beginning of output." NEWLINE
;

static FILE* output_file = stdout;

void SetOutputFile(char* fname)
{
  output_file = fopen(fname, "w");  // its4: ignore fopen
  if(!output_file)
    {
      Perror(fname);
      exit(6);
    }
}

FILE* GetOutputFile() { return output_file; }

int Paranoid() { return paranoid; }

void ShowUsage(FILE* filep)
{
  fprintf(filep, "%s %s" NEWLINE, GetProgramName(), usage_string);
}

int GetShowSeverity()
{
  if(GetInputScanning()) return 0;
  return show_severity;
}

void ShowHelp()
{
  ShowUsage(stdout);
  fprintf(stdout, "%s", help_string);
}

void GetVulnDBLocations(char *&user, char *&hardcode)
{
  user = vuln_db;
  hardcode = (char *)DB_LOCATION;
}

int GetArglessOkay()
{
  return argless_okay;
}

int ShowDescription()
{
  return show_description;
}

int ShowSolution()
{
  return show_solution;
}

static Severity cutoff = S_MODERATE_RISK;

Severity GetSeverityCutoff()
{
  return cutoff;
}

#define DEFAULT_WIDTH 80
static int width = 0;

void CalculateOutputWidth()
{
  char *width_str = getenv("COLUMNS");  /* ITS4: ignore getenv */

  if(!width_str)
    {
      width = DEFAULT_WIDTH;
    }
  else
    {
      width_str = strchr(width_str, '=');
      if(!width_str)
	{
	  width = DEFAULT_WIDTH;
	}
      else
	{
	  width = atoi(++width_str) || DEFAULT_WIDTH;
	}
    }
}

int GetOutputWidth()
{
  if(!width)
    CalculateOutputWidth();
  return width;
}

// 0 = natural (by file on command line)
// 1 = by severity, then filename
// 2 = by severity, then vulnname
// 3 = by vulnname, then severity
// 4 = by vulnname, then filename
// 5 = by filename, then severity
// 6 = by filename, then vulnname
static int sort_type = -1;

int GetSortType()
{
  if(sort_type < 0)
    {
      if(GetInputScanning()) return 0;
      return 2;
    }
  return sort_type;
}

static int reverse_sort = 0;

int GetReverseSort()
{
  return reverse_sort;
}

int GetUseHandlers()
{
  return use_handlers;
}

static int msvs_format = 0;

int GetMSVSFormat()
{
  return msvs_format;
}

static char *program_name = 0;
char *GetProgramName()
{
  return program_name;
}

void SetProgramName(char *s)
{
  program_name = s;
}

static int ignore_its4_commands = 0;

int IgnoreIts4Commands() { return ignore_its4_commands; }

int GetInputScanning(){ return input_scanning; }

void ParseOptions(int argc, char **argv, int &index)
{
  char *optstr = "a:c:CDhHi:I:l:mo:pq:Qrs:Sv:Vw:W?";
  char *n;
  int size;
  while(1)
    {
      // ITS4: ignore getopt_long
      switch(getopt_long(argc, argv, optstr, &(long_opts[0]), 0)) {
      case 'a':
	if(!optarg)
	  {
	    fprintf(stderr, "Warning: 'a' option requires an argument.\n");
	    continue;
	  }
	n = new char[strlen(optarg)];
	if(!STR_UDE)
	  MakeUserDefinedStringsShared();
	AddRecord(optarg, STR_UDE, 
		  STR_REC,
		  S_MOST_RISKY, 0, 0);
	continue;
      case 'C':
	ignore_its4_commands = 1;
	continue;
      case 'D':
	show_description = 0;
	continue;
      case 'H':
	use_handlers = 0;
      case 'S':
	show_solution = 0;
	continue;
      case 'o':
	if(!optarg)
	  {
	    fprintf(stderr, "Warning: option 'o' needs an argument."NEWLINE);
	    fprintf(stderr, "Writing to stdout."NEWLINE);
	    continue;
	  }
	SetOutputFile(optarg);
	continue;
      case 'p':
	paranoid = 1;
	continue;
      case 'c':
	if(!optarg)
	  {
	    fprintf(stderr,"Warning: option 'c' requires an argument." NEWLINE);
	    continue;
	  }
	if(optarg[0] < '0' || optarg[0] > '9')
	  {
	    fprintf(stderr,"Warning: argument to option 'c' (%s) not understood." NEWLINE, optarg);
	    continue;
	  }
	cutoff = (Severity)atoi(optarg);
	if(cutoff > MAX_SEVERITY)
	  {
	    fprintf(stderr, "Warning: Maximum severity exceeded.  Setting to the maximum." NEWLINE);
	    cutoff = MAX_SEVERITY;
	  }
	else if(cutoff < S_NO_RISK) cutoff = S_NO_RISK;	  
	continue;
      case 'i':
	if(!optarg)
	  {
	    fprintf(stderr,"Warning: option 'i' requires an argument." NEWLINE);
	    continue;
	  }
	AddIgnore(optarg);
	continue;
      case 'I':
	if(!optarg)
	  {
	    fprintf(stderr,"Warning: option 'i' requires an argument." NEWLINE);
	    continue;
	  }
	ScanIgnoreFile(optarg);
	continue;
      case 'l':
	if(!optarg)
	  {
	    fprintf(stderr,"Warning: option 'l' requires an argument." NEWLINE);
	    continue;
	  }
	AddLimit(optarg);
	continue;
      case 'm':
	input_scanning = 1;
	continue;
      case 'q':
	if(!optarg)
	  {
	    fprintf(stderr,"Warning: option 'q' requires an argument." NEWLINE);
	    continue;
	  }
	AddQuery(optarg);
	argless_okay = 1;
	continue;
      case 'Q':
	show_solution = show_description = 0;
	continue;
      case 'r':
	reverse_sort = 1;
	continue;
      case 's':
	if(!optarg)
	  {
	    fprintf(stderr,"Warning: option 's' requires an argument." NEWLINE);
	    continue;
	  }
	if(optarg[0] < '0' || optarg[0] > '9')
	  {
	    fprintf(stderr,"Warning: argument to option 's' (%s) not understood." NEWLINE, optarg);
	    continue;
	  }

	sort_type = atoi(optarg);
	continue;
      case 'v':
	if(!optarg)
	  {
	    fprintf(stderr, "Warning: option 'v' requires an argument." NEWLINE);
	    continue;
	  }
	size = strlen(optarg) + 1;
	vuln_db = new char[size];
	if(!vuln_db)
	  OutOfMemory();
	strncpy(vuln_db, optarg, size);
	continue;
      case 'V':
	show_severity = 0;
	continue;
      case 'w':
	if(!optarg)
	  {
	    fprintf(stderr,"Warning: option 'w' requires an argument." NEWLINE);
	    continue;
	  }
	if(optarg[0] < '0' || optarg[0] > '9')
	  {
	    fprintf(stderr,"Warning: argument to option 'w' (%s) not understood." NEWLINE, optarg);
	    continue;
	  }
	width = atoi(optarg);
	if(width <= 2) 
	  {
	    fprintf(stderr, "Width too low.  Using 80." NEWLINE);
	    width = 80;
	  }
	continue;
      case 'W':
	msvs_format = 1;
	continue;
      case '?':
      case 'h':
	ShowHelp();
	exit(0);
      case EOF:
	index = optind;
	return;
      default:
	if(show_version)
	  {
	    fprintf(stdout, "It's the software, stupid! (Security "
		    "Scanner) Version %s, %s." NEWLINE, ITS4_VERSION, 
		    VERSION_DATE);
	    show_version = 0;
	    argless_okay = 1;
	    continue;
	  }
	index = optind;
      }
    }
}


