Add Python module, still WIP, but should be usable. No xml
authorsobomax <sobomax@sippysoft.com>
Fri, 29 Jan 2010 12:30:28 +0000 (04:30 -0800)
committersobomax <sobomax@sippysoft.com>
Fri, 29 Jan 2010 12:30:28 +0000 (04:30 -0800)
documentation yet, some notes can be found here:

http://www.b2bua.org/wiki/PythonSEROpenSIPSKamailio

If anybody wants to step in and lend me a helping hand with the
xml docs please be my guest.

Thanks to the Daniel-Constantin Mierla for the help with the
port.

12 files changed:
modules/app_python/Makefile [new file with mode: 0644]
modules/app_python/handler.py [new file with mode: 0644]
modules/app_python/python_exec.c [new file with mode: 0644]
modules/app_python/python_exec.h [new file with mode: 0644]
modules/app_python/python_iface.c [new file with mode: 0644]
modules/app_python/python_iface.h [new file with mode: 0644]
modules/app_python/python_mod.c [new file with mode: 0644]
modules/app_python/python_mod.h [new file with mode: 0644]
modules/app_python/python_msgobj.c [new file with mode: 0644]
modules/app_python/python_msgobj.h [new file with mode: 0644]
modules/app_python/python_support.c [new file with mode: 0644]
modules/app_python/python_support.h [new file with mode: 0644]

