/* 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.
 *
 * cli.c - Command Line Interface manager
 * 
 * modified by Axel Nennker <axel@nennker.de> 20020418 
 *	do not need gtk here
 *	removed gcc -Wall complaints, NULL pointer checks
 *
 */
 
#include <includes.h>
#include "globals.h"

#include "nessus.h"
#include "context.h"
#include "preferences.h"
#include "parser.h"
#include "parseutils.h"
#include "nessus_plugin.h"
#include "cli.h"

#undef USE_GTK
#include "read_target_file.h"
#include "nbe_output.h"
#include "text_output.h"
#include "latex_output.h"
#include "xml_output_ng.h"
#include "html_output.h"
#include "html_graph_output.h"
#include "attack.h"
#include "auth.h"
#include "comm.h"
#include "backend.h"


static struct cli_args * g_cli = NULL;
/*---------------------------------------------------
   Private functions
-----------------------------------------------------*/

static int 
is_server_present(soc)
	int soc;
{
	fd_set  rd;
	struct timeval tv = {2,0};
	int fd = nessus_get_socket_from_connection(soc);

	FD_ZERO(&rd);
	FD_SET(fd, &rd);
	if(select(fd+1, &rd, NULL, NULL, &tv) > 0)
	{
		int len = -1;
		ioctl(fd, FIONREAD, &len);
		if(!len){
			fprintf(stderr, "Communication closed by server\n");
			return 0;
			}
	}
	return 1;
}

void 
cli_sigterm(s)
 int s;
{
 cli_report(g_cli);
 exit(5);
}

/**
 * @brief Monitor the test - read data from the client, and process it
 */
static void
cli_test_monitor(cli)
 struct cli_args * cli;
{
 int type, finished = 0;
 static char buf [16384], msg [16384];
 int backend = backend_init(NULL);
   
 signal(SIGTERM, cli_sigterm);
 g_cli = cli;  
 cli->backend = backend; 
      
  while(!finished)
   {
       /* I don't think buf[0] == 0 is a case that will happen, but just 
        * to be safe, as it was the previous semantics
        */
    if(network_gets(Context->socket, buf, sizeof(buf) - 1) < 0 || buf[0] == '\0')
    { 
       if(!is_server_present(Context->socket))
       {
       fprintf(stderr, "OpenVAS-Client: The server abruptly shut the communication down - the test may be incomplete\n");
       finished = 1;
       }
       continue;
   }
   buf[strlen(buf)-1]=0;
   if((type = parse_server_message(Context, buf, backend, msg))==MSG_BYE)
	   finished = 1;
   if(cli->verbose)
   {
	   switch(type)
	   {
		   case MSG_STAT2:
			   {
			   char * hostname;
			   char * action;
			   char * current;
			   int max;
			   char * plug = NULL;
			  
			   parse_nessusd_short_status(&(buf[2]), &hostname, &action, &current, &max);
			   if(!strcmp(action, "portscan"))plug="";
			   printf("%s|%s|%s|%d\n", action,hostname,current,max); 
			   efree(&hostname);
			   efree(&action);	   
			   efree(&current);
			   }
			   break;
	           case MSG_STAT:
			   {
			   char * hostname;
			   char * action;
			   char* current;
			   int max;

			   parse_nessusd_status(buf, &hostname, &action, &current, &max);
			   printf("%s|%s|%s|%d|foo\n", action,hostname,current,max); 
			   efree(&hostname);
			   efree(&action);	   
			   efree(&current);
			   }
			   break;
		   case MSG_FINISHED:
			   printf("finished|%s||||\n", msg);
			   break;
	   }		   
		 			   
   }
   fflush(stdout);
   msg[0] = '\0';
  }
}


/*---------------------------------------------------
   CLI arguments management
 ----------------------------------------------------*/
struct cli_args * 
cli_args_new()
{
 return emalloc(sizeof(struct cli_args));
}

void
cli_args_server(args, server)
 struct cli_args * args;
 char * server;
{
 if(args->server)free(args->server);
 args->server = strdup(server);
}

