/* OpenVAS
 * Copyright (C) 1998 - 2001 Renaud Deraison
 * Copyright (C) 2004 Intevation GmbH
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation
 *
 * 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.
 *
 * In addition, as a special exception, Renaud Deraison
 * gives permission to link the code of this program with any
 * version of the OpenSSL library which is distributed under a
 * license identical to that listed in the included COPYING.OpenSSL
 * file, and distribute linked combinations including the two.
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.  If you modify
 * this file, you may extend this exception to your version of the
 * file, but you are not obligated to do so.  If you do not wish to
 * do so, delete this exception statement from your version.
 *
 */

/**
 * @file
 * Maps the content of the openvasrc file to memory.
 */

#include <includes.h>

#ifdef USE_GTK
# include <gtk/gtk.h>
#endif

#include <glib.h>

#include "nessus_plugin.h"
#include "nessus_i18n.h"
#include "context.h"
#include "preferences.h"
#include "globals.h"
#include "nessus.h"
#include "error_dlg.h"

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

int
preferences_init (struct context *context)
{
  int result;

  context->prefs = emalloc(sizeof(struct arglist));
  result = preferences_process(context);
  if(result && getenv("NESSUSHOME") == 0)
    show_error_and_wait(CANNOT_SET_HOMEVAR);
  return (result);
}

/*
 * TODO : Under NT, the preference file should be put
 * at a propper place 
 * FIXED: nt used peks, which supports that feature
 */
/**
 * @brief Returns the location of the contexts openvasrc.
 * 
 * @param context Context of interest.
 * 
 * @return The location of the contexts openvasrc.
 */
gchar *
preferences_get_filename (struct context * context)
{
  gchar * openvasrc;

  if(context->dir)
  {
    openvasrc = g_build_filename(context->dir, "openvasrc", NULL);
  }
  else
  {
    if(Alt_rcfile)
      openvasrc = estrdup(Alt_rcfile);
    else
    {
      gchar *home = prefs_get_nessushome();

      openvasrc = g_build_filename (home, ".openvasrc", NULL);
    }
  }
  return openvasrc;
}

char *
preferences_get_altname (struct context *context, const char *ext)
{
  gchar *nessusrc = preferences_get_filename (context);

  if (ext && strlen(ext))
  {
    gchar *ret = g_strdup_printf ("%s.%s", nessusrc, ext);
    g_free (nessusrc);
    return ret;
  }
  else
    return nessusrc;
}

/**
 * @return -1 in case of error, 0 otherwise.
 */
static int
preferences_new (struct context* context)
{
  FILE *f;
  gchar *fn = preferences_get_filename(context);

  if(!fn)
    return (-1);

  if(!(f = fopen(fn, "w")))
  {
    show_error_and_wait(_("Error writing %s: %s"),
	fn, strerror(errno));
    return -1;
  }

  if(context->type != CONTEXT_TASK && context->type != CONTEXT_REPORT)
  {
    fprintf(f, _("# OpenVAS-Client Preferences File\n\n"));
    fprintf(f, "trusted_ca = cacert.pem\n");
    fprintf(f, "begin(SCANNER_SET)\n");
    fprintf(f, " 1.3.6.1.4.1.25623.1.0.10180 = yes\n");
    fprintf(f, " 1.3.6.1.4.1.25623.1.0.10278 = no\n");
    fprintf(f, " 1.3.6.1.4.1.25623.1.0.10331 = no\n");
    fprintf(f, " 1.3.6.1.4.1.25623.1.0.10335 = yes\n");
    fprintf(f, " 1.3.6.1.4.1.25623.1.0.10841 = no\n");
    fprintf(f, " 1.3.6.1.4.1.25623.1.0.10336 = no\n");
    fprintf(f, " 1.3.6.1.4.1.25623.1.0.10796 = no\n");
    fprintf(f, " 1.3.6.1.4.1.25623.1.0.11219 = no\n");
    fprintf(f, " 1.3.6.1.4.1.25623.1.0.14259 = no\n");
    fprintf(f, " 1.3.6.1.4.1.25623.1.0.14272 = no\n");
    fprintf(f, " 1.3.6.1.4.1.25623.1.0.14274 = no\n");
    fprintf(f, " 1.3.6.1.4.1.25623.1.0.14663 = no\n");
    fprintf(f, "end(SCANNER_SET)\n\n");

    fprintf(f, "begin(SERVER_PREFS)\n");
    fprintf(f, " max_hosts = 20\n");
    fprintf(f, " max_checks = 4\n");
    fprintf(f, " plugins_timeout = 320\n");
    /*
     * FIXME: see prefs_dialog_apply()
     * start of values from nessusd/preferences.c
     */
    fprintf(f, " cgi_path = /cgi-bin:/scripts\n");
    fprintf(f, " port_range = default\n");
    fprintf(f, " auto_enable_dependencies = yes\n");
    fprintf(f, " silent_dependencies = no\n");
    /* end of values from nessusd/preferences.c */
    fprintf(f, "end(SERVER_PREFS)\n");
  }

  fclose (f);
  chmod (fn, 0600);
  g_free (fn);
  return (0);
}