diff --git a/modules/app_python/Makefile b/modules/app_python/Makefile
new file mode 100644 (file)
index 0000000..446141e
--- /dev/null
@@ -0,0 +1,31 @@
+# $Id$
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=app_python.so
+
+# If you have multiple Python versions installed make sure to modify the
+# the following to point to the correct instance. Module has been tested
+# to work with 2.6 and 2.5. Python 2.4 has been only confirmed to compile,
+# but no testing has been done with that.
+PYTHON?=python
+
+PYTHON_VERSION=${shell ${PYTHON} -c "import distutils.sysconfig;print distutils.sysconfig.get_config_var('VERSION')"}
+PYTHON_LIBDIR=${shell ${PYTHON} -c "import distutils.sysconfig;print distutils.sysconfig.get_config_var('LIBDIR')"}
+PYTHON_LDFLAGS=${shell ${PYTHON} -c "import distutils.sysconfig;print distutils.sysconfig.get_config_var('LINKFORSHARED')"}
+PYTHON_INCDIR=${shell ${PYTHON} -c "import distutils.sysconfig;print distutils.sysconfig.get_python_inc()"}
+
+LIBS=-L${PYTHON_LIBDIR} ${PYTHON_LDFLAGS} -lpython${PYTHON_VERSION}
+
+ifeq ($(OS), freebsd)
+LIBS+=-pthread
+endif
+
+DEFS+=-I${PYTHON_INCDIR}
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+include ../../Makefile.modules
+
diff --git a/modules/app_python/handler.py b/modules/app_python/handler.py
new file mode 100644 (file)
index 0000000..6e3eb5e
--- /dev/null
@@ -0,0 +1,26 @@
+import sys
+from Router import LM_ERR
+
+class test:
+    def __init__(self):
+        LM_ERR('test.__init__\n')
+
+    def child_init(self, y):
+        LM_ERR('test.child_init(%d)\n' % y)
+        return 0
+
+    def handler(self, msg, args):
+        LM_ERR('test.handler(%s, %s)\n' % (msg.Type, str(arg)))
+        if msg.Type == 'SIP_REQUEST':
+            if msg.Method == 'INVITE':
+                msg.rewrite_ruri('sip:0022@192.168.2.24:5073')
+            LM_ERR('SIP request, method = %s, RURI = %s, From = %s\n' % (msg.Method, msg.RURI, msg.getHeader('from')))
+            LM_ERR('received from %s:%d\n' % msg.src_address)
+        else:
+            LM_ERR('SIP reply, status = %s\n' % msg.Status)
+            LM_ERR('received from %s:%d\n' % msg.src_address)
+        msg.call_function('append_hf', 'This-is: test\r\n')
+        return 1
+
+def mod_init():
+    return test()
diff --git a/modules/app_python/python_exec.c b/modules/app_python/python_exec.c
new file mode 100644 (file)
index 0000000..9480ec9
--- /dev/null
@@ -0,0 +1,125 @@
+/* $Id$
+ *
+ * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of SIP-Router, a free SIP server.
+ *
+ * SIP-Router 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
+ *
+ * SIP-Router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+*/
+
+#include <Python.h>
+
+#include "../../mem/mem.h"
+#include "../../data_lump.h"
+#include "../../parser/parse_param.h"
+#include "../../parser/msg_parser.h"
+#include "../../dprint.h"
+#include "../../action.h"
+#include "../../config.h"
+#include "../../parser/parse_uri.h"
+
+#include "python_exec.h"
+#include "python_mod.h"
+#include "python_msgobj.h"
+#include "python_support.h"
+
+int
+python_exec1(struct sip_msg* _msg, char* method_name, char *foobar)
+{
+    return python_exec2(_msg, method_name, NULL);
+}
+
+int
+python_exec2(struct sip_msg *_msg, char *method_name, char *mystr)
+{
+    PyObject *pFunc, *pArgs, *pValue, *pResult;
+    PyObject *msg;
+    int rval;
+
+    PyEval_AcquireLock();
+    PyThreadState_Swap(myThreadState);
+
+    pFunc = PyObject_GetAttrString(handler_obj, method_name);
+    if (pFunc == NULL || !PyCallable_Check(pFunc)) {
+        LM_ERR("%s not found or is not callable\n", method_name);
+        Py_XDECREF(pFunc);
+        PyThreadState_Swap(NULL);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    msg = newmsgobject(_msg);
+    if (msg == NULL) {
+        LM_ERR("can't create MSGtype instance\n");
+        Py_DECREF(pFunc);
+        PyThreadState_Swap(NULL);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    pArgs = PyTuple_New(mystr == NULL ? 1 : 2);
+    if (pArgs == NULL) {
+        LM_ERR("PyTuple_New() has failed\n");
+        msg_invalidate(msg);
+        Py_DECREF(msg);
+        Py_DECREF(pFunc);
+        PyThreadState_Swap(NULL);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+    PyTuple_SetItem(pArgs, 0, msg);
+    /* Tuple steals msg */
+
+    if (mystr != NULL) {
+        pValue = PyString_FromString(mystr);
+        if (pValue == NULL) {
+            LM_ERR("PyString_FromString(%s) has failed\n", mystr);
+            msg_invalidate(msg);
+            Py_DECREF(pArgs);
+            Py_DECREF(pFunc);
+            PyThreadState_Swap(NULL);
+            PyEval_ReleaseLock();
+            return -1;
+        }
+        PyTuple_SetItem(pArgs, 1, pValue);
+        /* Tuple steals pValue */
+    }
+
+    pResult = PyObject_CallObject(pFunc, pArgs);
+    msg_invalidate(msg);
+    Py_DECREF(pArgs);
+    Py_DECREF(pFunc);
+    if (PyErr_Occurred()) {
+        Py_XDECREF(pResult);
+        python_handle_exception("python_exec2");
+        PyThreadState_Swap(NULL);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    if (pResult == NULL) {
+        LM_ERR("PyObject_CallObject() returned NULL\n");
+        PyThreadState_Swap(NULL);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    rval = PyInt_AsLong(pResult);
+    Py_DECREF(pResult);
+    PyThreadState_Swap(NULL);
+    PyEval_ReleaseLock();
+    return rval;
+}
diff --git a/modules/app_python/python_exec.h b/modules/app_python/python_exec.h
new file mode 100644 (file)
index 0000000..51ae766
--- /dev/null
@@ -0,0 +1,31 @@
+/* $Id$
+ *
+ * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of SIP-Router, a free SIP server.
+ *
+ * SIP-Router 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
+ *
+ * SIP-Router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+*/
+
+#ifndef _PYTHON_EXEC_H
+#define  _PYTHON_EXEC_H
+
+#include "../../parser/msg_parser.h"
+
+int python_exec1(struct sip_msg *, char *, char *);
+int python_exec2(struct sip_msg *, char *, char *);
+
+#endif
diff --git a/modules/app_python/python_iface.c b/modules/app_python/python_iface.c
new file mode 100644 (file)
index 0000000..ecf692c
--- /dev/null
@@ -0,0 +1,49 @@
+/* $Id$
+ *
+ * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of SIP-Router, a free SIP server.
+ *
+ * SIP-Router 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
+ *
+ * SIP-Router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+*/
+
+#include "../../action.h"
+#include "../../dprint.h"
+#include "../../route_struct.h"
+#include "python_exec.h"
+
+#include <Python.h>
+
+/* Return the number of arguments of the application command line */
+static PyObject*
+router_LM_ERR(PyObject *self, PyObject *args)
+{
+    char *msg;
+
+    if(!PyArg_ParseTuple(args, "s:LM_ERR", &msg))
+        return NULL;
+
+    LM_ERR("%s", msg);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+PyMethodDef RouterMethods[] = {
+    {"LM_ERR", router_LM_ERR, METH_VARARGS,
+     "Pring error message."},
+    {NULL, NULL, 0, NULL}
+};
diff --git a/modules/app_python/python_iface.h b/modules/app_python/python_iface.h
new file mode 100644 (file)
index 0000000..8491ff4
--- /dev/null
@@ -0,0 +1,30 @@
+/* $Id$
+ *
+ * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of SIP-Router, a free SIP server.
+ *
+ * SIP-Router 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
+ *
+ * SIP-Router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+*/
+
+#ifndef _PYTHON_IFACE_H
+#define  _PYTHON_IFACE_H
+
+#include <Python.h>
+
+extern PyMethodDef RouterMethods[];
+
+#endif
diff --git a/modules/app_python/python_mod.c b/modules/app_python/python_mod.c
new file mode 100644 (file)
index 0000000..f943e07
--- /dev/null
@@ -0,0 +1,291 @@
+/* $Id$
+ *
+ * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of SIP-Router, a free SIP server.
+ *
+ * SIP-Router 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
+ *
+ * SIP-Router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+*/
+
+#include "../../str.h"
+#include "../../sr_module.h"
+
+#include "python_exec.h"
+#include "python_iface.h"
+#include "python_msgobj.h"
+#include "python_support.h"
+
+#include <Python.h>
+#include <libgen.h>
+
+MODULE_VERSION
+
+static int mod_init(void);
+static int child_init(int rank);
+static void mod_destroy(void);
+
+static str script_name = {.s = "/usr/local/etc/sip-router/handler.py", .len = 0};
+static str mod_init_fname = { .s = "mod_init", .len = 0};
+static str child_init_mname = { .s = "child_init", .len = 0};
+PyObject *handler_obj;
+PyObject *format_exc_obj;
+
+PyThreadState *myThreadState;
+
+/** module parameters */
+static param_export_t params[]={
+    {"script_name",        STR_PARAM, &script_name },
+    {"mod_init_function",  STR_PARAM, &mod_init_fname },
+    {"child_init_method",  STR_PARAM, &child_init_mname },
+    {0,0,0}
+};
+
+/*
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+    { "python_exec", (cmd_function)python_exec1, 1,  NULL, 0,
+      REQUEST_ROUTE | FAILURE_ROUTE
+      | ONREPLY_ROUTE | BRANCH_ROUTE },
+    { "python_exec", (cmd_function)python_exec2, 2,  NULL, 0,
+      REQUEST_ROUTE | FAILURE_ROUTE
+      | ONREPLY_ROUTE | BRANCH_ROUTE },
+    { 0, 0, 0, 0, 0, 0 }
+};
+
+/** module exports */
+struct module_exports exports = {
+    "app_python",                   /* module name */
+    RTLD_NOW | RTLD_GLOBAL,         /* dlopen flags */
+    cmds,                           /* exported functions */
+    params,                         /* exported parameters */
+    0,                              /* exported statistics */
+    0,                              /* exported MI functions */
+    0,                              /* exported pseudo-variables */
+    0,                              /* extra processes */
+    mod_init,                       /* module initialization function */
+    (response_function) NULL,       /* response handling function */
+    (destroy_function) mod_destroy, /* destroy function */
+    child_init                      /* per-child init function */
+};
+
+static int
+mod_init(void)
+{
+    char *dname, *bname;
+    int i;
+    PyObject *sys_path, *pDir, *pModule, *pFunc, *pArgs;
+    PyThreadState *mainThreadState;
+
+    if (script_name.len == 0) {
+        script_name.len = strlen(script_name.s);
+    }
+    if (mod_init_fname.len == 0) {
+        mod_init_fname.len = strlen(mod_init_fname.s);
+    }
+    if (child_init_mname.len == 0) {
+        child_init_mname.len = strlen(child_init_mname.s);
+    }
+
+    dname = dirname(script_name.s);
+    if (strlen(dname) == 0)
+        dname = ".";
+    bname = basename(script_name.s);
+    i = strlen(bname);
+    if (bname[i - 1] == 'c' || bname[i - 1] == 'o')
+        i -= 1;
+    if (bname[i - 3] == '.' && bname[i - 2] == 'p' && bname[i - 1] == 'y') {
+        bname[i - 3] = '\0';
+    } else {
+        LM_ERR("%s: script_name doesn't look like a python script\n",
+          script_name.s);
+        return -1;
+    }
+
+    Py_Initialize();
+    PyEval_InitThreads();
+    mainThreadState = PyThreadState_Get();
+
+    Py_InitModule("Router", RouterMethods);
+
+    if (python_msgobj_init() != 0) {
+        LM_ERR("python_msgobj_init() has failed\n");
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    sys_path = PySys_GetObject("path");
+    /* PySys_GetObject doesn't pass reference! No need to DEREF */
+    if (sys_path == NULL) {
+        LM_ERR("cannot import sys.path\n");
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    pDir = PyString_FromString(dname);
+    if (pDir == NULL) {
+        LM_ERR("PyString_FromString() has filed\n");
+        PyEval_ReleaseLock();
+        return -1;
+    }
+    PyList_Insert(sys_path, 0, pDir);
+    Py_DECREF(pDir);
+
+    pModule = PyImport_ImportModule(bname);
+    if (pModule == NULL) {
+        LM_ERR("cannot import %s\n", bname);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    pFunc = PyObject_GetAttrString(pModule, mod_init_fname.s);
+    Py_DECREF(pModule);
+    /* pFunc is a new reference */
+    if (pFunc == NULL || !PyCallable_Check(pFunc)) {
+        LM_ERR("cannot locate %s function in %s module\n",
+          mod_init_fname.s, script_name.s);
+        Py_XDECREF(pFunc);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    pModule = PyImport_ImportModule("traceback");
+    if (pModule == NULL) {
+        LM_ERR("cannot import traceback module\n");
+        Py_DECREF(pFunc);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    format_exc_obj = PyObject_GetAttrString(pModule, "format_exception");
+    Py_DECREF(pModule);
+    if (format_exc_obj == NULL || !PyCallable_Check(format_exc_obj)) {
+        LM_ERR("cannot locate format_exception function in" \
+          " traceback module\n");
+        Py_XDECREF(format_exc_obj);
+        Py_DECREF(pFunc);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    pArgs = PyTuple_New(0);
+    if (pArgs == NULL) {
+        LM_ERR("PyTuple_New() has failed\n");
+        Py_DECREF(pFunc);
+        Py_DECREF(format_exc_obj);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    handler_obj = PyObject_CallObject(pFunc, pArgs);
+    Py_DECREF(pFunc);
+    Py_DECREF(pArgs);
+
+    if (PyErr_Occurred()) {
+        python_handle_exception("mod_init");
+        Py_XDECREF(handler_obj);
+        Py_DECREF(format_exc_obj);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    if (handler_obj == NULL) {
+        LM_ERR("%s function has not returned object\n",
+          mod_init_fname.s);
+        Py_DECREF(format_exc_obj);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    myThreadState = PyThreadState_New(mainThreadState->interp);
+    PyEval_ReleaseLock();
+
+    return 0;
+}
+
+static int
+child_init(int rank)
+{
+    PyObject *pFunc, *pArgs, *pValue, *pResult;
+    int rval;
+
+    PyEval_AcquireLock();
+    PyThreadState_Swap(myThreadState);
+
+    pFunc = PyObject_GetAttrString(handler_obj, child_init_mname.s);
+    if (pFunc == NULL || !PyCallable_Check(pFunc)) {
+        LM_ERR("cannot locate %s function\n", child_init_mname.s);
+        if (pFunc != NULL) {
+            Py_DECREF(pFunc);
+        }
+        PyThreadState_Swap(NULL);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    pArgs = PyTuple_New(1);
+    if (pArgs == NULL) {
+        LM_ERR("PyTuple_New() has failed\n");
+        Py_DECREF(pFunc);
+        PyThreadState_Swap(NULL);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    pValue = PyInt_FromLong(rank);
+    if (pValue == NULL) {
+        LM_ERR("PyInt_FromLong() has failed\n");
+        Py_DECREF(pArgs);
+        Py_DECREF(pFunc);
+        PyThreadState_Swap(NULL);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+    PyTuple_SetItem(pArgs, 0, pValue);
+    /* pValue has been stolen */
+
+    pResult = PyObject_CallObject(pFunc, pArgs);
+    Py_DECREF(pFunc);
+    Py_DECREF(pArgs);
+
+    if (PyErr_Occurred()) {
+        python_handle_exception("child_init");
+        Py_XDECREF(pResult);
+        PyThreadState_Swap(NULL);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    if (pResult == NULL) {
+        LM_ERR("PyObject_CallObject() returned NULL but no exception!\n");
+        PyThreadState_Swap(NULL);
+        PyEval_ReleaseLock();
+        return -1;
+    }
+
+    rval = PyInt_AsLong(pResult);
+    Py_DECREF(pResult);
+    PyThreadState_Swap(NULL);
+    PyEval_ReleaseLock();
+    return rval;
+}
+
+static void
+mod_destroy(void)
+{
+
+    return;
+}
diff --git a/modules/app_python/python_mod.h b/modules/app_python/python_mod.h
new file mode 100644 (file)
index 0000000..26af411
--- /dev/null
@@ -0,0 +1,32 @@
+/* $Id$
+ *
+ * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of SIPRouter, a free SIP server.
+ *
+ * SIP-Router 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
+ *
+ * SIP-Router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+*/
+
+#ifndef _PYTHON_MOD_H
+#define  _PYTHON_MOD_H
+
+#include <Python.h>
+
+extern PyObject *handler_obj;
+extern PyObject *format_exc_obj;
+extern PyThreadState *myThreadState;
+
+#endif
diff --git a/modules/app_python/python_msgobj.c b/modules/app_python/python_msgobj.c
new file mode 100644 (file)
index 0000000..3b54ade
--- /dev/null
@@ -0,0 +1,532 @@
+/* $Id$
+ *
+ * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of SIP-Router, a free SIP server.
+ *
+ * SIP-Router 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
+ *
+ * SIP-Router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+*/
+
+#include "../../action.h"
+#include "../../mem/mem.h"
+#include "../../sr_module.h"
+#include "../../parser/msg_parser.h"
+
+#include <Python.h>
+#include "structmember.h"
+
+#ifndef Py_TYPE
+#define Py_TYPE(ob)               (((PyObject*)(ob))->ob_type)
+#endif
+
+typedef struct {
+    PyObject_HEAD
+    struct sip_msg *msg;
+} msgobject;
+
+static PyTypeObject MSGtype;
+
+#define is_msgobject(v)         ((v)->ob_type == &MSGtype)
+
+msgobject *
+newmsgobject(struct sip_msg *msg)
+{
+    msgobject *msgp;
+
+    msgp = PyObject_New(msgobject, &MSGtype);
+    if (msgp == NULL)
+        return NULL;
+
+    msgp->msg = msg;
+    return msgp;
+}
+
+void
+msg_invalidate(msgobject *self)
+{
+
+    self->msg = NULL;
+}
+
+static void
+msg_dealloc(msgobject *msgp)
+{
+
+    PyObject_Del(msgp);
+}
+
+static PyObject *
+msg_copy(msgobject *self)
+{
+    msgobject *msgp;
+
+    if ((msgp = newmsgobject(self->msg)) == NULL)
+        return NULL;
+
+    return (PyObject *)msgp;
+}
+
+static PyObject *
+msg_rewrite_ruri(msgobject *self, PyObject *args)
+{
+    char *ruri;
+    struct action act;
+    struct run_act_ctx ra_ctx;
+
+    if (self->msg == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    if ((self->msg->first_line).type != SIP_REQUEST) {
+        PyErr_SetString(PyExc_RuntimeError, "Not a request message - "
+          "rewrite is not possible.\n");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    if(!PyArg_ParseTuple(args, "s:rewrite_ruri", &ruri))
+        return NULL;
+
+    memset(&act, '\0', sizeof(act));
+
+    act.type = SET_URI_T;
+    act.val[0].type = STR_ST;
+    act.val[0].u.str.s = ruri;
+    act.val[0].u.str.len = strlen(ruri);
+
+    init_run_actions_ctx(&ra_ctx);
+    if (do_action(&ra_ctx, &act, self->msg) < 0) {
+        LM_ERR("Error in do_action\n");
+        PyErr_SetString(PyExc_RuntimeError, "Error in do_action\n");
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+msg_set_dst_uri(msgobject *self, PyObject *args)
+{
+    str ruri;
+
+    if (self->msg == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    if ((self->msg->first_line).type != SIP_REQUEST) {
+        PyErr_SetString(PyExc_RuntimeError, "Not a request message - "
+          "set destination is not possible.\n");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    if(!PyArg_ParseTuple(args, "s:set_dst_uri", &ruri.s))
+        return NULL;
+
+    ruri.len = strlen(ruri.s);
+
+    if (set_dst_uri(self->msg, &ruri) < 0) {
+        LM_ERR("Error in set_dst_uri\n");
+        PyErr_SetString(PyExc_RuntimeError, "Error in set_dst_uri\n");
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+msg_getHeader(msgobject *self, PyObject *args)
+{
+    struct hdr_field *hf;
+    str hname, *hbody;
+
+    if (self->msg == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    if(!PyArg_ParseTuple(args, "s:getHeader", &hname.s))
+        return NULL;
+    hname.len = strlen(hname.s);
+
+    parse_headers(self->msg, ~0, 0);
+    hbody = NULL;
+    for (hf = self->msg->headers; hf != NULL; hf = hf->next) {
+        if (hname.len == hf->name.len &&
+          strncasecmp(hname.s, hf->name.s, hname.len) == 0) {
+            hbody = &(hf->body);
+            break;
+        }
+    }
+
+    if (hbody == NULL) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    return PyString_FromStringAndSize(hbody->s, hbody->len);
+}
+
+static PyObject *
+msg_call_function(msgobject *self, PyObject *args)
+{
+    int i, rval;
+    char *fname, *arg1, *arg2;
+    union cmd_export_u* fexport;
+    struct action *act;
+    action_u_t elems[MAX_ACTIONS];
+    struct run_act_ctx ra_ctx;
+    unsigned mod_ver;
+
+    if (self->msg == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    i = PySequence_Size(args);
+    if (i < 1 || i > 3) {
+        PyErr_SetString(PyExc_RuntimeError, "call_function() should " \
+          "have from 1 to 3 arguments");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    if(!PyArg_ParseTuple(args, "s|ss:call_function", &fname, &arg1, &arg2))
+        return NULL;
+
+    fexport = find_export_record(fname, i - 2, 0, &mod_ver);
+    if (fexport == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "no such function");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    elems[0].type = MODEXP_ST;
+    elems[0].u.data = fexport;
+    elems[1].type = STRING_ST;
+    elems[1].u.data = arg1;
+    elems[2].type = STRING_ST;
+    elems[2].u.data = arg2;
+    act = mk_action(MODULE_T, 3, elems, 0);
+
+    if (act == NULL) {
+        PyErr_SetString(PyExc_RuntimeError,
+          "action structure could not be created");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    if (fexport->v1.fixup != NULL) {
+        if (i >= 3) {
+            rval = fexport->v1.fixup(&(act->val[2].u.data), 2);
+            if (rval < 0) {
+                PyErr_SetString(PyExc_RuntimeError, "Error in fixup (2)");
+                Py_INCREF(Py_None);
+                return Py_None;
+            }
+            act->val[2].type = MODFIXUP_ST;
+        }
+        if (i >= 2) {
+            rval = fexport->v1.fixup(&(act->val[1].u.data), 1);
+            if (rval < 0) {
+                PyErr_SetString(PyExc_RuntimeError, "Error in fixup (1)");
+                Py_INCREF(Py_None);
+                return Py_None;
+            }
+            act->val[1].type = MODFIXUP_ST;
+        }
+        if (i == 1) {
+            rval = fexport->v1.fixup(0, 0);
+            if (rval < 0) {
+                PyErr_SetString(PyExc_RuntimeError, "Error in fixup (0)");
+                Py_INCREF(Py_None);
+                return Py_None;
+            }
+        }
+    }
+
+    init_run_actions_ctx(&ra_ctx);
+    rval = do_action(&ra_ctx, act, self->msg);
+
+    if ((act->val[2].type == MODFIXUP_ST) && (act->val[2].u.data)) {
+       pkg_free(act->val[2].u.data);
+    }
+
+    if ((act->val[1].type == MODFIXUP_ST) && (act->val[1].u.data)) {
+        pkg_free(act->val[1].u.data);
+    }
+
+    pkg_free(act);
+
+    return PyInt_FromLong(rval);
+}
+
+PyDoc_STRVAR(copy_doc,
+"copy() -> msg object\n\
+\n\
+Return a copy (``clone'') of the msg object.");
+
+static PyMethodDef msg_methods[] = {
+    {"copy",          (PyCFunction)msg_copy,          METH_NOARGS,  copy_doc},
+    {"rewrite_ruri",  (PyCFunction)msg_rewrite_ruri,  METH_VARARGS,
+      "Rewrite Request-URI."},
+    {"set_dst_uri",   (PyCFunction)msg_set_dst_uri,   METH_VARARGS,
+      "Set destination URI."},
+    {"getHeader",     (PyCFunction)msg_getHeader,     METH_VARARGS,
+      "Get SIP header field by name."},
+    {"call_function", (PyCFunction)msg_call_function, METH_VARARGS,
+      "Invoke function exported by the other module."},
+    {NULL, NULL, 0, NULL}                              /* sentinel */
+};
+
+static PyObject *
+msg_getType(msgobject *self, PyObject *unused)
+{
+    const char *rval;
+
+    if (self->msg == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    switch ((self->msg->first_line).type) {
+    case SIP_REQUEST:
+       rval = "SIP_REQUEST";
+       break;
+
+    case SIP_REPLY:
+       rval = "SIP_REPLY";
+       break;
+
+    default:
+       /* Shouldn't happen */
+       abort();
+    }
+    return PyString_FromString(rval);
+}
+
+static PyObject *
+msg_getMethod(msgobject *self, PyObject *unused)
+{
+    str *rval;
+
+    if (self->msg == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    if ((self->msg->first_line).type != SIP_REQUEST) {
+        PyErr_SetString(PyExc_RuntimeError, "Not a request message - "
+          "no method available.\n");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    rval = &((self->msg->first_line).u.request.method);
+    return PyString_FromStringAndSize(rval->s, rval->len);
+}
+
+static PyObject *
+msg_getStatus(msgobject *self, PyObject *unused)
+{
+    str *rval;
+
+    if (self->msg == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    if ((self->msg->first_line).type != SIP_REPLY) {
+        PyErr_SetString(PyExc_RuntimeError, "Not a non-reply message - "
+          "no status available.\n");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    rval = &((self->msg->first_line).u.reply.status);
+    return PyString_FromStringAndSize(rval->s, rval->len);
+}
+
+static PyObject *
+msg_getRURI(msgobject *self, PyObject *unused)
+{
+    str *rval;
+
+    if (self->msg == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    if ((self->msg->first_line).type != SIP_REQUEST) {
+        PyErr_SetString(PyExc_RuntimeError, "Not a request message - "
+          "RURI is not available.\n");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    rval = &((self->msg->first_line).u.request.uri);
+    return PyString_FromStringAndSize(rval->s, rval->len);
+}
+
+static PyObject *
+msg_get_src_address(msgobject *self, PyObject *unused)
+{
+    PyObject *src_ip, *src_port, *pyRval;
+
+    if (self->msg == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    src_ip = PyString_FromString(ip_addr2a(&self->msg->rcv.src_ip));
+    if (src_ip == NULL) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    src_port = PyInt_FromLong(self->msg->rcv.src_port);
+    if (src_port == NULL) {
+        Py_DECREF(src_ip);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    pyRval = PyTuple_Pack(2, src_ip, src_port);
+    Py_DECREF(src_ip);
+    Py_DECREF(src_port);
+    if (pyRval == NULL) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    return pyRval;
+}
+
+static PyObject *
+msg_get_dst_address(msgobject *self, PyObject *unused)
+{
+    PyObject *dst_ip, *dst_port, *pyRval;
+
+    if (self->msg == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "self->msg is NULL");
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    dst_ip = PyString_FromString(ip_addr2a(&self->msg->rcv.dst_ip));
+    if (dst_ip == NULL) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    dst_port = PyInt_FromLong(self->msg->rcv.dst_port);
+    if (dst_port == NULL) {
+        Py_DECREF(dst_ip);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    pyRval = PyTuple_Pack(2, dst_ip, dst_port);
+    Py_DECREF(dst_ip);
+    Py_DECREF(dst_port);
+    if (pyRval == NULL) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    return pyRval;
+}
+
+static PyGetSetDef msg_getseters[] = {
+    {"Type",
+     (getter)msg_getType, NULL, NULL,
+     "Get message type - \"SIP_REQUEST\" or \"SIP_REPLY\"."},
+    {"Method",
+     (getter)msg_getMethod, NULL, NULL,
+     "Get SIP method name."},
+    {"Status",
+     (getter)msg_getStatus, NULL, NULL,
+     "Get SIP status code string."},
+    {"RURI",
+     (getter)msg_getRURI, NULL, NULL,
+     "Get SIP Request-URI."},
+    {"src_address",
+     (getter)msg_get_src_address, NULL, NULL,
+     "Get (IP, port) tuple representing source address of the message."},
+    {"dst_address",
+     (getter)msg_get_dst_address, NULL, NULL,
+     "Get (IP, port) tuple representing destination address of the message."},
+    {NULL, NULL, NULL, NULL, NULL}  /* Sentinel */
+};
+
+static PyTypeObject MSGtype = {
+#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6
+    PyVarObject_HEAD_INIT(NULL, 0)
+#else
+    PyObject_HEAD_INIT(NULL)
+    0,                        /*ob_size*/
+#endif
+    "Router.msg",             /*tp_name*/
+    sizeof(msgobject),        /*tp_size*/
+    0,                        /*tp_itemsize*/
+    /* methods */
+    (destructor)msg_dealloc,  /*tp_dealloc*/
+    0,                        /*tp_print*/
+    0,                        /*tp_getattr*/
+    0,                        /*tp_setattr*/
+    0,                        /*tp_compare*/
+    0,                        /*tp_repr*/
+    0,                        /*tp_as_number*/
+    0,                        /*tp_as_sequence*/
+    0,                        /*tp_as_mapping*/
+    0,                        /*tp_hash*/
+    0,                        /*tp_call*/
+    0,                        /*tp_str*/
+    0,                        /*tp_getattro*/
+    0,                        /*tp_setattro*/
+    0,                        /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT,       /*tp_flags*/
+    0,                        /*tp_doc*/
+    0,                        /*tp_traverse*/
+    0,                        /*tp_clear*/
+    0,                        /*tp_richcompare*/
+    0,                        /*tp_weaklistoffset*/
+    0,                        /*tp_iter*/
+    0,                        /*tp_iternext*/
+    msg_methods,              /*tp_methods*/
+    0,                        /*tp_members*/
+    msg_getseters,            /*tp_getset*/
+};
+
+int
+python_msgobj_init(void)
+{
+
+    Py_TYPE(&MSGtype) = &PyType_Type;
+    if (PyType_Ready(&MSGtype) < 0)
+        return -1;
+    return 0;
+}
diff --git a/modules/app_python/python_msgobj.h b/modules/app_python/python_msgobj.h
new file mode 100644 (file)
index 0000000..9c7387c
--- /dev/null
@@ -0,0 +1,34 @@
+/* $Id$
+ *
+ * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of SIP-Router, a free SIP server.
+ *
+ * SIP-Router 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
+ *
+ * SIP-Router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+*/
+
+#ifndef _PYTHON_MSGOBJ_H
+#define  _PYTHON_MSGOBJ_H
+
+#include "../../parser/msg_parser.h"
+
+#include <Python.h>
+
+PyObject *newmsgobject(struct sip_msg *);
+int python_msgobj_init(void);
+void msg_invalidate(PyObject *);
+
+#endif
diff --git a/modules/app_python/python_support.c b/modules/app_python/python_support.c
new file mode 100644 (file)
index 0000000..9c3cc2e
--- /dev/null
@@ -0,0 +1,82 @@
+/* $Id$
+ *
+ * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of SIP-Router, a free SIP server.
+ *
+ * SIP-Router 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
+ *
+ * SIP-Router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+*/
+
+#include "../../dprint.h"
+#include "python_mod.h"
+
+#include <stdio.h>
+#include <Python.h>
+
+void
+python_handle_exception(const char *fname)
+{
+    PyObject *pResult;
+    const char *msg;
+    PyObject *exception, *v, *tb, *args;
+    PyObject *line;
+    int i;
+
+    LM_ERR("%s: Unhandled exception in the Python code:\n", fname);
+    PyErr_Fetch(&exception, &v, &tb);
+    PyErr_Clear();
+    if (exception == NULL) {
+        LM_ERR("can't get traceback, PyErr_Fetch() has failed\n");
+        return;
+    }
+    PyErr_NormalizeException(&exception, &v, &tb);
+    if (exception == NULL) {
+        LM_ERR("can't get traceback, PyErr_NormalizeException() has failed\n");
+        return;
+    }
+    args = PyTuple_Pack(3, exception, v, tb ? tb : Py_None);
+    Py_XDECREF(exception);
+    Py_XDECREF(v);
+    Py_XDECREF(tb);
+    if (args == NULL) {
+        LM_ERR("can't get traceback, PyTuple_Pack() has failed\n");
+        return;
+    }
+    pResult = PyObject_CallObject(format_exc_obj, args);
+    Py_DECREF(args);
+    if (pResult == NULL) {
+        LM_ERR("can't get traceback, traceback.format_exception() has failed\n");
+        return;
+    }
+    for (i = 0; i < PySequence_Size(pResult); i++) {
+        line = PySequence_GetItem(pResult, i);
+        if (line == NULL) {
+            LM_ERR("can't get traceback, PySequence_GetItem() has failed\n");
+            Py_DECREF(pResult);
+            return;
+        }
+        msg = PyString_AsString(line);
+        if (msg == NULL) {
+            LM_ERR("can't get traceback, PyString_AsString() has failed\n");
+            Py_DECREF(line);
+            Py_DECREF(pResult);
+            return;
+        }
+        LM_ERR("\t%s", msg);
+        Py_DECREF(line);
+    }
+    Py_DECREF(pResult);
+}
diff --git a/modules/app_python/python_support.h b/modules/app_python/python_support.h
new file mode 100644 (file)
index 0000000..24aa525
--- /dev/null
@@ -0,0 +1,28 @@
+/* $Id$
+ *
+ * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of SIP-Router, a free SIP server.
+ *
+ * SIP-Router 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
+ *
+ * SIP-Router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+*/
+
+#ifndef _PYTHON_SUPPORT_H
+#define  _PYTHON_SUPPORT_H
+
+void python_handle_exception(const char *);
+
+#endif