/* Nessus
 * Copyright (C) 1998 - 2001 Renaud Deraison
 *
 * 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.
 *
 */

#include <includes.h>
/* for g_sprintf */
#include <glib/gprintf.h>

#include "nessus_i18n.h"

#ifdef USE_GTK
#include <gtk/gtk.h>
#include "prefs_dialog/prefs_dialog.h"
#include "prefs_dialog/prefs_context.h"
#else
#include <glib.h>
#endif

#include "error_dlg.h"
#include "file_utils.h" /* for file_utils_ensure_dir */
#include "read_target_file.h"
#include "comm.h"
#include "auth.h"
#include "nessus.h"
#include "attack.h"
#include "report.h"
#include "parser.h"
#include "sighand.h"
#include "context.h"
#include "preferences.h"
#include "globals.h"
#include "corevers.h"
#include "filter.h"

#include "backend.h"
#include "nbe_output.h"
#include "html_output.h"
#include "html_graph_output.h"
#include "latex_output.h"
#include "text_output.h"
#include "xml_output_ng.h"
#include "plugin_cache.h"
#include "severity_filter.h"

#include "cli.h"

#include <openssl/x509v3.h>

#include "sslui.h"

#ifndef INADDR_NONE
#define INADDR_NONE 0xFFFFFFFF
#endif

#ifndef inc_optind
#define inc_optind() (optind++)
#endif

/**
 * @mainpage
 *
 * @section Introduction
 * @verbinclude README
 * 
 * @section License Information
 * @verbinclude COPYING
 */

/**
 * \file
 * Starting point for both the GUI and the CLI client.
 */

/**
 * @brief Arglist holding the GUI elements (most of them nested in other arglists).
 * 
 * Following overview is not complete yet and should help to remove the dynamic
 * approach and replace this arglist by static structs representing the GUI.
 * 
 * MainDialog (arg)
 * - USER (arg)
 *   - FRAME
 * - SAVE_KB (arg)
 *   - FRAME
 * - COMMENT (arg)
 *   - VBOX
 *   - REPORT
 * - SCAN_OPTIONS (arg)
 *   - FRAME
 * - PLUGINS (arg)
 *   - SILENT_DEPTS
 *   - ENABLE_DEPS_AT_RUNTIME
 *   - FRAME
 *   - AUTO_ENABLE_PLUGINS
 *   - PLUGIN_TREE
 *   - FILTER_LABEL
 * - NOTEBOOK
 * - VBOX
 * - REPORT (arg)
 *   - FRAME
 *   - BE
 *   - ORDER_COMBOBOX
 *   - REPORT_CONTEXT
 *   - REPORT (GtkTextView)
 *   - REPORT_TREE_VIEW
 *   - SCAN_TIMESTAMPS
 *   - VBOX
 * - TARGET (arg)
 *   - FRAME
 *   - DNS_EXPAND
 *   - READ_FROM_FILE
 *   - TARGET
 * - PLUGIN_PREFS (arg)
 *   - FRAME_CREDENTIALS
 *   - PLUGIN_CREDENTIALS
 *   - FRAME
 *   - PLUGIN_PREFS
 * - SCAN_OPTIONS (arg)
 *   - FRAME
 *   - PING_HOSTS
 *   - PORT_RANGE
 *   - UNSCANNED_CLOSED
 *   - MAX_HOSTS
 *   - MAX_CHECKS
 *   - CGI_PATH
 *   - REVERSE_LOOKUP
 *   - OPTIMIZE_TEST
 *   - SAFE_CHECKS
 *   - USE_MAC_ADDR
 *   - SCANNERS_LIST
 */
struct arglist * MainDialog;

extern severity_filter_t* global_filter;

char * Alt_rcfile = NULL;
struct plugin_filter Filter;
static gboolean dont_check_ssl_cert = FALSE;
gboolean quiet_mode   = FALSE;
gboolean show_pixmaps = FALSE;
int F_show_pixmaps;
int F_quiet_mode;
int ListOnly = 0;

/**
 * @brief Initialize I18N support, if possible
 */
static void
i18n_init (void)
{
#ifdef HAVE_GNU_GETTEXT
  setlocale (LC_ALL, "" );

#ifdef CYGWIN
  char* localedir = g_win32_get_package_installation_subdirectory(NULL, NULL,
                      "share\\locale");

#ifdef LANGUAGE
  g_setenv("LANGUAGE", LANGUAGE, FALSE);
#endif /* LANGUAGE */

  bindtextdomain (PACKAGE, localedir);
#else /* CYGWIN */
  bindtextdomain (PACKAGE, LOCALEDIR);
#endif /* CYGWIN */

  bind_textdomain_codeset (PACKAGE, "UTF-8");
  textdomain (PACKAGE);
#endif
}


#define CLN_AUTH_SRV 1

static int
verify_callback (preverify_ok, ctx)
     int		preverify_ok;
     X509_STORE_CTX	*ctx;
{
#if DEBUG_SSL > 1
  fprintf(stderr, "verify_callback> preverify_ok=%d\n", preverify_ok);
#endif
  return preverify_ok;
}



#ifdef CLN_AUTH_SRV

/**
 * @brief Split a line "var=value" into two components.
 * 
 * @param line A line. Attention, will be modified!
 * @param[out] var If returned 2 contains the var name (points into \<line\> parameter).
 * @param[out] val If returned 2 contains the value (points into \<line\> parameter).
 * 
 * @return 0 if '=' was not found, 1 if line looks like "var=", 2 if OK.
 */
