/***************************************************************************
 *
 * Copyright (c) 2000, 2001 BalaBit IT Ltd, Budapest, Hungary
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.
 *
 * $Id: pypolicy.c,v 1.25.2.2.2.2 2001/09/19 19:22:17 bazsi Exp $
 *
 * Author  : Bazsi
 * Auditor : kisza
 * Last version: 1.20
 * Notes:
 *   - comments
 *
 ***************************************************************************/

/* 
 * This is an implementation of the policy interface with the python
 * interpreter 
 */

#include <zorp/policy.h>
#include <zorp/log.h>

#include <Python.h>
#include <assert.h>

#include <stdarg.h>

static PyObject *z_main_module = NULL;

/* the function interface (not the implementation) here should be
   independent of python */

gboolean
z_python_policy_init(void)
{
/*
  Py_DebugFlag = 1;
  Py_VerboseFlag = 1;
*/

  if (!z_python_init())
    return FALSE;

  z_policy_lock(z_python_global_state);
  z_main_module = PyImport_AddModule("__main__");
  Py_XINCREF(z_main_module);
  z_policy_unlock(z_python_global_state);

  return TRUE;
}

void
z_policy_destroy(void)
{
  z_policy_lock(z_python_global_state);
  Py_XDECREF(z_main_module);
  z_policy_unlock(z_python_global_state);
  z_python_destroy();
}

gboolean
z_policy_read(char *file)
{
  FILE *script;
  int res = -1;
  
  script = fopen(file, "r");
  if (script)
    {
      z_policy_lock(z_python_global_state);
      res = PyRun_SimpleFile(script, file);
      fclose(script);
      z_policy_unlock(z_python_global_state);
    }  
  return res != -1;
}

gboolean
z_policy_init(char *name)
{
  PyObject *py_init_func, *res;
  
  z_policy_lock(z_python_global_state);
  
  py_init_func = PyObject_GetAttrString(z_main_module, "init");
  res = PyObject_CallFunction(py_init_func, "(s)", name);
  Py_XDECREF(py_init_func);
  if (!res && PyErr_Occurred())
    {
      PyErr_Print();
    }

  z_policy_unlock(z_python_global_state);
  
  return res != NULL;
}

gint 
z_policy_setattr(PyObject *handler, char *name, PyObject *value)
{
  gint res;
  
  res = PyObject_SetAttrString(handler, name, value);
  Py_XDECREF(value);
  return res;
}

PyObject *
z_policy_getattr(PyObject *handler, char *name)
{
  PyObject *res;
  
  res = PyObject_GetAttrString(handler, name);
  return res;
}

PyObject *
z_session_getattr(PyObject *handler, char *name)
{
  PyObject *res, *res2;
  
  res = PyObject_GetAttrString(handler, "session");
  if (!res)
    return NULL;
  res2 = PyObject_GetAttrString(res, name);
  Py_XDECREF(res);
  return res2;
}

PyObject *
z_policy_call_object(PyObject *func, PyObject *args)
{
  PyObject *res = PyObject_CallObject(func, args);
  
  Py_XDECREF(args);
  if (!res)
    {
      PyObject *m = PyImport_AddModule("sys");
      PyErr_Print();
      PyObject_SetAttrString(m, "last_traceback", Py_None);
    }
  return res;
}

PyObject *
z_policy_call(PyObject *handler, char *name, PyObject *args, gboolean *called)
{
  PyObject *attr;
  PyObject *res;

  z_enter();
  assert(PyThreadState_GET());
  attr = PyObject_GetAttrString(handler, name);
  if (!attr || !PyCallable_Check(attr))
    {
      if (attr)
        {
          Py_XDECREF(attr);
          PyErr_Format(PyExc_TypeError, "Event must be callable: %s", name);
          PyErr_Print(); /* produce a backtrace, and handle it immediately */
        }
      PyErr_Clear();
      Py_XDECREF(args);
      res = NULL;
      if (called)
        *called = FALSE;
    }
  else
    {
      if (called)
        *called = TRUE;
      res = PyObject_CallObject(attr, (PyObject *) args);
      Py_XDECREF(args);
      Py_XDECREF(attr);
      if (!res)
        {
          PyObject *m = PyImport_AddModule("sys");
          PyErr_Print();
          PyObject_SetAttrString(m, "last_traceback", Py_None); 
        }
    }
  z_leave();
  return res;
}

gint 
z_policy_event(PyObject *handler, char *name, PyObject *args)
{
  PyObject *res;
  unsigned long c_res;
  gboolean called;

  Py_XINCREF(args);
  res = z_policy_call(handler, "preProcessEvent", args, &called);
  if (res)
    {
      if (PyInt_Check(res))
	{
	  c_res = PyInt_AsLong(res);
	  Py_XDECREF(res);
	  if (c_res != Z_UNSPEC)
	    {
	      Py_XDECREF(args);
	      return c_res;
	    }
	}
      else
	{
	  PyErr_Format(PyExc_TypeError, "preProcessEvent() handlers should return an int.");
	  PyErr_Print(); /* produce a backtrace, and handle it immediately */
	  Py_XDECREF(res);
	}
    }
  else
    if (called)
      return Z_ABORT;
  Py_XINCREF(args);
  res = z_policy_call(handler, name, args, &called);
  if (res)
    {
      if (PyInt_Check(res))
	{
	  c_res = PyInt_AsLong(res);
	  Py_XDECREF(res);
	  if (c_res != Z_UNSPEC)
	    {
	      Py_XDECREF(args);
	      return c_res;
	    }
	}
      else
	{
	  PyErr_Format(PyExc_TypeError, "Event handlers should return an int: %s", name);
	  PyErr_Print(); /* produce a backtrace, and handle it immediately */
	  Py_XDECREF(res);
	}
    }
  else
    if (called)
      return Z_ABORT;
  res = z_policy_call(handler, "postProcessEvent", args, &called);
  if (res)
    {
      if (PyInt_Check(res))
	{
	  c_res = PyInt_AsLong(res);
	  Py_XDECREF(res);
	  return c_res;
	}
      else
	{
	  PyErr_Format(PyExc_TypeError, "postProcessEvent() handlers should return an int.");
	  PyErr_Print(); /* produce a backtrace, and handle it immediately */
	  Py_XDECREF(res);
	}
    }
  else
    if (called)
      return Z_ABORT;
  return Z_UNSPEC;
}

void
z_policy_lock(ZPolicyThread *thread)
{
  PyEval_AcquireThread(thread);
}

void
z_policy_unlock(ZPolicyThread *thread)
{
  PyEval_ReleaseThread(thread);
}
