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

#include "dbparser.H"
#include "lex.H"
#include "config.H"
#include "vulndb.H"
#include "dict.H"
#include "fatal.H"
#include "strutils.H"
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>

static Dictionary<VulnInfo> *db = NULL;
static Dictionary<DummyBucket> *ignore = NULL;
static Dictionary<DummyBucket> *limit  = NULL;


// Note, vulns are sorted based on the order they appear in
// the data file, so make sure they stay alphabetical.

static char **call_names;
static const int NAME_INCREMENT = 200;
static int counter = 0;

FILE *GetDBFilePtr(char *&s)
{
  FILE *f = 0;
  char *user_fname, *hardcode_fname;
  GetVulnDBLocations(user_fname, hardcode_fname);
  if(user_fname)
    {
      f = fopen(user_fname, "r"); // its4: ignore fopen
      if(!f)
	{
	  Perror(user_fname);
	}
      else
	{
	  s = user_fname;
	  return f;
	}
    }
  if(hardcode_fname)
    {
      f = fopen(hardcode_fname, "r"); // its4: ignore fopen
      if(!f)
	{
	  Perror(hardcode_fname);
	}
      else
	{
	  s = hardcode_fname;
	  return f;
	}
    }
  // Try the local directory as a backup.
  f = fopen(DB_FILE_NAME, "r"); // its4: ignore fopen
  if(!f)
    {
      Perror(DB_FILE_NAME);
      fprintf(stderr, "Can't find a data file. Use -v to set one.\n");
      exit(1);
    }
  s = DB_FILE_NAME;
  return f;
}

void AddName(char *name)
{
  if(!(counter % NAME_INCREMENT))
    {
      char **old_call_names = call_names;
      call_names = new char* [counter+NAME_INCREMENT];
      if(!call_names)
	OutOfMemory();
      for(int i=0;i<counter;i++)
	call_names[i] = old_call_names[i];
      if(counter) delete[] old_call_names;
    }
  call_names[counter] = name;
}

char *GetNameById(int id)
{
  return call_names[id];
}

/* TODO: Better error reporting. */
void AddRecord(char *name, int desc, int repair, int r, int h, int inp)
{
  if(!db)
    {
      db = new Dictionary<VulnInfo>(7);
      if(!db)
	OutOfMemory();
    }
  AddName(name);
  VulnInfo *v = new VulnInfo(desc, repair, (Severity)r, h, counter++, inp);

  if(!v)
    OutOfMemory();
  db->SetItem(name, v);
}

// Responsible for closing the file.
char* SnarfFile(FILE *f)
{
  if(!f)
    {
      abort();
    }
  const int BUFSIZE = 1<<15; // 2 ^ 15 = 32K
  int num_expands = 0;
  char *b = "";
  int file_size = 0;
  size_t t;
  do
    {
      char *old_b = b;
      b = new char[file_size+BUFSIZE+1];
      if(!b)
	OutOfMemory();
      if(num_expands)
	{
	  strncpy(b, old_b, file_size);
	  delete[] old_b;
	}
      t = fread((void *)(b+BUFSIZE*(num_expands++)), sizeof(char), BUFSIZE, f);
      file_size += t;
    } while(t || (errno == EAGAIN));
  b[file_size] = 0;
  fclose(f);
  return b;
}

void SnarfDBFile()
{
  char *s = 0;
  FILE *f = GetDBFilePtr(s);
  Lex *l = new Lex(f, s, 0);
  if(!l)
    OutOfMemory();
  fclose(f);
  InitParser(l->GetTokens());
  NT_Program();  
  delete l;
}

void InitVulnDB()
{
  // 7 will handle up to 382 vulns w/o a rehash.
  if(!db)
    db = new Dictionary<VulnInfo>(7);
  if(!db)
    OutOfMemory();
  SnarfDBFile();
}

VulnInfo* GetVulnInfo(char *name)
{
  /* We can safely ignore error, since no valid key would give back NULL */
  short error;
  // Ignore takes precidence over limit.
  if(ignore && ignore->GetItem(name, error))
    return NULL;
  if(limit && !limit->GetItem(name, error))
    return NULL;
  return db->GetItem(name, error);
}

void AddLimit(char *name)
{
  char *buf = new char[strlen(name)+1];
  if(!buf)
    OutOfMemory();
  strcpy(buf, name); // ITS4: ignore strcpy

   if(!limit)
    {
      limit = new Dictionary<DummyBucket>(1);
      if(!limit)
	OutOfMemory();
    }
  limit->SetItem(buf, dummy_bucket);
}

void AddIgnore(char *name)
{
  char *buf = new char[strlen(name)+1];
  if(!buf)
    OutOfMemory();
  strcpy(buf, name); // ITS4: ignore strcpy

   if(!ignore)
    {
      ignore = new Dictionary<DummyBucket>(1);
      if(!ignore)
	OutOfMemory();
    }
  ignore->SetItem(buf, dummy_bucket);
}

void ScanIgnoreFile(char *fname)
{
  FILE *f = fopen(fname, "r");  // ITS4: ignore fopen
  if(!f)
    {
      Perror(fname);
      return;
    }
  // Will close f for us.
  char *b = SnarfFile(f);
  char *buf = new char[strlen(b)+1];
  char *p = b;
  char *q = b;
  while((q = strchr(p, '\n')))
    {
      *q = 0;
      if(strlen(p))
	{
	  Strip(p, buf);
	  if(strlen(buf))
	    AddIgnore(buf);
	}
      p = q+1;
    }
  delete[] buf;
}