int
preferences_process (struct context *context)
{
  return preferences_process_filename(context, NULL);
}

int
preferences_process_filename (struct context *context, gchar *filename)
{
  FILE *fd;
  char *buffer;
  int default_filename = !filename;

  if(default_filename)
  {
    filename = preferences_get_filename (context);
    chmod(filename, 0600);
  }

  if(!(fd = fopen(filename, "r")))
  {
#ifndef NESSUSNT
    if(errno == EACCES)
    {
      show_error_and_wait(
	  _("The OpenVAS-Client doesn't have the right to read %s\n"),
	  filename);
      efree(&filename);
      return (1);
    }
#endif
    if(default_filename)
    {
      if(context->type == CONTEXT_TASK || context->type == CONTEXT_REPORT)
      {
	efree(&filename);
        return 0;
      }
#ifdef DEBUG
      show_info(_("Couldn't find prefs file... Creating a new one..."));
#endif
      if((preferences_new(context)) < 0)
      {
	show_error_and_wait(_("Error creating %s: %s"),
	    filename, strerror(errno));
	efree(&filename);
	return (1);
      }
      else if(!(fd = fopen(filename, "r")))
      {
	show_error_and_wait(_("Error creating %s: %s"),
	    filename, strerror(errno));
	efree(&filename);
	return(2);
      }
    }
    else
    {
      show_error(_("Error reading %s: %s"),
	  filename, strerror(errno));
	efree(&filename);
      return (1);
    }
  }

  /* Parse file that has been opened without error */
  buffer = emalloc(4096);
  while(!feof(fd) && fgets(buffer, 4096, fd))
  {
    if(strchr(buffer, '='))
      prefs_buffer_parse(buffer, context->prefs, 1);
    else if(!strncmp(buffer, "begin(",
                      strlen("begin("))) /* RATS: ignore, string literals are nul terminated */
    {
      char *t = buffer + (strlen("begin(") * sizeof(char)); /* RATS: ignore, string literals are nul terminated */
      char *end = strchr(t, ')');

      if(!end)
	show_warning(_("Parse error in %s: %s"), filename, buffer);
      else
      {
	char *category_name;
	struct arglist *subcategory;

	end[0] = 0;
	category_name = emalloc(strlen(t) + 1);
	strncpy(category_name, t, strlen(t));
	subcategory = emalloc(sizeof(struct arglist));
	/* When parsing a plugin set the order is not important and we
	 * can use a faster method to build the arglist */
	if(prefs_add_subcategory(subcategory, fd,
		strcmp(category_name, "PLUGIN_SET") != 0))
	  show_warning(_("Missing 'end' in %s"), filename);
	else
	  arg_add_value(context->prefs, category_name, ARG_ARGLIST, -1, subcategory);
      }
    }
  }
  fclose(fd);
  efree(&filename);
  return (0);
}

static int
prefs_add_subcategory (struct arglist *arglist, FILE *fd, int keep_order)
{
  char *buffer = emalloc(4096);
  int flag = 0;

  while(!flag && !feof(fd) && fgets(buffer, 4096, fd))
  {
    if(!strlen(buffer))
      return (1);
    if((!strcmp(buffer, "end\n")) || (!strncmp(buffer, "end(", 4)))
      flag = 1;
    else
      prefs_buffer_parse(buffer, arglist, keep_order);
    bzero(buffer, 255);
  }
  efree(&buffer);
  return (0);
}

