/*
 * The builtin System object.
 * Copyright (c) 1998-1999 New Generation Software (NGS) Oy
 *
 * Author: Markku Rossi <mtr@ngs.fi>
 */

/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA
 */

/*
 * $Source: /tmp/cvsroot-15-2-2007/clamav-devel/libclamav/js/b_system.c,v $
 * $Id: b_system.c,v 1.3 2006/11/18 22:44:04 njh Exp $
 */

/*
 * Static methods:
 *
 *   chdir (string) => boolean
 *   error ([ANY...]) => undefined
 *   exit (value)
 *   getcwd () => string
 *   getenv (string) => string / undefined
 *   popen (COMMAND, MODE) => file
 *   print ([ANY...]) => undefined
 *   sleep (int) => undefined
 *   strerror (errno) => string
 *   system (string) => integer
 *   usleep (int) => undefined
 *
 * Properties:			type		mutable
 *
 *   bits			integer
 *   canonicalHost		string
 *   canonicalHostCPU		string
 *   canonicalHostVendor	string
 *   canonicalHostOS		string
 *   errno			integer
 *   lineBreakSequence		string
 *   stderr			file
 *   stdin			file
 *   stdout			file
 */
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif

#ifdef	CL_EXPERIMENTAL

#include "jsint.h"

/*
 * Types and definitions.
 */

#define INSECURE()		\
  do {				\
    if (secure_mode)		\
      goto insecure_feature;	\
  } while (0)

struct system_ctx_st
{
  JSSymbol s_chdir;
  JSSymbol s_error;
  JSSymbol s_exit;
  JSSymbol s_getcwd;
  JSSymbol s_getenv;
  JSSymbol s_popen;
  JSSymbol s_print;
  JSSymbol s_sleep;
  JSSymbol s_strerror;
  JSSymbol s_system;
  JSSymbol s_usleep;

  JSSymbol s_bits;
  JSSymbol s_canonicalHost;
  JSSymbol s_canonicalHostCPU;
  JSSymbol s_canonicalHostVendor;
  JSSymbol s_canonicalHostOS;
  JSSymbol s_errno;
  JSSymbol s_lineBreakSequence;
  JSSymbol s_stderr;
  JSSymbol s_stdin;
  JSSymbol s_stdout;

  /* System file handles. */
  JSNode pstderr;
  JSNode pstdin;
  JSNode pstdout;
};

typedef struct system_ctx_st SystemCtx;


/*
 * Static functions.
 */