void
cli_args_port(args, port)
 struct cli_args * args;
 int port;
{
 args->port = port;
}

void
cli_args_login(args, login)
struct cli_args * args;
 char * login;
{
 if(args->login)free(args->login);
 args->login = strdup(login);
}


void
cli_args_password(args, pwd)
 struct cli_args * args;
 char * pwd;
{
 if(args->password)free(args->password);
 args->password = strdup(pwd);
}


void 
cli_args_target(args, target)
 struct cli_args * args;
 char * target;
{
 if(args->target)free(args->target);
 args->target = strdup(target);
}

void 
cli_args_results(args, results)
 struct cli_args * args;
 char * results;
{
 char* ftype;
 
 if(args->results)free(args->results);
 args->results = strdup(results);
 if(args->extension)free(args->extension);
 /* choose output file type based on fname */
 ftype = strrchr(args->results, '.');
 if(!ftype)
 {
   if(args->results[strlen(args->results)-1]=='/')
    {
      args->results[strlen(args->results)-1]=0;
      ftype = "html_pie";
    }
    else
    {
      ftype = "nbe";
    }
  }
  else 
   ftype++;
    
 args->extension = strdup(ftype);
}

void 
cli_args_verbose(args, verbose)
 struct cli_args * args;
 int verbose;
{ 
 args->verbose = verbose;
}

/**
 * Sets the output function pointer in an cli_args.
 * The output function is chosen according to the type, and currently one of
 *  - backend_to_nbe (type: "nbe", NULL)
 *  - arglist_to_html_graph ("html_graph", "html_pie")
 *  - arglist_to_html ("html")
 *  - arglist_to_latex ("tex", "latex")
 *  - arglist_to_text ("txt", "txt")
 *  - backend_to_xml_ng ("xml")
 *
 * @param args The cli_args of interest.
 * @param type Sets the type of output created when calling the output function
 *             ("xml", "nbe" etc).
 * @see backend_to_nbe
 * @see backend_to_xml_ng
 * @see arglist_to_latex
 * @see arglist_to_html
 * @see arglist_to_html_graph
 * @see arglist_to_text 
 */
void
cli_args_output(args, type)
 struct cli_args * args;
 char * type;
{
 char * ftype = args->extension;
 
 if(type)ftype = type;

 if(!ftype)
  {
    args->output = (output_func_t)backend_to_nbe;
    args->backend_output_func = 1;
    return;
  }

#ifndef NO_GDCHART
 if(!strncmp(ftype, "html_pie", 8)||
     !strncmp(ftype, "html_graph", 10)) {
    args->output = arglist_to_html_graph;
    }
     else 
#endif /* NO_GDCHART */
     if (!strncmp(ftype, "html", 4)) {
	args->output = arglist_to_html;
     } else if (!strcmp(ftype, "latex") ||
    	        !strcmp(ftype, "tex")) {
	args->output = arglist_to_latex;
     } else if(!strcmp(ftype, "txt")||
    	      !strcmp(ftype, "text"))  {
    	args->output = arglist_to_text;
     } 
     else if(!strcmp(ftype, "nbe"))
     {	    
    	args->output = (output_func_t)backend_to_nbe;
	args->backend_output_func = 1;
     }
     else if(!strcmp(ftype, "xml"))
     {	    
    	args->output = (output_func_t)backend_to_xml_ng;
	args->backend_output_func = 1;
     }
     else {
	     fprintf(stderr, "'%s' is not a valid report type\n", ftype);
	     exit(1);
     }
}
/*---------------------------------------------------------
 * Auditing now
 *--------------------------------------------------------*/
 
int cli_connect_to_nessusd(cli)
 struct cli_args * cli;
{
 /*ENABLE_CRYPTO_LAYER*/
 char * err;
 prefs_set_string(Context, "nessusd_host", cli->server);
 prefs_set_int(Context, "nessusd_port", cli->port);
 prefs_set_string(Context, "nessusd_user", cli->login);
 Context->passwd = cli->password;
 err = connect_to_nessusd(Context);

