/* Nessus Attack Scripting Language 
 *
 * Copyright (C) 2002 - 2004 Michel Arboi and 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.
 *
 */
 /*
  * This file contains all the "unsafe" functions found in NASL
  */
#include <includes.h>

#include "nasl_tree.h"
#include "nasl_global_ctxt.h"
#include "nasl_func.h"
#include "nasl_var.h"
#include "nasl_lex_ctxt.h"
#include "exec.h"  

#include "strutils.h"
#include "nasl_cmd_exec.h"
#include "nasl_debug.h"

static  pid_t pid = 0;
static  void (*old_sig_t)() = NULL, (*old_sig_i)() = NULL, (*old_sig_c) = NULL;

static void
sig_h()
{
  if (pid > 0)
    (void) kill(pid, SIGKILL);
}

static void	sig_c()
{
  if (pid > 0)
    (void) waitpid(pid, NULL, WNOHANG);
}

tree_cell*
nasl_pread(lex_ctxt * lexic)
{
  tree_cell	*retc = NULL, *a;
  anon_nasl_var	*v;
  nasl_array	*av;
  int		i, j, n, sz, sz2, cd, nice;
  char		** args = NULL, *cmd, *str, *str2, buf[8192];
  FILE		*fp;
#ifdef MAXPATHLEN
  char		cwd[MAXPATHLEN], newdir[MAXPATHLEN];
#else
  char		cwd[1024], newdir[1024];
#endif

 if ( check_authenticated(lexic) < 0 ) return NULL;
  
  if (pid != 0)
    {
      nasl_perror(lexic, "nasl_pread is not reentrant!\n");
      return NULL;
    }

  a = get_variable_by_name(lexic, "argv");
  cmd = get_str_local_var_by_name(lexic, "cmd");
  if (cmd == NULL || a == NULL || (v = a->x.ref_val) == NULL)
    {
      nasl_perror(lexic, "pread() usage: cmd:..., argv:...\n");
      return NULL;
    }

  nice = get_int_local_var_by_name(lexic, "nice", 0);
  
  if (v->var_type == VAR2_ARRAY)
    av = &v->v.v_arr;
  else
    {
      nasl_perror(lexic, "pread: argv element must be an array (0x%x)\n",
		  v->var_type);
      return NULL;
    }

  cd = get_int_local_var_by_name(lexic, "cd", 0);

  cwd[0] = '\0';
  if (cd)
    {
      char	*p;

      if (cmd[0] == '/')
	{
	  strncpy(newdir, cmd, sizeof(newdir)-1);
	  p = strrchr(newdir, '/');
	  if (p != newdir) *p = '\0';
	}
      else
	{
	  p = find_in_path(cmd, 0);
	  if (p != NULL)
	    strncpy(newdir, p, sizeof(newdir)-1);
	  else
	    {
	      nasl_perror(lexic, "pread: '%s' not found in $PATH\n", cmd);
	      return NULL;
	    }
	  
	}
      newdir[sizeof(newdir)-1] = '\0';

      if (getcwd(cwd, sizeof(cwd)) == NULL)
	{
	  nasl_perror(lexic, "pread(): getcwd: %s\n", strerror(errno));
	  *cwd = '\0';
	}

      if (chdir(newdir) < 0)
	{
	  nasl_perror(lexic, "pread: could not chdir to %s\n", newdir);
	  return NULL;
	}
      if (cmd[0] != '/' && strlen(newdir) + strlen(cmd) + 1 < sizeof(newdir))
	{
	  strcat(newdir, "/");
	  strcat(newdir, cmd);
	  cmd = newdir;
	}
    }

  if (av->hash_elt != NULL)
    nasl_perror(lexic, "pread: named elements in 'cmd' are ignored!\n");
  n = av->max_idx;
  args = emalloc(sizeof(char**) * (n+2)); /* Last arg is NULL */
  for (j= 0, i = 0; i < n; i ++)
    {
      str = (char*)var2str(av->num_elt[i]);
      if (str != NULL)
	args[j++] = estrdup(str);
    }
  args[j++] = NULL;

  old_sig_t = signal(SIGTERM, sig_h);
  old_sig_i = signal(SIGINT, sig_h);
  old_sig_c = signal(SIGCHLD, sig_c);

  fp = nessus_popen4((const char*)cmd, args, &pid, nice);

  for (i = 0; i < n; i ++)
    efree(&args[i]);
  efree(&args);
  if (fp != NULL)
    {
      sz = 0; str = emalloc(1);

      while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) /* && kill(pid, 0) >= 0)*/
	{
	  sz2 = sz + n;
	  str2 = erealloc(str, sz2);
	  if (str2 == NULL)
	    {
	      nasl_perror(lexic, "nasl_pread: erealloc failed\n");
	      break;
	    }
	  str = str2;
	  memcpy(str + sz, buf, n);
	  sz = sz2;
	}
      if (ferror(fp))
	nasl_perror(lexic, "nasl_pread: fread(): %s\n", strerror(errno));

      (void) nessus_pclose(fp, pid);
      pid = 0;

      if (*cwd != '\0')
	if (chdir(cwd) < 0)
	  nasl_perror(lexic, "pread(): chdir(%s): %s\n", cwd, strerror(errno));

      retc = alloc_typed_cell(CONST_DATA);
      retc->x.str_val = str;
      retc->size = sz;
    }

  signal(SIGINT, old_sig_i);
  signal(SIGTERM, old_sig_t);
  signal(SIGCHLD, old_sig_c);

  return retc;
}

