/* Nessus
 * Copyright (C) 1998 Renaud Deraison
 *
 * 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.
 *
 * Preferences  -- maps the content of the nessusd.conf file to memory
 *
 */
 
#include <includes.h>
#include <pwd.h>
#include "preferences.h"
#include "globals.h"

static int preferences_new();
static int prefs_buffer_parse(char *, struct arglist *);
static int prefs_add_subcategory(struct arglist *, FILE *);
static char * preferences_get_filename();
static void new_pluginset(struct arglist *, struct arglist *);
static int preferences_process(char *, struct arglist *);



int preferences_init(prefs)
	struct arglist ** prefs;
{
  char * filename;
  int result;
  *prefs = emalloc(sizeof(struct arglist));
  filename = preferences_get_filename();
  result = preferences_process(filename, *prefs);
  efree(&filename);
  return(result);
}

static char * 
preferences_get_filename()
{
  char* home;
  char * ret;
  struct passwd * pwd;
  
  home = getenv("HOME");
  if(home)
  {
   ret = emalloc(strlen(home)+strlen("/.nessusrc")+1);
   sprintf(ret, "%s/.nessusrc", home);
   return(ret);
  }
  pwd = getpwuid(getuid());
  if (pwd && pwd->pw_dir)
  {
   ret = emalloc(strlen(pwd->pw_dir)+strlen("/.nessusrc")+1);
   sprintf(ret, "%s/.nessusrc", pwd->pw_dir);
   return(ret);
  } 
  return(NULL);
}
 
static int preferences_new()
{
  int fd;
  char * buf;
  char * fn = preferences_get_filename();
  if(!fn)return(-1);
  
   if((fd = open(fn, O_CREAT | O_RDWR | O_EXCL, 0600))<0){
    perror("open ");
    return(-1);
  }
  buf = emalloc(500);
  sprintf(buf,"# Nessus Client Preferences File\n\n");
  write(fd, buf, strlen(buf));
  efree(&buf);
  close(fd);
  efree(&fn);
  return(0);
}

static int preferences_process(filename,prefs)
     char * filename;
     struct arglist * prefs;
{
  FILE * fd;
  char * buffer;
    if(filename)
      {
	if(!(fd = fopen(filename, "r"))) {
	 if(errno == EACCES)
	 {
	  fprintf(stderr,
	  	"The Nessus client doesn't have the right to read %s\n", filename);
	  exit(1);
	 }
#ifdef DEBUG
	  printf("Couldn't find any prefs file... Creating a new one...\n");
#endif 
	  if(preferences_new()){
	    fprintf(stderr, "Error creating %s : ", preferences_get_filename());
	    perror("open ");
	    return(1);
	  }
	  else
	    if(!(fd = fopen(filename, "r")))
	      {
	        perror("open ");
		printf("Could not open %s -- now quitting\n", filename);
		exit(2);
	      }
	}
	buffer = emalloc(255);
	while(!feof(fd) && fgets(buffer, 254,fd))
	  {
	   if(strchr(buffer, '='))
	    prefs_buffer_parse(buffer, prefs);
	   else if(!strncmp(buffer, "begin(", strlen("begin(")))
	   {
	    char * t = buffer+(strlen("begin(")*sizeof(char));
	    char * end = strchr(t, ')');
	    char * category_name;
	    
	    if(!end)
	    fprintf(stderr, "Parse error in %s : %s\n", filename, buffer);
	    else
	    {
	     struct arglist * subcategory;
	     end[0]=0;
	     category_name = emalloc(strlen(t)+1);
	     strncpy(category_name, t, strlen(t));
	     subcategory = emalloc(sizeof(struct arglist));
	     if(prefs_add_subcategory(subcategory, fd))
	       fprintf(stderr, "Missing 'end' in %s\n", filename);
	     else
	     arg_add_value(prefs, category_name, ARG_ARGLIST, -1, subcategory);
	     }
	    }
     	 }
     fclose(fd);
    return(0);
    }
   else return(1);
}
 
 
static int 
prefs_add_subcategory(arglist, fd)
 struct arglist * arglist;
 FILE * fd;
{
 char * buffer = emalloc(255);
 int flag = 0;
 
 while(!flag && !feof(fd) && fgets(buffer, 254,fd))
 {
  if(!strlen(buffer))return(1);
  if((!strcmp(buffer, "end\n"))||(!strncmp(buffer, "end(", 4)))flag = 1;
  else prefs_buffer_parse(buffer, arglist);
  bzero(buffer, 255);
 }
 efree(&buffer);
 return(0);
}
   