 if(err)
  {
  fprintf(stderr, "OpenVAS-Client : %s\n", err);
  return -1;
 }
 
 return 0;
}

/**
 *
 * @return -1 If targetfile could not be converted to a list or attack failed,
 *         0 otherwise.
 */
int
cli_test_network (struct cli_args * cli)
{
    /* If we fail to turn the target file into a list then
     * We should _NOT_ try to attack anything */
  char * target_list = target_file_to_list(cli->target);
  if (target_list == NULL) {
      /* report the error */
      fprintf(stderr, "OpenVAS-Client : error turning targetfile (%s) to list\n", cli->target);
      return -1;
  }

  if(attack_host(target_list, Context))
  {
    cli_test_monitor(cli);
    return 0;
  }
  else
    return -1;
}

/**
 * @brief Exports a report with the output function set.
 */
void
cli_report (struct cli_args * cli)
{
 if(!cli->backend_output_func)
    cli->output(backend_convert(cli->backend), cli->results);
 else
    cli->output((struct arglist*)GSIZE_TO_POINTER(cli->backend), cli->results);
}

/* Only difference to addslashes from addslashes.c is that single "'"s are escaped, too.
  Should verify if that is of any use and merge. */
static char* 
sql_addslashes (char *in)
{
 char * ret;
 char * out;

 if ( in == NULL ) return NULL;

 out = malloc(strlen(in) * 2 + 1);
 bzero(out, strlen(in) * 2 + 1);
 ret = out;
 while(in[0])
 {
  if(in[0] == '\\')
  {
   out[0] = '\\'; out++;
   out[0] = '\\'; out++;
  }

  else if(in[0] == '\n')
  {
   out[0] = '\\'; out++;
   out[0] = 'n'; out++;
  }
  else if(in[0] == '\r')
  {
    out[0] = '\\'; out++;
    out[0] = 'r';  out++;
  }
  else if(in[0] == '\'')
  {
    out[0] = '\\'; out++;
    out[0] = '\'';  out++;
  }
  else {
	  out[0] = in[0];
	  out++;
  }
  in++;
 }
 return realloc(ret, strlen(ret) + 1);
}

static void 
_cli_sql_dump_plugins(p)
 struct nessus_plugin * p;
{
 while (p != NULL )
 {
 char * m, * n;

 printf("INSERT INTO plugins VALUES ('%s', ", p->oid);


 m = sql_addslashes(p->name);
 printf("'%s', ", m);
 efree(&m);
 
 m = sql_addslashes(p->family); 
 printf("'%s', ", m);
 efree(&m);
 
 
 m = sql_addslashes(p->category); 
 printf("'%s', ", m);
 efree(&m);
 
 m = sql_addslashes(p->copyright);
 printf("'%s', ", m);
 efree(&m);
 
 m = sql_addslashes(p->summary);
 printf("'%s', ", m);
 efree(&m);
 
 n = nessus_plugin_get_description(p);
 m = sql_addslashes(n);
 printf("'%s',", m);
 efree(&m);
 
 m = sql_addslashes(p->version);
 printf("'%s',", m);
 efree(&m);
 
 
 if( p->cve != NULL )
 {
  m = sql_addslashes(p->cve );
  printf("'%s',", m);
  efree(&m);
 }
 else printf("'',");
 
 
 if(p->bid != NULL)
  {
  m = sql_addslashes(p->bid );
  printf("'%s',", m);
  efree(&m);
  }
 else printf("'',");
 
 if(p->xrefs != NULL)
  {
  m = sql_addslashes(p->xrefs);
  printf("'%s');\n", m);
  efree(&m);
  }
 else printf("'');\n");
 p = p->next;
 }
}