static int
split_var_val (const char *line, char* var, char* val)
{
  const char *p;
  char *q;

  for (p = line, q = var; *p != '=' && *p != '\0' && *p != '\n'; p ++, q ++)
    *q = *p;
  *q = '\0';
  if (*p == '\0')
    return 0;

  for (q = val, p ++; *p != '\0' && *p != '\n'; p ++, q ++)
    *q = *p;
  *q = '\0';
  return q == line ? 1 : 2;
}

/**
 * Returns -1 if error, 0 if hash not found, 1 if found
 */
static int
get_server_cert_hash(sname, hash)
     const char		*sname;
     unsigned char	*hash;
{
  char	*fname;
  FILE	*fp;
  char	line[1024];
  char	ho[1024], ha[1024];
  int	i, x;


  if ((fname = preferences_get_altname(Global, "cert")) == NULL)
    return -1;

  fp = fopen(fname, "r");
  efree(&fname);
  if (fp == NULL)
    {
      if (errno == ENOENT)
	return 0;
      else
	return -1;
    }

  while (fgets(line, sizeof(line), fp) != NULL)
    {
      if (split_var_val(line, ho, ha) == 2)
	{
	  if (strcmp(ho, sname) == 0 && strlen(ha) == SHA_DIGEST_LENGTH * 2)
	    {
	      for (i = 0; i < SHA_DIGEST_LENGTH; i ++)
		{
		  (void) sscanf(ha + 2 * i, "%2x", &x);
		  hash[i] = x;
		}
	      fclose(fp);
	      return 1;
	    }
	}
    }

  if (ferror(fp))
    return -1;
  else
    return 0;
}

/**
 * @brief Prints hash into a string.
 * 
 * @param hash_str[out] String to contain hash, needs to be 2* SHA_DIGEST_LENGTH
 *                      bytes long.
 */
static void
print_hash(hash_str, hash)
     char	*hash_str;
     const unsigned char	*hash;
{
  int i;

  for (i = 0; i < SHA_DIGEST_LENGTH; i ++)
    sprintf(hash_str + 2 * i, "%02x", hash[i]);
}

static int
set_server_cert_hash(sname, hash)
     const char		*sname;
     unsigned char	*hash;
{
  char	ho[1024], ha[1024];
  char	*fname = NULL;
  FILE	*fp1 = NULL, *fp2 = NULL;
  char	line[1024];
  int	x;
  int	found;

  if ((fname = preferences_get_altname(Global, "cert")) == NULL)
    return -1;

  if ((fp2 = tmpfile()) == NULL)
    goto error;

  fp1 = fopen(fname, "r");
  if (fp1 == NULL && errno != ENOENT)
    goto error;

  found = 0;
  if (fp1 != NULL)
    {
      while (fgets(line, sizeof(line), fp1) != NULL)
	{
	  x = strlen(line); /* RATS: ignore, fgets ensured nul termination */
	  if (x > 0 && line[x - 1] != '\n') /* invalid line */
	    continue;

	  if (split_var_val(line, ho, ha) == 2)
	    {
	      if (strcmp(ho, sname) == 0)
	      {
		if (found) /* multiple lines */
		  continue;
		else
		  {
		    print_hash(ha, hash);
		    sprintf(line, "%s=%s\n", ho, ha);
		    found = 1;
		  }
	      }
	    }
	  if (fputs(line, fp2) < 0)
	    goto	error;
	}
      (void) fclose(fp1);
    }

  if (! found)
    {
      print_hash(ha, hash);
      sprintf(line, "%s=%s\n", sname, ha);
      if (fputs(line, fp2) < 0)
	goto error;
    }

  rewind(fp2);
  if ((fp1 = fopen(fname, "w")) == NULL)
    goto error;

  while (fgets(line, sizeof(line), fp2) != NULL)
    (void) fputs(line, fp1);

  if (ferror(fp1) || fclose(fp1) < 0)
    goto error;
  (void) fclose(fp2);		/* auto delete */
  efree(&fname);
  return 0;

 error:
  if (fp1 != NULL)
    fclose(fp1);
  if (fp2 != NULL)
    fclose(fp2);
  if (fname != NULL)
    efree(&fname);

  return -1;
}
#endif /* CLN_AUTH_SRV */


/**
 * @brief Establishes the connection between openvas-client and openvasd, logs
 * @brief in and reads the plugin list from the server.
 *
 * @return NULL in case of success, error message text otherwise.
 */