static int 
prefs_buffer_parse(buffer, arglist)
 char * buffer;
 struct arglist * arglist;
{
 char * t;
 char * opt;
 char * value;
 int val = -1;
 if(buffer[strlen(buffer)-1]=='\n')buffer[strlen(buffer)-1]=0;
 if(buffer[0]=='#')return(1);
 opt = buffer;
 /* remove the spaces before the pref name */
 if(opt[0]==' ' && opt[0])opt+=sizeof(char);
 if((t = strchr(buffer, '=')))
 {
  t[0]=0;
  t+=sizeof(char);
  while(t[0]==' ' && t[0])t+=sizeof(char);
  if(!t[0])return(1);
  /* remove the spaces after the pref name */
  while(opt[strlen(opt)-1]==' ')opt[strlen(opt)-1]=0;
  
  /* char to int conversion if necessary */
  if(!strcmp(t, "yes"))val = 1;
  if(!strcmp(t, "no"))val = 0;
  if(val == -1)
  {
   /* the string is not 'yes' nor 'no' so we take it as a string */
   value=emalloc(strlen(t)+1);
   strncpy(value, t, strlen(t));
   arg_add_value(arglist, opt, ARG_STRING, strlen(value), value);
  }
  else arg_add_value(arglist, opt, ARG_INT, sizeof(int), (void *)val);
  return(0);
 }
 else return(1);
}

void
preferences_save(plugins)
 struct arglist * plugins;
{
 FILE * fd;
 char * filename = preferences_get_filename();
 struct arglist * t;
 fd = fopen(filename , "w");
 if(!fd)
  {
  fprintf(stderr, "%s could not be opened write only\n", filename);
  efree(&filename);
  return;
  }
 fprintf(fd, "# This file was automagically created by nessus\n");
 t = Prefs;
 while(t && t->next)
 {
  if((int)t->type==ARG_INT)
   fprintf(fd, "%s = %s\n", t->name, t->value?"yes":"no");
  else if((t->type == ARG_STRING)&&(strlen(t->value)))
  	fprintf(fd, "%s = %s\n", t->name, (char *)t->value);
  t = t->next;
 }
 
 t = Prefs;
 
 while(t && t->next)
 {
  if(t->type == ARG_ARGLIST)
  {
   struct arglist * v;
   v = t->value;
   fprintf(fd, "begin(%s)\n", t->name);
   while(v && v->next)
   {
    if(!strcmp(v->name, "plugin_set")){
      v = v->next;
      continue;
      }
    if((int)v->type==ARG_INT)
     fprintf(fd, " %s = %s\n", v->name, v->value?"yes":"no");
    else if((v->type == ARG_STRING)&&(strlen(v->value)))
    	fprintf(fd, " %s = %s\n", v->name, (char *)v->value);
     v = v->next;
   }
  fprintf(fd, "end(%s)\n\n", t->name);
  }
  t = t->next;
 }
  fclose(fd);
}


void
pluginset_apply(plugins, name)
 struct arglist * plugins;
 char * name;
{
 while(plugins && plugins->next)
 {
  int flag = 0;
  struct arglist * t = arg_get_value(Prefs, name);
  if(!t)return;
  while(!flag && t && t->next)
  {
   if(!strcmp(plugins->name, t->name))
    {
     plug_set_launch(plugins->value, (int)t->value);
     flag = 1;
    }
    t = t->next;
   }
   plugins = plugins->next;
  }
 }
 
void
pluginset_reload(plugins, scanners)
 struct arglist * plugins;
 struct arglist * scanners;
{
  struct arglist * t = arg_get_value(Prefs, "PLUGIN_SET");
  if(!t){
   t = emalloc(sizeof(struct arglist));
   new_pluginset(t, plugins);
   arg_add_value(Prefs, "PLUGIN_SET", ARG_ARGLIST, -1, t);
   }
 else while(plugins && plugins->next)
 {
  int flag = 0;
   t = arg_get_value(Prefs, "PLUGIN_SET");
  if(!t)return;
  while(!flag && t && t->next)
  {
   if(!strcmp(plugins->name, t->name))
   {
    t->value = (void *)plug_get_launch(plugins->value);
    flag = 1;
   }
  t = t->next;
  }
  plugins = plugins->next;
 }
 
 t = arg_get_value(Prefs, "SCANNER_SET");
 if(!t){
   t = emalloc(sizeof(struct arglist));
   new_pluginset(t, scanners);
   arg_add_value(Prefs, "SCANNER_SET", ARG_ARGLIST, -1, t);
   }
 else while(scanners && scanners->next)
 {
  int flag = 0;
   t = arg_get_value(Prefs, "SCANNER_SET");
  if(!t)return;
  while(!flag && t && t->next)
  {
   if(!strcmp(scanners->name, t->name))
   {
    t->value = (void *)plug_get_launch(scanners->value);
    flag = 1;
   }
  t = t->next;
  }
  scanners = scanners->next;
 }
}


static void new_pluginset(pluginset, plugins)
 struct arglist * pluginset;
 struct arglist * plugins;
{
 while(plugins && plugins->next)
 {
  arg_add_value(pluginset, plugins->name, ARG_INT, sizeof(int),
  	 (void *)plug_get_launch(plugins->value));
  plugins = plugins->next;
 }
}