static void
_cli_dump_plugins(p)
 struct nessus_plugin * p;
{
 while( p != NULL )
 {
  char * var = p->oid;
  char * n;

  printf("%s|", var);
  var = addslashes(p->family);
  printf("%s|", var);
  efree(&var);
  var = addslashes(p->name);
  printf("%s|", var);
  efree(&var);
  var = addslashes(p->category);
  printf("%s|", var);
  efree(&var);
  var = addslashes(p->copyright);
  printf("%s|", var);
  efree(&var);  
 
  
  var = addslashes(p->summary);
  printf("%s|", var);
  efree(&var);
  
  
  var = addslashes(p->version);
  printf("%s|", var);
  efree(&var);
  
  var = addslashes(p->cve);
  printf("%s|", var);
  efree(&var);
  
  var = addslashes(p->bid);
  printf("%s|", var);
  efree(&var);
  
  var = addslashes(p->xrefs);
  printf("%s|", var);
  efree(&var);
     
  n = nessus_plugin_get_description(p);
  var = addslashes(n);
  printf("%s\n", var);
  efree(&var);
  p = p->next;
 }
}


void
cli_sql_dump_plugins(cli)
 struct cli_args * cli;
{
 printf("DROP TABLE IF EXISTS plugins;\n");
 printf("CREATE TABLE plugins (\n");
 printf(" oid varchar(50) NOT NULL,\n");
 printf(" name varchar(255),\n");
 printf(" family varchar(255),\n");
 printf(" category varchar(255),\n");
 printf(" copyright varchar(255),\n");
 printf(" summary varchar(255),\n");
 printf(" description blob,\n");
 printf(" version varchar(255),\n");
 printf(" cve_id varchar(255),\n");
 printf(" bugtraq_id varchar(255),\n");
 printf(" xref blob,\n");
 printf(" primary key (oid));\n");
 _cli_sql_dump_plugins(Context->plugins);
 _cli_sql_dump_plugins(Context->scanners);
}


void
cli_dump_plugins(cli)
 struct cli_args * cli;
{
 _cli_dump_plugins(Context->plugins);
 _cli_dump_plugins(Context->scanners);
}


static void
_cli_dump_pprefs()
{
 struct arglist * p = arg_get_value(Context->prefs, "PLUGINS_PREFS");
 if(!p)
  return;
  
 while(p->next)
 {
  switch(p->type)
  {
	  case ARG_STRING:
 		 printf("%s = %s\n", p->name, (char*)p->value);
		 break;
	  case ARG_INT:
		 printf("%s = %s\n", p->name, p->value ? "yes":"no");
		 break;
	  default:
		 break;
  }
  p = p->next;
 }
}


void
cli_sql_dump_prefs(cli)
 struct cli_args * cli;
{
 cli_dump_prefs(cli);
}

void
cli_dump_prefs(cli)
 struct cli_args * cli;
{
 struct arglist * p = arg_get_value(Context->prefs, "SERVER_PREFS");
 if(!p)
  return;
  
 while(p->next)
 { 
  switch(p->type)
  {
   case ARG_INT :
    printf("%s = %d\n", p->name, (int)GPOINTER_TO_SIZE(p->value));
    break;
   case ARG_STRING:
   printf("%s = %s\n", p->name, (char*)p->value);
   break;
  }
 p = p->next;	
 }
 

 if(Context->plugins)_cli_dump_pprefs(Context->plugins);
 if(Context->scanners)_cli_dump_pprefs(Context->scanners);
 
 return;
}

int 
cli_close_connection(cli)
 struct cli_args * cli;
{
 return close_stream_connection(Context->socket);
}

#ifdef ENABLE_SAVE_TESTS
void
cli_restore_session(cli, session)
 struct cli_args * cli;
 char * session;
{   
  restore_attack(session, Context);
  cli_test_monitor(cli);
}

void
cli_list_sessions(struct cli_args* cli)
{
 hargwalk * hw;
 if(!comm_server_restores_sessions(Context))
  printf("** The remote OpenVAS-Server does not support session-saving\n");
 else
  {
   char * key;
   hw = harg_walk_init(Context->sessions);
   printf("Remote sessions :\n");
   printf("-----------------\n\n");
   printf("Session ID      | Targets\n");
   printf("==========================\n");
   while((key = (char*)harg_walk_next(hw)))
   {
    printf("%s | %s\n", key, harg_get_string(Context->sessions, key));
   }
  }
}
#endif