char *
connect_to_nessusd (struct context * context)
{
  const char * hostname;
  gchar * certhash;
  int port;
  const char * login;
#ifdef CLN_AUTH_SRV
  int paranoia_level;
  /*
   * 0: not initialised.
   * 1: remember certificate
   * 2: trust CA
   * 3: trust CA & check certificate
   */
#endif
  static SSL_CTX    *ssl_ctx = NULL;
  static SSL_METHOD *ssl_mt  = NULL;
  SSL *ssl                   = NULL;
  const char *cert, *key, *client_ca, *trusted_ca, *ssl_ver;
  const char *ssl_cipher_list;
  STACK_OF(X509_NAME) *cert_names;
  int use_client_cert = prefs_get_int(context, "use_client_cert");
  char *pem_pass = NULL;
  int soc, soc2;
  int opt;
  const char *passwd;


  if(prefs_get_int(Global, "nessusd_autoconnect"))
    passwd = estrdup(context->passwd);
  else
  {
    /* Don't cache the password */
    passwd = context->passwd;
    context->passwd = NULL;
  }

  hostname = prefs_get_string(context, "nessusd_host");
  port = prefs_get_int(context, "nessusd_port");
  login = prefs_get_string(context, "nessusd_user");

  soc = open_sock_tcp_hn(hostname, port);

  if(soc<0)
  	{
  	static char err_msg[1024];
  	struct in_addr a = nn_resolve(hostname);
	if(a.s_addr == INADDR_NONE) 
		return _("Host not found!");
  	else
		{
		snprintf(err_msg, sizeof(err_msg), _("Could not open a connection to %s\n"), hostname);
  		return err_msg;
		}
  	}

  opt = 1;
  setsockopt(soc, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));

  ssl_ver = prefs_get_string(context, "ssl_version");
  if (!strcasecmp(ssl_ver, "NONE"))
    ssl_ver = prefs_get_default(context, "ssl_version");

  if (strcasecmp(ssl_ver, "NONE") != 0)
    {
#ifdef CLN_AUTH_SRV
      paranoia_level = prefs_get_int(context, "paranoia_level");
      if(!paranoia_level && !dont_check_ssl_cert)
      {
	paranoia_level = sslui_ask_paranoia_level();
	if(paranoia_level >= 1 && paranoia_level <= 3)
	  prefs_set_int(context, "paranoia_level", paranoia_level);
      }
#endif

      if(nessus_SSL_init(NULL) < 0)
	{
	  return(_("Could not initialize the OpenSSL library !\n\
Please launch openvasclient-mkrand(1) first !"));
	}
      if (ssl_mt == NULL)
	{
	  if (strcasecmp(ssl_ver, "SSLv2") == 0)
	    ssl_mt = SSLv2_client_method();
	  else if (strcasecmp(ssl_ver, "SSLv3") == 0)
	    ssl_mt = SSLv3_client_method();
	  else if (strcasecmp(ssl_ver, "SSLv23") == 0)
	    ssl_mt = SSLv23_client_method();
	  else if (strcasecmp(ssl_ver, "TLSv1") == 0)
	    ssl_mt = TLSv1_client_method();
	  else
	    {
	      show_warning(
		  _("Unknown SSL version \"%s\"\nUsing default: %s"),
		  ssl_ver, SSL_VER_DEF_NAME);
	      ssl_ver = SSL_VER_DEF_NAME;
	      ssl_mt = SSL_VER_DEF_METH();
	    }

	  if (ssl_mt == NULL)
	    {
	      char	s[32];
	      sprintf(s, "%s_client_method", ssl_ver);
	      sslerror(s);
	      return "SSL error";
	    }
	}

      if (ssl_ctx == NULL)
	if ((ssl_ctx = SSL_CTX_new(ssl_mt)) == NULL)
	  {
	    sslerror("SSL_CTX_new");
	    return "SSL error";
	  }

      if (SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL) < 0)
	sslerror("SSL_CTX_set_options(SSL_OP_ALL)");

#define NOEXP_CIPHER_LIST "EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DES-CBC3-SHA:DES-CBC3-MD5:DHE-DSS-RC4-SHA:IDEA-CBC-SHA:RC4-SHA:RC4-MD5:IDEA-CBC-MD5:RC2-CBC-MD5:RC4-MD5:RC4-64-MD5:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DES-CBC-SHA:DES-CBC-MD5"
#define STRONG_CIPHER_LIST "EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DES-CBC3-SHA:DES-CBC3-MD5:DHE-DSS-RC4-SHA:IDEA-CBC-SHA:RC4-SHA:RC4-MD5:IDEA-CBC-MD5:RC2-CBC-MD5:RC4-MD5"
#define EDH_CIPHER_LIST "EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DHE-DSS-RC4-SHA"
      ssl_cipher_list = prefs_get_string(context, "ssl_cipher_list");
      if (ssl_cipher_list != NULL && *ssl_cipher_list != '\0' )
	{
	  if (strcmp(ssl_cipher_list, "noexp") == 0)
	    ssl_cipher_list = NOEXP_CIPHER_LIST;
	  else if (strcmp(ssl_cipher_list, "strong") == 0)
	    ssl_cipher_list = STRONG_CIPHER_LIST;
	  else if (strcmp(ssl_cipher_list, "edh") == 0)
	    ssl_cipher_list = EDH_CIPHER_LIST;

	  if (! SSL_CTX_set_cipher_list(ssl_ctx, ssl_cipher_list))
	    sslerror("SSL_CTX_set_cipher_list");
	}

      if ((ssl = SSL_new(ssl_ctx)) == NULL)
	{
	  sslerror("SSL_new");
	  return "SSL_error";
	}

      if(use_client_cert)
      {
      cert = prefs_get_string(context, "cert_file");
      key = prefs_get_string(context, "key_file");
      client_ca = prefs_get_string(context, "client_ca");

      if(passwd && key)
	{
	  pem_pass = estrdup(passwd);
	  nessus_install_passwd_cb(ssl_ctx, pem_pass);
	  efree(&passwd);
	  passwd = estrdup("*"); /* Do not send it over the network */
	}

      if (cert != NULL)
	SSL_use_certificate_file(ssl, cert, SSL_FILETYPE_PEM);
      if (key != NULL)
	SSL_use_PrivateKey_file(ssl, key, SSL_FILETYPE_PEM);

      if (client_ca != NULL)
	{
	  cert_names = SSL_load_client_CA_file(client_ca);
	  if (cert_names != NULL)
	    SSL_CTX_set_client_CA_list(ssl_ctx, cert_names);
	  else
	    sslerror("SSL_load_client_CA_file");
	}
      }