/**
 * Workaround to cope with internal storage and communication of preference values.
 * @return TRUE if in argl a key prefname has either the value 'yes' or 1,
 *         FALSE otherwise.
 */
gboolean
preferences_yes_or_one (struct arglist* argl, char* prefname)
{
  // Check various malconditions
  if (argl == NULL || prefname == NULL)
    return FALSE;

  // Check for int and 1 or string and 'yes'
  void* argval = arg_get_value (argl, prefname);
  int argtype = GPOINTER_TO_INT(arg_get_type (argl, prefname));
  
  if (    (argtype == ARG_INT    && GPOINTER_TO_INT(argval) == 1)
       || (argtype == ARG_STRING && argval && !strcmp(argval, "yes")) )
    return TRUE;
  
  return FALSE;
}

static int
prefs_buffer_parse (char *buffer, struct arglist *arglist, int keep_order)
{
  char *t;
  char *opt;
  char *value;
  int val = -1;

  /* If we need to keep the order, we have to use arg_add_value,
   * otherwise we can use the faster (for long lists much faster)
   * arg_add_value_at_head */
  void (*arg_add)(struct arglist *, const char *, int, long, void *)
    = keep_order ? arg_add_value : arg_add_value_at_head;

  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(!strcmp(opt, "paranoia_level") || !strcmp(opt, "nessusd_port") ||
        !strcmp(opt, "sort_order") || !strcmp(opt, "protocol_version"))
    {
      arg_add(arglist, opt, ARG_INT, sizeof(gpointer), GSIZE_TO_POINTER(atoi(t)));
    }
    else
    {
      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(arglist, opt, ARG_STRING, strlen(value), value);
      }
      else
	arg_add(arglist, opt, ARG_INT, sizeof(gpointer), GSIZE_TO_POINTER(val));
    }
    return (0);
  }
  else
    return (1);
}


static void
new_pluginset (struct arglist *pluginset, struct nessus_plugin *plugins)
{
  while(plugins != NULL )
  {
    gchar* name = g_strdup (plugins->oid);
    arg_add_value(pluginset, name, ARG_INT, sizeof(gpointer), GSIZE_TO_POINTER(plugins->enabled != 0) );
    plugins = plugins->next;
    g_free (name);
  }
}

/**
 * @brief Gets or creates the plugin set (arglist) with name \<name\> of a 
 * @brief context.
 * 
 * If the set is created, all plugins in \<plugins\> are used to create the set.
 */
struct arglist *
prefs_get_pluginset (struct context *context, char *name,
                     struct nessus_plugin *plugins)
{
  struct arglist *plugin_set = arg_get_value(context->prefs, name);

  if(!plugin_set)
  {
    plugin_set = emalloc(sizeof(struct arglist));
    arg_add_value(context->prefs, name, ARG_ARGLIST, -1, plugin_set);
    new_pluginset(plugin_set, plugins);
  }
  return plugin_set;
}

/*
 * TODO: This whole private hash implementation should be replaced by
 * a adequate fast search method from glib or a least be consolidated
 * with other hash implementation that occur in OpenVAS.
 * XXX: This hashing is probably less performant since it was migrated from
 * integer names to string names.
 */

#define MAGIC 8197
struct hash
{
  char * name;
  struct arglist *v;
  struct hash *next;
};

static struct arglist *
hash_get(struct hash **hash, const char * oid)
{
  int id, dummy;
  struct hash *h;

  sscanf(oid, "1.3.6.1.4.1.25623.1.%d.%d", &dummy, &id);
  h = hash[id % MAGIC];

  while(h != NULL)
  {
    if(!strcmp(h->name, oid))
      return h->v;
    h = h->next;
  }
  return NULL;
}

/**
 * @brief Update arglists "PLUGIN_SET" or "SCANNER_SET" from context->plugins
 * @brief or context->scanners.
 */
void
pluginset_reload (struct context *context, char *name, 
                  struct nessus_plugin *plugins)
{
  struct arglist *pluginset;
  struct hash **hash;
  struct hash *h;
  int i;

  if(!plugins)
    return;