tree_cell*
nasl_find_in_path(lex_ctxt * lexic)
{
  tree_cell	*retc;
  char		*cmd;


 if ( check_authenticated(lexic) < 0 ) return NULL;


  cmd = get_str_var_by_num(lexic, 0);
  if (cmd == NULL)
    {
      nasl_perror(lexic, "find_in_path() usage: cmd\n");
      return NULL;
    }

  retc = alloc_typed_cell(CONST_INT);
  retc->x.i_val = (find_in_path(cmd, 0) != NULL);
  return retc;
}

/*
 * Not a command, but dangerous anyway
 */

tree_cell*
nasl_fread(lex_ctxt * lexic)
{
  tree_cell	*retc;
  char		*fname;
  struct stat	st;
  char		*buf, *p;
  int		alen, len, n;
  FILE		*fp;

 if ( check_authenticated(lexic) < 0 ) return NULL;
    
  fname = get_str_var_by_num(lexic, 0);
  if (fname == NULL)
    {
      nasl_perror(lexic, "fread: need one argument (file name)\n");
      return NULL;
    }
  
  if (stat(fname, &st) < 0)
    {
      nasl_perror(lexic, "fread: stat(%s): %s\n", fname, strerror(errno));
      return NULL;
    }

  fp = fopen(fname, "r");
  if (fp == NULL)
    {
      nasl_perror(lexic, "fread: %s: %s\n", fname, strerror(errno));
      return NULL;
    }

  alen = st.st_size + 1;
  buf = emalloc(alen);
  if (buf == NULL)
    {
      nasl_perror(lexic, "fread: cannot malloc %d bytes\n", alen);
      goto error;
    }

  len = 0;
  while ((n = fread(buf + len, 1, alen - len, fp)) > 0)
    {
      len += n;
      if (alen <= len)
	{
	  alen += 4096;
	  p = erealloc(buf, alen);
	  if (p == NULL)
	    {
	      nasl_perror(lexic, "fread: cannot realloc %d bytes\n", alen);
	      goto error;
	    }
	  buf = p;
	}
    }

  buf[len] ='\0';
  if (alen > len+1)
    {
      p = erealloc(buf, len+1);
      if (p != NULL) buf = p;
    }

  retc = alloc_typed_cell(CONST_DATA);
  retc->size = len;
  retc->x.str_val = buf;
  return retc;
  
 error:
  efree(&buf);
  fclose(fp);
  return NULL;
}

/*
 * Not a command, but dangerous anyway
 */

tree_cell*
nasl_unlink(lex_ctxt * lexic)
{
  char		*fname;

 if (check_authenticated(lexic) < 0) return NULL;
    
  fname = get_str_var_by_num(lexic, 0);
  if (fname == NULL)
    {
      nasl_perror(lexic, "unlink: need one argument (file name)\n");
      return NULL;
    }

  if (unlink(fname) < 0)
    {
      nasl_perror(lexic, "unlink(%s): %s\n", fname, strerror(errno));
      return NULL;
    }
  /* No need to return a value */
  return FAKE_CELL;
}



tree_cell*
nasl_get_tmp_dir(lex_ctxt * lexic)
{
  tree_cell	*retc;
  char		*p, path[MAXPATHLEN];
  int		warn = 0;

 if (check_authenticated(lexic) < 0) return NULL;
 retc = alloc_typed_cell(CONST_DATA);
 snprintf(path, sizeof(path), "%s/nessus/tmp/", NESSUS_STATE_DIR);
 if (access(path, R_OK|W_OK|X_OK) < 0)
   {
     warn ++;
     perror(path);
     p = getenv("TMPDIR");
     if (p != NULL)
       {
	 snprintf(path, sizeof(path), "%s/", p);
	 if (access(path, R_OK|W_OK|X_OK) < 0)
	   {
	     perror(path);
	     p = NULL;
	   }
       }
     if (p == NULL)
       snprintf(path, sizeof(path), "/tmp/");
     /* Here we could create a "safe" directory - is this really necessary? */
   }
 if (warn)
   fprintf(stderr, "get_tmp_dir: using \"%s\" temporary directory (may be unsafe)\n", path);
     
 retc->x.str_val = strdup(path);
 retc->size = strlen(retc->x.str_val);
 return retc;
}