#ifdef CLN_AUTH_SRV
      if (paranoia_level == 2 || paranoia_level == 3)
	{
	  trusted_ca = prefs_get_string(context, "trusted_ca");
	  if (trusted_ca == NULL)
	    {
	      show_warning(
		  _("paranoia_level=%d but \"trusted_ca\" not set"),
		  paranoia_level);
	      paranoia_level = 1;
	    }
	  else if (!check_exists(trusted_ca))
	    {
	      show_warning(
		  _("paranoia_level=%d but \"trusted_ca\" file not found:\n%s"),
		  paranoia_level, trusted_ca);
	      paranoia_level = 1;
	    }
	  else
	    {
	      SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, verify_callback);
#if 0
	      if (SSL_CTX_set_default_verify_paths(ssl_ctx) <= 0)
		sslerror("SSL_CTX_set_default_verify_paths");
#endif
	      if (! SSL_CTX_load_verify_locations(ssl_ctx, trusted_ca, NULL))
		{
		  sslerror("SSL_CTX_load_verify_locations");
		  show_warning(
		      _("Error while setting the trusted CA: %s\n"
			"SSL connections are likely to fail."),
		      trusted_ca);
		}
	    }
	}
#endif

      if (! SSL_set_fd(ssl, soc))
	{
	  sslerror("SSL_set_fd");
	  efree(&passwd);
	  efree(&pem_pass);
	  return "SSL error";
	}

      if (SSL_connect(ssl) <= 0)
	{
	  sslerror("SSL_connect");
	  efree(&passwd);
	  efree(&pem_pass);
	  return "SSL error";
	}

#if DEBUG_SSL > 1
      {
	char	*p = SSL_get_cipher(ssl);
	if (p != NULL)
	  fprintf(stderr, "SSL_get_cipher = %s\n", p);
      }
#endif

#ifdef CLN_AUTH_SRV
      if (!dont_check_ssl_cert && (paranoia_level == 1 || paranoia_level == 3))
	{
	  X509	*cert = SSL_get_peer_certificate(ssl);
	  char	stored_hash[SHA_DIGEST_LENGTH];

	  /*  Essentially store a key,value pair of 'hostname,port'=certificate hash
	   *  Allows instances of openvasd to have their own server certs w/out confusing the cli 
	   *
	   *  certhash is len(hostname)+1(comma)+5(port = )+1(\n) 
	   */
	  if((certhash = g_try_malloc(strlen(hostname)+10)) ==  NULL)
	  {
            fprintf(stderr, "could not allocate memory for certhash");
            return "Out of memory error";
	  }
	  g_sprintf(certhash,"%s,%d",hostname,port);

	  if (get_server_cert_hash(certhash, stored_hash) <= 0)
	    memset(stored_hash, 0, sizeof(stored_hash));

	  g_free(certhash);

	  if(cert == NULL)
	    {
	      sslerror("SSL_get_peer_certificate");
	      return _("SSL error: cannot get server certificate");
	      efree(&passwd);
	      efree(&pem_pass);
	    }
	  X509_check_purpose(cert, -1, 0); /* Make sure hash is correct */
	  if (memcmp(cert->sha1_hash, stored_hash, SHA_DIGEST_LENGTH) != 0)
	    {
	      int x = sslui_check_cert(ssl);
	      if(x < 0)
	      {
		efree(&passwd);
		efree(&pem_pass);
		return _("Invalid server certificate");
	      }

	      /* certhash is len(hostname)+1(comma)+5(16bitport = )+1(\n)  */
	      certhash = malloc(strlen(hostname)+10);

              if((certhash = g_try_malloc(strlen(hostname)+10)) ==  NULL)
              {
                fprintf(stderr, "could not allocate memory for certhash");
                return "Out of memory error";
              }
	      sprintf(certhash,"%s,%d",hostname,port);

	      if (set_server_cert_hash(certhash, cert->sha1_hash) < 0)
		perror(_("Could not save server certificate"));
	      g_free(certhash);
	    }
	}
#endif
    } /* ssl_ver != "NONE" */

  if ((soc2 = nessus_register_connection(soc, ssl)) <0)
    {
      shutdown(soc, 2);
      efree(&passwd);
      efree(&pem_pass);
      return _("Could not register the connection");
    }
  stream_set_buffer(soc2, 1024 * 1024);
  soc = soc2;

  context->socket = soc;

  if(comm_init(context->socket, PROTO_NAME) || (auth_login(context->socket, login, passwd)))
    {
      close_stream_connection(context->socket);
      efree(&pem_pass);
      context->socket = -1;
      efree(&passwd);
      return(_("Unable to establish a connection to the remote host using the specified protocol version!"));
    }

  if(comm_get_plugins(context))
  {
    close_stream_connection(context->socket);
    efree(&pem_pass);
    context->socket = -1;
    efree(&passwd);
    return(_("Login failed"));
  }

  comm_get_preferences(context);
  comm_get_rules(context);
  comm_get_dependencies(context);
  comm_get_certificates(context);

  if(prefs_get_int(Global, "cache_plugin_information") > 0)
  {
    plugin_cache_write(context, context->plugins_md5sum);
  }