  hash = emalloc(MAGIC * sizeof(struct hash *));
  pluginset = prefs_get_pluginset(context, name, plugins);

  while(pluginset->next)
  {
    int dummy, id;

    sscanf(pluginset->name, "1.3.6.1.4.1.25623.1.%d.%d", &dummy, &id);

    h = emalloc(sizeof(struct hash));
    h->name = pluginset->name;
    h->v = pluginset;
    h->next = hash[id % MAGIC];
    hash[id % MAGIC] = h;
    pluginset = pluginset->next;
  }

  while(plugins != NULL )
  {
    struct arglist *pluginset_entry = hash_get(hash, plugins->oid);

    if(pluginset_entry != NULL )
       pluginset_entry->value = GSIZE_TO_POINTER(plugins->enabled != 0 );
    plugins = plugins->next;
  }

  i = MAGIC;
  while(i--)
    efree(hash+i);
  efree(&hash);
}

void
preferences_save (struct context *context)
{
  char *filename = preferences_get_filename(context);

  preferences_save_as(context, filename);
  efree(&filename);
}

void
preferences_save_as (struct context *context, char *filename)
{
  FILE *fd;
  struct arglist *t;

  fd = fopen(filename, "w");
  if(!fd)
  {
    show_error(_("%s could not be opened write only"), filename);
    return;
  }
  chmod(filename, 0600);
  fprintf(fd, _("# This file was automatically created by OpenVAS-Client\n"));

  pluginset_reload(context, "PLUGIN_SET", context->plugins);
  pluginset_reload(context, "SCANNER_SET", context->scanners);

  t = context->prefs;
  while(t && t->next)
  {
    if((int)t->type == ARG_INT)
    {
      if(!strcmp(t->name, "paranoia_level") || !strcmp(t->name, "nessusd_port")
          || !strcmp(t->name, "sort_order") || !strcmp(t->name, "protocol_version"))
	fprintf(fd, "%s = %d\n", t->name, (int)GPOINTER_TO_SIZE(t->value));
      else
	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;
  }

  fprintf(fd, "\n");

  t = context->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(v->type == ARG_INT)
	  fprintf(fd, " %s = %s\n", v->name, v->value ? "yes" : "no");
	else if((v->type == ARG_STRING) && v->value)
	{
	  if(!strcmp(t->name, "PLUGINS_PREFS") || ((char *)v->value)[0])
	    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);
}


int
preferences_generate_new_file (struct context *context, const char *name)
{
  struct context *copy_from = context->parent;

  if(!context->prefs)
  {
    if(context->type == CONTEXT_TASK)
      copy_from = NULL;
    else if(context->type == CONTEXT_SCOPE)
      copy_from = Global;

    if(copy_from)
    {
      context_type type = copy_from->type;
      gchar *filename = preferences_get_filename (context);

      copy_from->type = context->type;
      preferences_save_as(copy_from, filename);
      copy_from->type = type;
      g_free (filename);
    }
    else
      preferences_new(context);

    preferences_init(context);
  }
  if(name && name[0])
    prefs_set_string(context, "name", name);
  preferences_save(context);
  return 0;
}


#ifdef USE_GTK
gboolean
prefs_is_executable (gchar *name)
{
  gchar *prog = g_find_program_in_path (name);

  if (prog)
    {
      g_free (prog);
      return TRUE;
    }
  else
    return FALSE;
}
#endif /* USE_GTK */

void *
prefs_get_default (struct context *context, const char *name)
{
  if(!strcmp(name, "name"))
  {
    char *name = NULL;
    if(context->type == CONTEXT_GLOBAL)
      name = _("Global Settings");
    else if(context->dir)
      name = strrchr(context->dir, '/') + 1;
    if(!name)
      name = NULL;
    return name;
  }
  else if(context->parent)
    return prefs_get_default(context->parent, name);
  else if(!strcmp(name, "nessusd_host"))
#ifdef DEFAULT_SERVER
    return DEFAULT_SERVER;
#else
    return "localhost";
#endif
  else if(!strcmp(name, "nessusd_user"))
    return get_username();
  else if(!strcmp(name, "nessusd_port"))
    return (void *)OPENVAS_IANA_OTP_PORT;
  else if(!strcmp(name, "ssl_version"))
    return SSL_VER_DEF_NAME;
  else if(!strcmp(name, "nessus_dir"))
  {
    static char *nessus_dir;
    if(!nessus_dir)
    {
      char *nessushome = prefs_get_nessushome();
      nessus_dir = g_build_filename( nessushome, ".openvas", NULL);
    }
    return nessus_dir;
  }
#ifdef USE_GTK
  else if(!strcmp(name, "pdfviewer"))
  {
    static char *pdfviewer;
    if(!pdfviewer)
    {
      if(prefs_is_executable("gpdf"))
	pdfviewer = "gpdf";
      else if(prefs_is_executable("kpdf"))
	pdfviewer = "kpdf";
      else if(prefs_is_executable("xpdf"))
	pdfviewer = "xpdf";
      else if(prefs_is_executable("ggv"))
	pdfviewer = "ggv";
      else if(prefs_is_executable("gv"))
	pdfviewer = "gv";
      else if(prefs_is_executable("acroread"))
	pdfviewer = "acroread";
      else if(prefs_is_executable("acrord32"))
	pdfviewer = "acrord32";
    }
    return pdfviewer;
  }
#endif /* USE_GTK */
  else if(!strcmp(name, "url_cve"))
    return "http://cve.mitre.org/cgi-bin/cvename.cgi?name=%s-%s-%s";
  else if(!strcmp(name, "url_bid"))
    return "http://www.securityfocus.com/bid/%s";
  else if(!strcmp(name, "url_nessus"))
    return "http://www.openvas.org/?oid=%s";
  else if(!strcmp(name, "targets"))
    return "localhost";
  else if(!strcmp(name, "tree_autoexpand"))
    return (void *)1;
  else if(!strcmp(name, "cache_plugin_information"))
    return (void *)1;
  else if(!strcmp(name, "reports_use_plugin_cache"))
    return (void *)1;
  else if(!strcmp(name, "scopes_load_plugin_cache_immediately"))
    return (void *)1;
  else if(!strcmp(name, "report_plugin_details_in_pdf"))
    return (void *)1;
  else if(!strcmp(name, "show_nvt_name_and_oid"))
    return (void *)1;
  else if(!strcmp(name, "auto_enable_new_plugins"))
    return (void *)1;
  else
    return NULL;
}

/**
 * @brief Returns either the user-set value of a preference or its Default.
 * 
 * As this function returns a void pointer, it is wrapped in the macros
 * prefs_get_int and prefs_get_string. Use these, as you should know which
 * type you expect.
 * 
 * @param context Context to query preferences of.
 * @param name    Preference name (e.g. "trusted_ca").
 * 
 * @return Value of unknown type (thus use prefs_get_int or prefs_get_string).
 */
void *
prefs_get_value (struct context *context, const char *name)
{
  void *value = arg_get_value(context->prefs, name);
  int type = arg_get_type(context->prefs, name);

  if(type < 0 || (type != ARG_INT && value == NULL)
      || (type == ARG_STRING && !strlen((const char *)value)))
    value = prefs_get_default (context, name);
  return value;
}

void
prefs_set_value (struct context *context, const char *name, void *value,
                 int type)
{
  int len;
  int arg_type;

  switch (type)
  {
    case ARG_INT:
      len = sizeof(int);
      break;
    case ARG_STRING:
      len = strlen(value);
      value = estrdup((char *)value);
      break;
    default:
      show_error(_("prefs_set_value() called with illegal type"));
      return;
  }

  arg_type = arg_get_type(context->prefs, name);
  if(arg_type < 0)
    arg_add_value(context->prefs, name, type, len, value);
  else if(arg_type == type)
  {
    if(type == ARG_STRING)
    {
      char *old_value = arg_get_value(context->prefs, name);

      efree(&old_value);
    }
    arg_set_value(context->prefs, name, len, value);
  }
  else
  {
    if(type == ARG_STRING)
      efree(&value);
    show_error(_("prefs_set_value() called with illegal type"));
  }
}

void
prefs_set_int (struct context *context, const char *name, int value)
{
  prefs_set_value(context, name, GSIZE_TO_POINTER(value), ARG_INT);
}

void
prefs_set_string (struct context *context, const char *name, const char *value)
{
  const char *default_value = prefs_get_default(context, name);
  if(!default_value || strcmp(value, default_value))
    prefs_set_value(context, name, (void *)value, ARG_STRING);
  else
    prefs_set_value(context, name, (void *)"", ARG_STRING);
}

/* are there any options besides "name" and "comment"? */
int
prefs_has_options (struct context *context)
{
  struct arglist *t = context->prefs;

  while(t && t->next)
  {
    if(strcmp(t->name, "name") && strcmp(t->name, "comment"))
      return 1;
    t = t->next;
  }

  return 0;
}


/**
 * @return The name of the current user or empty string if none found.
 */
static char *
get_username(void)
{
#ifdef _WIN32
  char *user;

  /* Look up the user's name. */
  user = getenv("USERNAME");
  if(user)
    return user;

  return "";
#else
  char *user;
  struct passwd *pwd;

  /* Look up the user's name. */
  user = getenv("USER");
  if(user)
    return user;

  user = getenv("LOGNAME");
  if(user)
    return user;

  pwd = getpwuid(getuid());
  if(pwd && pwd->pw_name)
    return pwd->pw_name;

  return "";
#endif
}


#ifdef _WIN32
#include <windows.h>
#include <shlobj.h>
#include <io.h>

#define OPENVAS_DEFAULT_HOMEDIR "c:\openvas"
#define RTLD_LAZY 0

static __inline__ void *
dlopen (const char * name, int flag)
{
  void * hd = LoadLibrary (name);
  return hd;
}

static __inline__ void *
dlsym (void * hd, const char * sym)
{
  if (hd && sym)
    {
      void * fnc = GetProcAddress (hd, sym);
      if (!fnc)
        return NULL;
      return fnc;
    }
  return NULL;
}

static __inline__ int
dlclose (void * hd)
{
  if (hd)
    {
      FreeLibrary (hd);
      return 0;
    }
  return -1;
}  


/* This is a helper function to load and run a Windows function from
   either of one DLLs. */
static HRESULT
w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
{
  static int initialized;
  static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);

  if (!initialized)
    {
      static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
      void *handle;
      int i;

      initialized = 1;

      for (i=0, handle = NULL; !handle && dllnames[i]; i++)
        {
          handle = dlopen (dllnames[i], RTLD_LAZY);
          if (handle)
            {
              func = dlsym (handle, "SHGetFolderPathA");
              if (!func)
                {
                  dlclose (handle);
                  handle = NULL;
                }
            }
        }
    }

  if (func)
    return func (a,b,c,d,e);
  else
    return -1;
}

