/***************************************************************************
 *
 * 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: zpython.c,v 1.32.2.6.2.4 2001/09/19 19:22:17 bazsi Exp $
 *
 * Author  : Bazsi
 * Auditor : kisza
 * Last version: 1.22
 * Notes:
 *
 ***************************************************************************/

/* 
 * this module implements the interface with python 
 */
#include <zorp/zpython.h>
#include <zorp/log.h>
#include <zorp/io.h>
#include <zorp/policy.h>
#include <zorp/sysdep.h>

#include <zorp/pysockaddr.h>
#include <zorp/pylisten.h>
#include <zorp/pyreceive.h>
#include <zorp/pyconnect.h>
#include <zorp/pystream.h>
#include <zorp/pyproxy.h>
#include <zorp/pyipchains.h>
#include <zorp/pysockaddr.h>
#include <zorp/pyctrack.h>
#include <zorp/pytisauth.h>
#include <zorp/sockaddr.h>
#include <netdb.h>

PyThreadState *z_python_global_state;

/* exported python methods */

/*+

  Called by Python to send a message to the event log. Declaration
  in Python would be:

    def z_py_log(verbosity, priority, msg)

  Parameters:
    self       unused, required by Python calling mechanism
    args       arguments tuple

  Returns:
    PyNone

  +*/
static PyObject *
z_py_log(PyObject *self, PyObject *args)
{
  unsigned long verbosity;
  char *class, *msg;

  if (!PyArg_ParseTuple(args, "sis", &class, &verbosity, &msg))
    return NULL;
  /*NOLOG*/
  z_llog(class, verbosity, "%s", msg);
  Py_XINCREF(Py_None);
  return Py_None;
}

/*+
  +*/

static PyObject *
z_py_quit(PyObject *self, PyObject *args)
{
  int exit_code;
  
  if (!PyArg_ParseTuple(args, "i", &exit_code))
    return NULL;
  z_main_quit(exit_code);
  Py_XINCREF(Py_None);
  return Py_None;
}

static PyObject *
z_py_close_fd(PyObject *self, PyObject *args)
{
  int fd;
  
  if (!PyArg_ParseTuple(args, "i", &fd))
    return NULL;
  /*LOG
    This debug message indicates that the given fd is closed from Python.
   */
  z_log(NULL, CORE_DEBUG, 8, "closing fd from Python; fd='%d'", fd);
  close(fd);
  Py_XINCREF(Py_None);
  return Py_None;
}

/*+

  Query and return the original destination of a redirected connection.
  Python declaration would be:

    def z_py_get_transparent_dest(fd)

  Parameters:
    self       unused, required by Python calling mechanism
    args       arguments tuple

  Returns:
    ZorpSockAddr

  +*/
static PyObject *
z_py_get_transparent_dest(PyObject *self, PyObject *args)
{
  char buf[128];
  struct sockaddr *sa = (struct sockaddr *) buf;
  socklen_t salen = sizeof(buf);
  ZSockAddr *zsa;
  PyObject *res;
  int fd;
  
  if (!PyArg_ParseTuple(args, "i", &fd))
    return NULL;

  if (!z_get_transparent_dest(fd, sa, &salen))
    {
      PyErr_SetString(PyExc_IOError, "Error getting transparent destination");
      return NULL;
    }
  zsa = z_sockaddr_new(sa, salen);
  if (!zsa)
    {
      Py_XINCREF(Py_None);
      return Py_None;
    }
  res = z_py_zorp_sockaddr_new(zsa);
  z_sockaddr_unref(zsa);
  return res;
}

static PyObject *
z_py_get_sock_name(PyObject *self, PyObject *args)
{
  gchar sabuf[1500];
  gint sabuflen = sizeof(sabuf);
  gint fd;

  if (!PyArg_ParseTuple(args, "i", &fd))
    return NULL;
    
  if (getsockname(fd, (struct sockaddr *) sabuf, &sabuflen) < 0)
    {
      /*LOG
        This message indicates that the getsockname() system call failed.
       */
      z_log(NULL, CORE_ERROR, 4, "getsockname() failed; fd='%d', error='%m'", fd);
      Py_XINCREF(Py_None);
      return Py_None;
    }
  else
    {
      ZSockAddr *local = z_sockaddr_new((struct sockaddr *) sabuf, sabuflen);
      PyObject *res = z_py_zorp_sockaddr_new(local);
      
      z_sockaddr_unref(local);
      return res;
    }
}

static PyMethodDef zorp_funcs[] = 
{
  { "log", z_py_log, METH_VARARGS },
  { "getTransparentDestination", z_py_get_transparent_dest, METH_VARARGS },
  { "getSockName", z_py_get_sock_name, METH_VARARGS },
  { "closeFd", z_py_close_fd, METH_VARARGS },
  { "quit", z_py_quit, METH_VARARGS },
  { NULL, NULL, 0 }
};

/*+

  Initialize the low level Python-Zorp interface. Called by the Python
  policy implementation.

  Parameters:
    none

  Returns:
    TRUE if initialization was successful

  +*/
gboolean
z_python_init(void)
{
  FILE *bootstrap;
  char buf[2048];

  if (getenv("PYTHONPATH") == NULL)
    {
      snprintf(buf, sizeof(buf), "PYTHONPATH=%s", ZORP_DATADIR "/pylib");
    }
  else
    {
      snprintf(buf, sizeof(buf), "PYTHONPATH=%s:%s", ZORP_DATADIR "/pylib", getenv("PYTHONPATH"));
    }
  putenv(buf);

  Py_Initialize();
  PyEval_InitThreads();

  
  if ((bootstrap = fopen(ZORP_POLICY_BOOT_FILE, "r")) == NULL)
    {
      /*LOG
        This message indicates that the policy.boot file couldn't be opened for
        reading.
       */
      z_log(NULL, CORE_ERROR, 0, "Error opening policy.boot file; file='%s'", ZORP_POLICY_BOOT_FILE);
      return FALSE;
    }
  PyRun_SimpleFile(bootstrap, ZORP_POLICY_BOOT_FILE);
  fclose(bootstrap);

  /* PyImport_AddModule("Zorp.Zorp"); */
  Py_InitModule("Zorp.Zorp", zorp_funcs);

  z_py_zorp_listen_init();
  z_py_zorp_receive_init();
  z_py_zorp_connect_init();
  z_py_zorp_stream_init();
  z_py_zorp_proxy_init();
  z_py_zorp_sockaddr_init();
  z_py_zorp_ipchains_init();
  z_py_zorp_ctracker_init();
  z_py_zorp_tisauth_init();
  
  z_python_global_state = PyEval_SaveThread();
  return TRUE;
}

/*+

  Called to deinitialize the Python-ZORP interface.

  Parameters:
    none
  
  Returns:
    TRUE if deinitialization was successful.

  +*/
gboolean
z_python_destroy(void)
{
  z_policy_lock(z_python_global_state);
  Py_Finalize();
  Py_Finalize();
  return TRUE;
}

/*+

  Lock the python interpreter, without setting the current thread pointer.

  Parameters:
    none

  Returns:
    none
  +*/
void 
z_python_lock(void)
{
  PyEval_AcquireLock();
}

/*+

  Unlock the python interpreter.

  Parameters:
    none

  Returns:
    none

  +*/
void 
z_python_unlock(void)
{
  PyEval_ReleaseLock();
}