#ifdef ENABLE_SAVE_TESTS
  if(comm_server_restores_sessions(context))
    context->sessions = comm_get_sessions(context);
#endif

  efree(&passwd);
  efree(&pem_pass);
  return(NULL);
}

/**
 * @brief Converts a nbe report into one of the supported formats (pdf, xml,
 * @brief html, tex etc).
 * 
 * Attention, this function calls exit(1) in case of errors and prints messages
 * to stdout or stderr.
 * 
 * @param in_report   nbe- file to read from.
 * @param out_report  File to write. If parameter output_type is not specified,
 *                    the type will be derived from the file extension.
 * @param output_type The type of output wished (can be NULL, then the type
 *                    will be derived from parameter output_reports file
 *                    extension).
 * @param filter_report If TRUE, the global severity filter will be applied
 *                      before report conversion.
 * @param drop_fp     If TRUE, Issues of severity level "False Positive" will be
 *                    dropped during html report conversion (makes sense only in
 *                    combination with parameter filter_report set to TRUE).
 * @param drop_no_info If TRUE, found ports and services with no further issues
 *                     (or LOG or DEBUG messages) will be dropped during html
 *                     report conversion.
 */
static void
convert_report (gchar* in_report, gchar* out_report,
                gchar* output_type, gboolean filter_report, gboolean drop_fp,
                gboolean drop_no_info)
{
  int be;

  // Fail if parameter is missing
  if (!((in_report != NULL) && (out_report != NULL)))
    {
      printf (_("You need to specify an input file as well as an output file for report conversion.\n")); /* RATS: ignore, the non-constant format string comes from a trusted source. */
      printf (_("Please use --help for more information.\n")); /* RATS: ignore, the non-constant format string comes from a trusted source. */
      exit (1);
    }

  // If filter should be applied during report conversion, load global severity
  // filter and enable filtering for the Current context.
  if (filter_report == TRUE)
    {
      gchar* filter_file = g_build_filename (prefs_get_nessushome(), ".openvas", "severity_overrides.xml", NULL);
      global_filter = severity_filter_from_xml (filter_file);
      g_free (filter_file);
      Context->is_severity_mapped = TRUE;
    }

  F_quiet_mode = TRUE;
  be = backend_import_report (in_report);
  if (be >= 0)
    {
      char * type;

      if (!output_type)
        {
          type = strrchr (out_report, '.');
          if (type != NULL)
            type++;
          else
            type = "nbe";
        }
      else
        {
          type = output_type;
        }

      if (drop_fp && strcmp (type, "html"))
        printf (_("Warning: Dropping False Positives (--convert-drop-fp or -D) is just supported in combination with conversion to html.\n"));

      if (drop_no_info && strcmp (type, "html"))
        printf (_("Warning: Dropping Ports and Services with 'No Information' (--convert-drop-no-info or -N) is just supported in combination with conversion to html.\n"));

      if (!strcmp(type, "tex") || !strcmp(type, "latex"))
        arglist_to_latex (backend_convert(be), out_report);
      else if (!strcmp(type, "txt") || !strcmp(type, "text"))
        arglist_to_text (backend_convert(be), out_report);
      else if (!strcmp(type, "html"))
        html_output_from_arg (backend_convert(be), out_report, drop_fp, drop_no_info);
#ifndef NO_GDCHART
      else if (!strcmp(type, "html_pie") || !strcmp(type, "html_graph"))
        arglist_to_html_graph (backend_convert(be), out_report);
#endif
      else if (!strcmp(type, "nbe"))
        {
          if (filter_report == TRUE)
            {
              fprintf (stderr, _("Cannot apply severity overrides when converting to nbe!\nConsider dropping the -F flag.\n"));
              exit (1);
            }
          backend_to_nbe (be, out_report);
        }
      else if (!strcmp(type, "xml"))
        backend_to_xml_ng (be, out_report);
      else
        {
          if (!strcmp (type, "pdf"))
            fprintf (stderr, _("Pdf conversion of reports is available in GUI mode only.\n"));
          else
            fprintf (stderr, _("Unsupported report type '%s'\n"), type);
          exit (1);
        }

      backend_close (be);
    }
  else /* Could not open backend from in_report */
    {
      fprintf (stderr, _("Could not import '%s' - is it a .nbe file?\n"),
              in_report);
    }
}