/* Method proc */
static int
method (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
	void *instance_context, JSSymbol method, JSNode *result_return,
	JSNode *args)
{
  SystemCtx *ctx = builtin_info->obj_context;
  int i;
  char *cp;
  int secure_mode = vm->security & JS_VM_SECURE_SYSTEM;

  /* The default result. */
  result_return->type = JS_UNDEFINED;

  if (method == ctx->s_chdir)
    {
      INSECURE ();

      if (args->u.vinteger != 1)
	goto argument_error;
      if (args[1].type != JS_STRING)
	goto argument_type_error;

      result_return->type= JS_BOOLEAN;

      cp = js_string_to_c_string (vm, &args[1]);
      result_return->u.vboolean = (chdir (cp) == 0);
      js_free (cp);
    }
  /* ********************************************************************** */
  else if (method == ctx->s_exit)
    {
      INSECURE ();

      if (args->u.vinteger != 1)
	goto argument_error;
      if (args[1].type != JS_INTEGER)
	goto argument_type_error;

      /* Exit. */
      exit (args[1].u.vinteger);
    }
  /* ********************************************************************** */
  else if (method == ctx->s_getcwd)
    {
      int size = 10 * 1024;

      INSECURE ();

      if (args->u.vinteger != 0)
	goto argument_error;

      cp = js_malloc (vm, size);
      if (!getcwd (cp, size))
	{
	  result_return->type = JS_BOOLEAN;
	  result_return->u.vboolean = 0;
	  js_free (cp);
	}
      else
	{
	  js_vm_make_string (vm, result_return, cp, strlen (cp));
	  js_free (cp);
	}
    }
  /* ********************************************************************** */
  else if (method == ctx->s_getenv)
    {
      char *val;

      if (args->u.vinteger != 1)
	goto argument_error;
      if (args[1].type != JS_STRING)
	goto argument_type_error;

      cp = js_string_to_c_string (vm, &args[1]);
      val = getenv (cp);
      js_free (cp);

      if (val)
	js_vm_make_string (vm, result_return, val, strlen (val));
    }
  /* ********************************************************************** */
  else if (method == ctx->s_popen)
    {
      char *cmd, *mode;
      FILE *fp;
      int readp = 0;

      INSECURE ();

      if (args->u.vinteger != 2)
	goto argument_error;
      if (args[1].type != JS_STRING || args[2].type != JS_STRING
	  || args[2].u.vstring->len > 10)
	goto argument_type_error;

      cmd = js_string_to_c_string (vm, &args[1]);
      mode = js_string_to_c_string (vm, &args[2]);

      for (i = 0; mode[i]; i++)
	if (mode[i] == 'r')
	  readp = 1;

      JS_VM_ALLOCATE_FD (vm, "System.popen()");
      fp = popen (cmd, mode);

      if (fp == NULL)
	JS_VM_FREE_FD (vm);
      else
	js_builtin_File_new (vm, result_return, cmd,
			     js_iostream_pipe (fp, readp), 0);

      js_free (cmd);
      js_free (mode);
    }
  /* ********************************************************************** */
  else if (method == ctx->s_print || method == ctx->s_error)
    {
      JSIOStream *stream;

      if (method == ctx->s_print)
	stream = vm->s_stdout;
      else
	stream = vm->s_stderr;

      for (i = 1; i <= args->u.vinteger; i++)
	{
	  JSNode result;

	  js_vm_to_string (vm, &args[i], &result);
	  js_iostream_write (stream, result.u.vstring->data,
			     result.u.vstring->len);
	}
    }
  /* ********************************************************************** */
  else if (method == ctx->s_sleep)
    {
      if (args->u.vinteger != 1)
	goto argument_error;
      if (args[1].type != JS_INTEGER)
	goto argument_type_error;

      /*sleep (args[1].u.vinteger);*/
    }
  /* ********************************************************************** */
  else if (method == ctx->s_strerror)
    {
      if (args->u.vinteger != 1)
	goto argument_error;
      if (args[1].type != JS_INTEGER)
	goto argument_type_error;

      cp = strerror (args[1].u.vinteger);
      js_vm_make_string (vm, result_return, cp, strlen (cp));
    }
  /* ********************************************************************** */
  else if (method == ctx->s_system)
    {
      INSECURE ();

      if (args->u.vinteger != 1)
	goto argument_error;
      if (args[1].type != JS_STRING)
	goto argument_type_error;

      result_return->type = JS_INTEGER;

      cp = js_string_to_c_string (vm, &args[1]);
      result_return->u.vinteger = system (cp);
      js_free (cp);
    }
  /* ********************************************************************** */
  else if (method == vm->syms.s_toString)
    {
      if (args->u.vinteger != 0)
	goto argument_error;

      js_vm_make_static_string (vm, result_return, "System", 6);
    }
  /* ********************************************************************** */
  else if (method == ctx->s_usleep)
    {
      if (args->u.vinteger != 1)
	goto argument_error;
      if (args[1].type != JS_INTEGER)
	goto argument_type_error;

      /*usleep (args[1].u.vinteger);*/
    }
  /* ********************************************************************** */
  else
    return JS_PROPERTY_UNKNOWN;

  return JS_PROPERTY_FOUND;


  /*
   * Error handling.
   */

 argument_error:
  sprintf (vm->error, "System.%s(): illegal amout of arguments",
	   js_vm_symname (vm, method));
  js_vm_error (vm);

 argument_type_error:
  sprintf (vm->error, "System.%s(): illegal argument",
	   js_vm_symname (vm, method));
  js_vm_error (vm);

 insecure_feature:
  sprintf (vm->error, "System.%s(): not allowed in secure mode",
	   js_vm_symname (vm, method));
  js_vm_error (vm);

  /* NOTREACHED */
  return 0;
}