#ifndef CSIDL_APPDATA
#define CSIDL_APPDATA 0x001a
#endif
#ifndef CSIDL_LOCAL_APPDATA
#define CSIDL_LOCAL_APPDATA 0x001c
#endif
#ifndef CSIDL_COMMON_APPDATA
#define CSIDL_COMMON_APPDATA 0x0023
#endif
#ifndef CSIDL_FLAG_CREATE
#define CSIDL_FLAG_CREATE 0x8000
#endif

/* Get the standard home directory.  This function does not consider a
   registry value (under W32) or the environment variable.  */
static char *
standard_homedir (void)
{
  static char *dir;

  if (!dir)
    {
      char path[MAX_PATH];

      /* It might be better to use LOCAL_APPDATA because this is
         defined as "non roaming" and thus more likely to be kept
         locally.  For private keys this is desired.  However, given
         that many users copy private keys anyway forth and back,
         using a system roaming services might be better than to let
         them do it manually.  A security conscious user will anyway
         use the registry entry to have better control.  */
      if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
                               NULL, 0, path) >= 0) 
        {
      dir = malloc (strlen (path) + 1);
      strcpy (dir, path);
    }
      else
        dir = OPENVAS_DEFAULT_HOMEDIR;
    }
  return dir;
}

#endif

char *
prefs_get_nessushome(void)
{
  char *home;

  home = getenv("NESSUSHOME");
  if (home)
    return home;

#ifdef _WIN32
  return standard_homedir ();
#else

  struct passwd *pwd;

  home = getenv ("HOME");
  if (home)
    return home;

  pwd = getpwuid (getuid());
  if (pwd && pwd->pw_dir)
    return pwd->pw_dir;

  return "";
#endif
}