int
main (int argc, char * argv[])
{
  char * arg = NULL;
  gchar *myself = NULL;

  /* Setup I18N. */
  i18n_init();

  MainDialog = NULL;
#ifdef USE_GTK
  quiet_mode = FALSE;
  show_pixmaps = TRUE;
#endif

  /* Options for command line parsing */
  static gboolean make_config_file = FALSE;
  static gboolean display_version = FALSE;
  static gboolean verbose = FALSE;
#ifdef USE_GTK  
  static gboolean no_pixmap = FALSE;
#endif
  static gchar *batch_mode = NULL;
  static gboolean list_plugins = FALSE;
  static gboolean list_prefs = FALSE;
  static gchar *in_report = NULL;
  static gchar *out_report = NULL;
  static gchar *config_file = NULL;
  static gchar *output_type = NULL;
  static gboolean sqlize_output = FALSE;
  static gboolean list_sessions = FALSE;
  static gboolean restore_session = FALSE;
  static gboolean convert_filtered = FALSE;
  static gboolean convert_drop_fp  = FALSE;
  static gboolean convert_drop_ni  = FALSE;
  static gchar **remaining_options = NULL;
  guint remaining_options_count = 0;
  GError *error = NULL;
  GOptionContext *option_context;
  gchar *session_id = NULL;
  static GOptionEntry entries[] =
  {
    { "version", 'v', 0, G_OPTION_ARG_NONE, &display_version, N_("Display version information"), NULL },
#ifdef USE_GTK
    { "no-pixmap", 'n', 0, G_OPTION_ARG_NONE, &no_pixmap, N_("No pixmaps"), NULL },
#endif
    { "batch-mode", 'q', 0, G_OPTION_ARG_STRING, &batch_mode, N_("Batch-mode scan"), N_("<host> <port> <user> <pass> <targets-file> <result-file>") },
    { "make-config-file", 'm', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &make_config_file, "", NULL },
    { "config-file", 'c', 0, G_OPTION_ARG_FILENAME, &config_file, N_("Configuration file"), N_("<.rcfile>") },
#ifndef NO_GDCHART
    { "output-type", 'T', 0, G_OPTION_ARG_STRING, &output_type, N_("Output format"), N_("[nbe|html|html_graph|text|xml|tex]") },
#else
    { "output-type", 'T', 0, G_OPTION_ARG_STRING, &output_type, N_("Output format"), N_("[nbe|html|text|xml|tex]") },
#endif
    { "verbose", 'V', 0, G_OPTION_ARG_NONE, &verbose, N_("Display status messages in batch mode"), NULL },
    { "list-plugins", 'p', 0, G_OPTION_ARG_NONE, &list_plugins, N_("Obtain list of plugins installed on the server"), NULL },
    { "list-prefs", 'P', 0, G_OPTION_ARG_NONE, &list_prefs, N_("Obtain list of server and plugin preferences"), NULL },
    { "in-report", 'i', 0, G_OPTION_ARG_FILENAME, &in_report, N_("Input file (report conversion)"), N_("<in.nbe>") },
    { "out-report", 'o', 0, G_OPTION_ARG_FILENAME, &out_report, N_("Output file (report conversion)"), N_("<out.[html|xml|nbe]>") },
    { "convert-filtered", 'F', 0, G_OPTION_ARG_NONE, &convert_filtered, N_("Apply global severity filter during report conversion"), NULL },
    { "convert-drop-fp", 'D', 0, G_OPTION_ARG_NONE, &convert_drop_fp, N_("Drop false positives during report conversion to html"), NULL },
    { "convert-drop-no-info", 'N', 0, G_OPTION_ARG_NONE, &convert_drop_ni, N_("Drop issues with \"No Information\" during report conversion to html"), NULL },
    { "dont-check-ssl-cert", 'x', 0, G_OPTION_ARG_NONE, &dont_check_ssl_cert, N_("Override SSL \"paranoia\" question preventing OpenVAS-Client from checking certificates"), NULL },
    { "sqlize-output", 'S', 0, G_OPTION_ARG_NONE, &sqlize_output, N_("Issue SQL output for -p and -P (experimental)"), NULL },
    { "list-sessions", 's', 0, G_OPTION_ARG_NONE, &list_sessions, N_("List sessions"), N_("<host> <port> <user> <pass>") },
    { "restore-session", 'R', 0, G_OPTION_ARG_NONE, &restore_session, N_("Restore session"), N_("<sessionid> <host> <port> <user> <pass> <result-file>") },
    { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &remaining_options, NULL, NULL },
    { NULL }
  };

  option_context = g_option_context_new(_("- client for the OpenVAS security scanner"));
  g_option_context_add_main_entries(option_context, entries, NULL);
#ifdef USE_GTK
  g_option_context_add_group(option_context, gtk_get_option_group (FALSE));
#endif

  if (!g_option_context_parse(option_context, &argc, &argv, &error))
  {
    g_print ("%s\n\n", error->message);
    exit (0);
  }

  myself = g_get_prgname();

  g_option_context_free(option_context);

  if (remaining_options != NULL)
    while (remaining_options[remaining_options_count] != NULL)
      remaining_options_count++;

  if (config_file != NULL)
    Alt_rcfile = g_strdup(config_file);

#ifdef USE_GTK
  if (no_pixmap)
      show_pixmaps = FALSE;
#endif

  if (display_version)
  {
    printf(_("OpenVAS-Client (%s) %s for %s\n\n"), PROGNAME, /* RATS: ignore, the non-constant format string comes from a trusted source. */
           OPENVASCLIENT_VERSION, NESS_OS_NAME);
    printf(_("NessusClient origin: (C) 1998 - 2003 Renaud Deraison <deraison@nessus.org>\n")); /* RATS: ignore, the non-constant format string comes from a trusted source. */
    printf(_("New code since OpenVAS-Client: (C) 2007, 2008, 2009 Intevation GmbH\n")); /* RATS: ignore, the non-constant format string comes from a trusted source. */
    printf ("\n");
    exit(0);
  }

  if (batch_mode != NULL)
    quiet_mode = TRUE;

  if (list_prefs || list_plugins)
  {
    ListOnly = 1;
    quiet_mode = TRUE;
  }

#ifdef ENABLE_SAVE_TESTS
  if (restore_session)
  {
    if(remaining_options != NULL)
      // TODO: Is remaining_options[0] enough? restore_session seems to completely ignore session_id anyway.
      session_id = g_strdup(remaining_options[0]);
    else
    {
      printf(_("The session ID is required to restore a session.\n")); /* RATS: ignore, the non-constant format string comes from a trusted source. */
      printf(_("Please use %s --help for more information.\n"), myself); /* RATS: ignore, the non-constant format string comes from a trusted source. */
      exit(1);
    }
  }
#endif

  F_quiet_mode   = quiet_mode;
  F_show_pixmaps = show_pixmaps;

  /* Initialize global context */
  context_init (&Global, NULL);
  Context = Global;

  // Just convert a report from one format into another
  if((in_report != NULL) || (out_report != NULL))
    {
      // If in_report is part of a "report" directory, load the context
      // To do so, we have to trick a bit (usually all contexts are read in
      // recursively).
      struct context* context;
      context_init (&context, Global);
      context->type = CONTEXT_REPORT;
      context_collect_recurse (context, g_path_get_dirname (in_report));
      context_load_plugin_cache (context);
      Context = context;

      convert_report (in_report, out_report, output_type, convert_filtered,
                      convert_drop_fp, convert_drop_ni);
      exit (0);
    }

  // Alarm if --convert-filtered flag is given but no --in-report or --out-report
  if (convert_filtered == TRUE)
    printf (_("Ignoring -F (--convert-filtered) option.\nThis option can only be used in combination with -i (--in-report) and -o (--out-report)."));

  // Alarm if --convert-drop-fp flag is given but no --in-report or --out-report
  if (convert_drop_fp == TRUE)
    printf (_("Ignoring -D (--convert-drop-fp) option.\nThis option can only be used in combination with -i (--in-report) and -o (--out-report) and html as target format."));

  // Alarm if --convert-drop-no-info flag is given but no --in-report or --out-report
  if (convert_drop_ni == TRUE)
    printf (_("Ignoring -N (--convert-drop-no-info) option.\nThis option can only be used in combination with -i (--in-report) and -o (--out-report) and html as target format."));

  if(make_config_file && !quiet_mode)
  {
    printf(_("The option --make_config_file can only be used in batch mode.\n")); /* RATS: ignore, the non-constant format string comes from a trusted source. */
    printf(_("Please use %s --help for more information.\n"), myself); /* RATS: ignore, the non-constant format string comes from a trusted source. */
    exit(1);
  }

#define BATCH_USAGE "batch mode and <host> <port> <user> <pass> "
#define BATCH_ARGC 3

#ifdef ENABLE_SAVE_TESTS
  if(list_sessions && (remaining_options_count < BATCH_ARGC) && !quiet_mode)
  {
    fprintf(stderr, _("list-sessions requires %s\n"), BATCH_USAGE);
    exit(1);
  }

  if(restore_session && (remaining_options_count < BATCH_ARGC) && !quiet_mode)
  {
    fprintf(stderr, _("restore-session requires -q %s <result>\n"), BATCH_USAGE);
    exit(1);
  }

  if(restore_session && list_sessions)
  {
    fprintf(stderr, _("--restore-session and --list-sessions are mutually exclusive\n"));
    exit(1);
  }
#endif


  if(remaining_options_count >= BATCH_ARGC || quiet_mode)
  {
    signal(SIGINT, sighand_exit);
    signal(SIGQUIT, sighand_exit);
    signal(SIGKILL, sighand_exit);
    signal(SIGTERM, sighand_exit);

    F_quiet_mode = TRUE;
  }
#ifdef USE_GTK
  else
  {
    gtk_init(&argc, &argv);
  }
#endif

  /* System environment set up */
  if(!make_config_file)
  {
    if (preferences_init(Global))
      exit (2);
  }
  else
    Global->prefs = emalloc(sizeof(struct arglist));

#ifdef USE_GTK
  if(!quiet_mode)
    context_collect(Global);
#endif /* USE_GTK */

  if(verbose && !quiet_mode)
  {
    fprintf(stderr, _("Verbose mode can only be used in batch mode\n"));
    exit(1);
  }

  /* Do we run in batchmode ? */
  if (remaining_options_count >= BATCH_ARGC || quiet_mode)
  {
    struct cli_args * cli;

    F_quiet_mode = TRUE;

    cli = cli_args_new();
    cli_args_verbose(cli, verbose);

    /* With, or without ENABLE_CRYPTO_LAYER */
#define NUM_ARGS 5

#ifndef ENABLE_SAVE_TESTS
    if (remaining_options_count != NUM_ARGS)
    {
      if(!((remaining_options_count == NUM_ARGS - 2) && make_config_file))
      {
        printf(_("Batch mode requires login information.\n")); /* RATS: ignore, the non-constant format string comes from a trusted source. */
        printf(_("Please use %s --help for more information.\n"), myself); /* RATS: ignore, the non-constant format string comes from a trusted source. */
        exit(0);
      }
    }
#else
    if(list_sessions)
    {
      if (remaining_options_count != NUM_ARGS - 2)
      {
        fprintf(stderr, "%s" BATCH_USAGE "\n",
                _("list-sessions only requires "));
        exit(1);
      }
    }
    else if(restore_session)
    {
      if (remaining_options_count != NUM_ARGS - 1)
      {
        fprintf(stderr, "%s" BATCH_USAGE " <result-file>\n",
                _("restore-session only requires "));
        exit(1);
      }
    }
    else if(list_prefs || list_plugins)
    {
      if (batch_mode == NULL || remaining_options_count < NUM_ARGS - 2)
      {
        fprintf(stderr, "%s" BATCH_USAGE "\n",
                _("list-prefs and list-plugins require "));
        exit(1);
      }
    }
    else if (remaining_options_count != NUM_ARGS)
    {
      printf(_("Batch mode requires login information.\n")); /* RATS: ignore, the non-constant format string comes from a trusted source. */
      printf(_("Please use %s --help for more information.\n"), myself); /* RATS: ignore, the non-constant format string comes from a trusted source. */
      exit(0);
    }

#endif /* !ENABLE_SAVE_TESTS */

    /* next arguments: SERVER PORT */
    cli_args_server(cli, batch_mode);
    cli_args_port(cli, atoi(remaining_options[0]));

    /* next argument: LOGIN */
    arg = remaining_options[1];
    cli_args_login(cli, arg);
    bzero(arg, strlen(arg));

    /* next argument: PASSWORD */
    arg = remaining_options[2];
    cli_args_password(cli, arg);
    bzero(arg, strlen(arg));

    if(list_prefs)
    {
      if(cli_connect_to_nessusd(cli) < 0)
      {
        fprintf(stderr, _("Could not connect to openvasd\n"));
        exit(1);
      }
      if(sqlize_output)
        cli_sql_dump_prefs(cli);
      else
        cli_dump_prefs(cli);
      cli_close_connection(cli);
      exit(0);
    }

    if(list_plugins)
    {
      if(cli_connect_to_nessusd(cli) < 0)
      {
        fprintf(stderr, _("Could not connect to openvasd\n"));
        exit(1);
      }
      if(sqlize_output)
        cli_sql_dump_plugins(cli);
      else
        cli_dump_plugins(cli);

      cli_close_connection(cli);
      exit(0);
    }

    if(!make_config_file)
    {
      if(restore_session)
      {
        cli_args_results(cli, remaining_options[3]);
      }
      else
      {
        if(!list_sessions)
        {
          char * t = remaining_options[3];
          if(t)
            cli_args_target(cli, t);
          else
          {
            fprintf(stderr, _("Missing parameter\n"));
          }
          t = remaining_options[4];
          if(t)
            cli_args_results(cli,  t);
          else
          {
            fprintf(stderr, _("Missing parameter\n"));
          }
        }
      }
    }

    cli_args_output(cli, output_type);

    /* login now */
    if((cli_connect_to_nessusd(cli)) < 0)
      nessus_exit(1);

#ifdef ENABLE_SAVE_TESTS
    if(list_sessions)
    {
      cli_list_sessions(cli);
      close_stream_connection(Context->socket);
      Context->socket = -1;
      nessus_exit(0);
    }
    else if(restore_session)
    {
      cli_restore_session(cli, session_id);
    }
    else
#endif

    if(make_config_file)
    {
      if(!preferences_generate_new_file(Global, NULL))
        printf(_("A new openvasrc file has been saved\n")); /* RATS: ignore, the non-constant format string comes from a trusted source. */
    }
    else
    {
      cli_test_network(cli);
      cli_report(cli);
    }
    /* Free session id. It's no longer needed */
    g_free(session_id);
    /* End, exit */
    nessus_exit(0);
  }
  /* Free session id. It's no longer needed */
  g_free(session_id);

  /*
   * Set up the main window
   */

#if 0
  paranoia_level = prefs_get_int(context, "paranoia_level");
  if (paranoia_level == 0)
   
    {
        paranoia_level = sslui_ask_paranoia_level();
	if(paranoia_level > 0)
 	 prefs_set_int(context, "paranoia_level", paranoia_level);
    }
#endif
#ifdef USE_GTK
  prefs_dialog_setup(Global);
  prefs_context_update(Global);

  // Initialize global severity_filter
  gchar * filter_file = g_build_filename (prefs_get_nessushome(), ".openvas", "severity_overrides.xml", NULL);
  global_filter = severity_filter_from_xml (filter_file);
  if (global_filter == NULL)
    {
      gchar * filter_file_dir = g_build_filename (prefs_get_nessushome(), ".openvas", NULL); 
      if (file_utils_ensure_dir (filter_file_dir) == TRUE)
        global_filter = severity_filter_new (_("Global Severity Overrides"), filter_file);
      g_free (filter_file_dir);
    }
  g_free (filter_file);

  signal(SIGINT, sighand_exit);
  signal(SIGQUIT, sighand_exit);
  signal(SIGKILL, sighand_exit);
  signal(SIGTERM, sighand_exit);

  gtk_main();

  /* FIXME: Server preferences are not saved here */
  prefs_dialog_apply (Context, MainDialog);

  context_save_recurse (Global);

  nessus_exit (0);
  return 0; /* make the compiler happy */

#else
  /* RATS: ignore, the non-constant format string comes from a trusted
   * source.
   */
  printf(_("\n\
  This version of OpenVAS-Client was compiled without gui support\n\
  and can only be run in batch mode.\n\
  Help on running OpenVAS-Client in batch mode is available using\n\
  the --help option and in the OpenVAS documentation.\n"));
  exit (1);
#endif
} /* main */

#ifdef NESSUSNT
int WINAPI WinMain (HINSTANCE hThisInst, HINSTANCE hPrevInst,
                    LPSTR lpszArgs, int nWinMode)
{
/*
 * Initialize WinSock and jump into the regular 'main'
 */
  WSADATA winSockData;
  WSAStartup(0x0101, &winSockData);
  main(__argc, __argv);
  WSACleanup();
  return 0;
}
#endif /* NESSUSNT */