/* Property proc. */
static int
property (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
	  void *instance_context, JSSymbol property, int set, JSNode *node)
{
  SystemCtx *ctx = builtin_info->obj_context;

  if (property == ctx->s_bits)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
#if SIZEOF_INT == 2
      node->u.vinteger = 16;
#else /* not SIZEOF_INT == 2 */

#if SIZEOF_LONG == 4
      node->u.vinteger = 32;
#else /* not SIZEOF_LONG == 4 */

#if SIZEOF_LONG == 8
      node->u.vinteger = 64;
#else /* not SIZEOF_LONG == 8 */

      /* Do not know. */
      node->u.vinteger = 0;

#endif /* not SIZEOF_LONG == 8 */
#endif /* not SIZEOF_LONG == 4 */
#endif /* not SIZEOF_INT == 2 */
    }
  else if (property == ctx->s_canonicalHost)
    {
      if (set)
	goto immutable;

      js_vm_make_static_string (vm, node, CANONICAL_HOST,
				strlen (CANONICAL_HOST));
    }
  else if (property == ctx->s_canonicalHostCPU)
    {
      if (set)
	goto immutable;

      js_vm_make_static_string (vm, node, CANONICAL_HOST_CPU,
				strlen (CANONICAL_HOST_CPU));
    }
  else if (property == ctx->s_canonicalHostVendor)
    {
      if (set)
	goto immutable;

      js_vm_make_static_string (vm, node, CANONICAL_HOST_VENDOR,
				strlen (CANONICAL_HOST_VENDOR));
    }
  else if (property == ctx->s_canonicalHostOS)
    {
      if (set)
	goto immutable;

      js_vm_make_static_string (vm, node, CANONICAL_HOST_OS,
				strlen (CANONICAL_HOST_OS));
    }
  else if (property == ctx->s_errno)
    {
      if (set)
	goto immutable;

      node->type = JS_INTEGER;
      node->u.vinteger = errno;
    }
  else if (property == ctx->s_lineBreakSequence)
    {
      if (set)
	goto immutable;

      js_vm_make_static_string (vm, node, JS_HOST_LINE_BREAK,
				JS_HOST_LINE_BREAK_LEN);
    }
  else if (property == ctx->s_stderr)
    {
      if (set)
	goto immutable;

      JS_COPY (node, &ctx->pstderr);
    }
  else if (property == ctx->s_stdin)
    {
      if (set)
	goto immutable;

      JS_COPY (node, &ctx->pstdin);
    }
  else if (property == ctx->s_stdout)
    {
      if (set)
	goto immutable;

      JS_COPY (node, &ctx->pstdout);
    }

  else
    {
      if (!set)
	node->type = JS_UNDEFINED;

      return JS_PROPERTY_UNKNOWN;
    }

  return JS_PROPERTY_FOUND;


  /*
   * Error handling.
   */

 immutable:
  sprintf (vm->error, "System.%s: immutable property",
	   js_vm_symname (vm, property));
  js_vm_error (vm);

  /* NOTREACHED. */
  return 0;
}

/* Mark proc. */
static void
mark (JSBuiltinInfo *builtin_info, void *instance_context)
{
  SystemCtx *ctx = builtin_info->obj_context;

  js_vm_mark (&ctx->pstderr);
  js_vm_mark (&ctx->pstdin);
  js_vm_mark (&ctx->pstdout);
}


/*
 * Global functions.
 */

void
js_builtin_System (JSVirtualMachine *vm)
{
  JSNode *n;
  JSBuiltinInfo *info;
  SystemCtx *ctx;

  ctx = js_calloc (vm, 1, sizeof (*ctx));

  ctx->s_chdir			= js_vm_intern (vm, "chdir");
  ctx->s_error			= js_vm_intern (vm, "error");
  ctx->s_exit			= js_vm_intern (vm, "exit");
  ctx->s_getcwd			= js_vm_intern (vm, "getcwd");
  ctx->s_getenv			= js_vm_intern (vm, "getenv");
  ctx->s_popen			= js_vm_intern (vm, "popen");
  ctx->s_print			= js_vm_intern (vm, "print");
  ctx->s_sleep			= js_vm_intern (vm, "sleep");
  ctx->s_strerror		= js_vm_intern (vm, "strerror");
  ctx->s_system			= js_vm_intern (vm, "system");
  ctx->s_usleep			= js_vm_intern (vm, "usleep");

  ctx->s_bits			= js_vm_intern (vm, "bits");
  ctx->s_canonicalHost		= js_vm_intern (vm, "canonicalHost");
  ctx->s_canonicalHostCPU	= js_vm_intern (vm, "canonicalHostCPU");
  ctx->s_canonicalHostVendor	= js_vm_intern (vm, "canonicalHostVendor");
  ctx->s_canonicalHostOS	= js_vm_intern (vm, "canonicalHostOS");
  ctx->s_errno			= js_vm_intern (vm, "errno");
  ctx->s_lineBreakSequence	= js_vm_intern (vm, "lineBreakSequence");
  ctx->s_stderr			= js_vm_intern (vm, "stderr");
  ctx->s_stdin			= js_vm_intern (vm, "stdin");
  ctx->s_stdout			= js_vm_intern (vm, "stdout");

  /* Object information. */

  info = js_vm_builtin_info_create (vm);

  info->method_proc 		= method;
  info->property_proc		= property;
  info->mark_proc		= mark;
  info->obj_context 		= ctx;
  info->obj_context_delete	= js_free;

  /* Define it. */
  n = &vm->globals[js_vm_intern (vm, "System")];
  js_vm_builtin_create (vm, n, info, NULL);

  /* Enter system properties. */
  js_builtin_File_new (vm, &ctx->pstderr, "stdout", vm->s_stderr, 1);
  js_builtin_File_new (vm, &ctx->pstdin, "stdin", vm->s_stdin, 1);
  js_builtin_File_new (vm, &ctx->pstdout, "stdout", vm->s_stdout, 1);
}
#endif	/*CL_EXPERIMENTAL*/