2
0
Эх сурвалжийг харах

app_mono: new module to run managed code embedded

- embeds the mono project interpreter
- allows execution of manged code from config file, such as assemblies
  for C#/.NET, Java, a.s.o.
- can load the assembly at startup or read it every time before
  execution
Daniel-Constantin Mierla 13 жил өмнө
parent
commit
fa893fb17b

+ 22 - 0
modules/app_mono/Makefile

@@ -0,0 +1,22 @@
+#
+#
+
+include ../../Makefile.defs
+auto_gen=
+NAME=app_mono.so
+
+BUILDER = $(shell which pkg-config)
+ifeq ($(BUILDER),)
+	DEFS+=-D_REENTRANT -I/usr/include/mono-2.0
+	LIBS= -lmono-2.0 -lm -lrt -ldl -lpthread
+else
+	DEFS+= $(shell pkg-config --cflags mono-2)
+	LIBS = $(shell pkg-config --libs mono-2)
+endif
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+
+include ../../Makefile.modules

+ 218 - 0
modules/app_mono/README

@@ -0,0 +1,218 @@
+app_mono Module
+
+Daniel-Constantin Mierla
+
+   asipto.com
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2012 Daniel-Constantin Mierla (asipto.com)
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Parameters
+
+              3.1. load (string)
+
+        4. Functions
+
+              4.1. mono_exec(path [, param])
+              4.2. mono_run([param])
+
+        5. Usage
+
+   List of Examples
+
+   1.1. Set load parameter
+   1.2. lua_dofile usage
+   1.3. lua_run usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Parameters
+
+        3.1. load (string)
+
+   4. Functions
+
+        4.1. mono_exec(path [, param])
+        4.2. mono_run([param])
+
+   5. Usage
+
+1. Overview
+
+   This module allows executing assemblies of managed code, among most
+   popular being C# (.NET). It uses Mono project
+   (http://www.mono-project.com/) to embed the managed code interpreter
+   inside the SIP server, for a fast execution.
+
+   Besides C#, other languages can be used to build managed assemblies,
+   such as: Java, Python, VisualBasic.NET, JavaScript. For more details of
+   what kind of languages can be used to build managed assemblies see:
+   http://www.mono-project.com/Languages .
+
+   A managed assembly can get access to any of Kamailio config variables
+   and set them, as well as many other functions implemented inside
+   Kamailio itself, to make the handling of SIP easier from managed code.
+
+   There are two ways to execute managed code assemlies: load the code at
+   startup and only execute at runtime or load and execute at runtime. It
+   can run in one mode only at a time, a matter of 'load' parameter and
+   used function to execute the code.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * none.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * mono-devel - Mono devel toolkit.
+
+3. Parameters
+
+   3.1. load (string)
+
+3.1. load (string)
+
+   Set the path to the Mono assembly to be loaded at startup. Then you can
+   use mono_run(param) to execute the assembly at runtime.
+
+   Default value is “null”.
+
+   Example 1.1. Set load parameter
+...
+modparam("app_mono", "load", "/usr/local/etc/kamailio/mono/myscript.exe")
+...
+
+4. Functions
+
+   4.1. mono_exec(path [, param])
+   4.2. mono_run([param])
+
+4.1.  mono_exec(path [, param])
+
+   Execute the managed code assembly stored in 'path'. The path can be a
+   string with pseudo-variables evaluated at runtime. A second parameter
+   can be optionally gived and passed over to the assembly.
+
+   Note that the assembly is loaded every time from the file, so any
+   change to it is immediately visible. This function cannot be used if
+   'load' parameter is set.
+
+   Example 1.2. lua_dofile usage
+...
+mono_exec("/usr/local/etc/kamailio/mono/myscript.exe");
+...
+
+4.2.  mono_run([param])
+
+   Execute the assembly specified by 'load' module parameter. The assembly
+   is loaded at startup, changes to it will be effective after Kamailio
+   restart.
+
+   An optional paraeter can be given and it will be passed over to the
+   assembly. It can be a string with pseudo-variables that are evaluated
+   at runtime.
+
+   Example 1.3. lua_run usage
+...
+if(!mono_run("myparam"))
+{
+    xdbg("SCRIPT: failed to execute mono assembly!\n");
+}
+...
+
+5. Usage
+
+   First create a library from the 'SR.cs' file provided in the folder
+   'modules/app_mono/lib/'. The examples uses the folder
+   /usr/local/etc/kamailio/mono/ to store the Mono specific assemblies to
+   be used by Kamailio.
+...
+mkdir -p /usr/local/etc/kamailio/mono/
+cp modules/app_mono/lib/SR.cs /usr/local/etc/kamailio/mono/
+gmcs -t:library SR.cs
+...
+
+   You should see 'SR.dll' file generated.
+
+   Create your application in C#/.NET and save it in the same folder with
+   SR.dll. You have to use SR package in your managed source code file --
+   say you named MySRTest.cs. Also, be aware that the Main() function from
+   the managed assembly is executed, thus be sure you have it defined.
+...
+using SR;
+
+namespace MySRTest {
+        class Test {
+                static int Main(string[] args) {
+                        SR.Core.Log(1, "Kamailio API Version: " + SR.Core.APIVer
+sion() + "\n");
+                        if (args.Length == 1) {
+                                SR.Core.Log(1, "Parameter from Kamailio config:
+"
+                                        + args[0] + "\n");
+                        }
+                        SR.Core.Dbg("Request URI is: " + SR.PV.GetS("$ru") + "\n
+");
+                        SR.PV.SetS("$rU", "123");
+                        SR.HDR.AppendToReply("My-Mono-Hdr: ok\r\n");
+                        SR.Core.ModF("sl_reply_error");
+                        return 1;
+                }
+        }
+}
+...
+
+   You have to compile the 'SR.dll' file generated.
+...
+$ gmcs -r:SR.dll MySRTest.cs
+...
+
+   You should see the file 'MySRTest.exe' generated in the folder.
+
+   In the config file of Kamailio, load app_mono.so module and use its
+   functions inside the routing blocks.
+...
+loadmodule "app_mono.so"
+...
+route {
+    ...
+    mono_exec("/usr/local/etc/kamailio/mono/MySRTest.exe");
+    ...
+}
+...
+
+   The API exported by SR package to Mono applications is documented on
+   Kamailio wiki site. Also, it is easy to figure out by looking in the
+   source code tree inside the file modules/app_mono/lib/SR.cs.

+ 945 - 0
modules/app_mono/app_mono_api.c

@@ -0,0 +1,945 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2011 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../mem/mem.h"
+#include "../../data_lump.h"
+#include "../../data_lump_rpl.h"
+#include "../../lib/kcore/cmpapi.h"
+
+#include "app_mono_api.h"
+
+#include <mono/metadata/mono-config.h>
+
+#define SRVERSION "1.0"
+
+/**
+ *
+ */
+typedef struct _sr_mono_load
+{
+	char *script;
+	MonoDomain *domain;
+	MonoAssembly *assembly;
+	struct _sr_mono_load *next;
+} sr_mono_load_t;
+
+static sr_mono_load_t *_sr_mono_load_list = NULL;
+
+int sr_mono_load_class_core();
+int sr_mono_load_class_pv();
+int sr_mono_load_class_hdr();
+
+/**
+ *
+ */
+static sr_mono_env_t _sr_M_env;
+
+/**
+ * @return the static Mono env
+ */
+sr_mono_env_t *sr_mono_env_get(void)
+{
+	return &_sr_M_env;
+}
+
+/**
+ *
+ */
+int sr_mono_load_script(char *script)
+{
+	sr_mono_load_t *mi;
+
+	if(_sr_mono_load_list != NULL)
+	{
+		LM_ERR("only one assembly can be loaded\n");
+		return -1;
+	}
+	mi = (sr_mono_load_t*)pkg_malloc(sizeof(sr_mono_load_t));
+	if(mi==NULL)
+	{
+		LM_ERR("no more pkg\n");
+		return -1;
+	}
+	memset(mi, 0, sizeof(sr_mono_load_t));
+	mi->script = script;
+	mi->next = _sr_mono_load_list;
+	_sr_mono_load_list = mi;
+	return 0;
+}
+
+/**
+ *
+ */
+int sr_mono_assembly_loaded(void)
+{
+	if(_sr_mono_load_list != NULL)
+		return 1;
+	return 0;
+}
+
+/**
+ *
+ */
+int sr_mono_register_module(char *mname)
+{
+//	if(mono_sr_exp_register_mod(mname)==0)
+//		return 0;
+	return -1;
+}
+
+/**
+ *
+ */
+int mono_sr_init_mod(void)
+{
+	memset(&_sr_M_env, 0, sizeof(sr_mono_env_t));
+//	if(mono_sr_exp_init_mod()<0)
+//		return -1;
+	return 0;
+}
+
+/**
+ *
+ */
+int mono_sr_init_load(void)
+{
+	sr_mono_load_t *mi;
+	if(_sr_mono_load_list == NULL) {
+		LM_DBG("no assembly to load\n");
+		return 0;
+	}
+	mono_config_parse (NULL);
+	mi = _sr_mono_load_list;
+	if(mi->domain != NULL)
+	{
+		LM_ERR("worker mono environment already initialized\n");
+		return 0;
+	}
+	while(mi!=NULL)
+	{
+		mi->domain = mono_jit_init (mi->script);
+		if (mi->domain==NULL) {
+			LM_ERR("failed to init domain for: %s\n", mi->script);
+			return -1;
+		}
+		sr_mono_load_class_core();
+		sr_mono_load_class_pv();
+		sr_mono_load_class_hdr();
+
+		mi->assembly = mono_domain_assembly_open(mi->domain, mi->script);
+		if (mi->assembly==NULL) {
+			LM_ERR("failed to open assembly: %s\n", mi->script);
+			return -1;
+		}
+		mi = mi->next;
+		/* only one (first) assembly for now */
+		break;
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+int mono_sr_init_probe(void)
+{
+	/* todo: test if assemblies exist */
+	return 0;
+}
+
+/**
+ *
+ */
+int mono_sr_init_child(void)
+{
+	memset(&_sr_M_env, 0, sizeof(sr_mono_env_t));
+
+	/*
+	 * Load the default Mono configuration file, this is needed
+	 * if you are planning on using the dllmaps defined on the
+	 * system configuration
+	 */
+	mono_config_parse (NULL);
+
+	return 0;
+}
+
+/**
+ *
+ */
+void mono_sr_destroy(void)
+{
+	memset(&_sr_M_env, 0, sizeof(sr_mono_env_t));
+}
+
+/**
+ *
+ */
+int mono_sr_initialized(void)
+{
+	if(_sr_M_env.domain==NULL)
+		return 1;
+
+	return 1;
+}
+
+/**
+ *
+ */
+int app_mono_exec(struct sip_msg *msg, char *script, char *param)
+{
+	int ret;
+	char *argv[2];
+	int argc;
+
+	argc = 1;
+	argv[0] = script;
+	if(param!=NULL) {
+		argc++;
+		argv[1] = param;
+	}
+	LM_DBG("executing Mono assembly: [[%s]]\n", argv[0]);
+	_sr_M_env.msg = msg;
+
+	mono_config_parse (NULL);
+	/*
+	 * mono_jit_init() creates a domain: each assembly is
+	 * loaded and run in a MonoDomain.
+	 */
+	_sr_M_env.domain = mono_jit_init (argv[0]);
+	/*
+	 * We add our special internal functions, so that C# code
+	 * can call us back.
+	 */
+	sr_mono_load_class_core();
+	sr_mono_load_class_pv();
+	sr_mono_load_class_hdr();
+
+	_sr_M_env.assembly = mono_domain_assembly_open(_sr_M_env.domain, argv[0]);
+	if (_sr_M_env.assembly==NULL) {
+		ret = -1;
+		goto done;
+	}
+	/*
+	 * mono_jit_exec() will run the Main() method in the assembly.
+	 * The return value needs to be looked up from
+	 * System.Environment.ExitCode.
+	 */
+	mono_jit_exec(_sr_M_env.domain, _sr_M_env.assembly, argc, argv);
+	ret = mono_environment_exitcode_get();
+	LM_DBG("returned code from mono environment: %d\n", ret);
+
+done:
+	mono_jit_cleanup(_sr_M_env.domain);
+
+	memset(&_sr_M_env, 0, sizeof(sr_mono_env_t));
+	return (ret==0)?1:-1;
+}
+
+/**
+ *
+ */
+int app_mono_run(struct sip_msg *msg, char *arg)
+{
+	int ret;
+	char *argv[2];
+	int argc;
+	sr_mono_load_t *mi;
+
+	if(_sr_mono_load_list == NULL)
+		return -1;
+	mi = _sr_mono_load_list;
+
+	LM_DBG("running Mono assembly: [[%s]]\n", mi->script);
+	_sr_M_env.msg = msg;
+
+	_sr_M_env.domain = mi->domain;
+	_sr_M_env.assembly = mi->assembly;
+	if (_sr_M_env.assembly==NULL) {
+		LM_DBG("empty assembly\n");
+		memset(&_sr_M_env, 0, sizeof(sr_mono_env_t));
+		return -1;
+	}
+	mono_domain_set(_sr_M_env.domain, 0);
+	argc = 1;
+	argv[0] = mi->script;
+	if(arg!=NULL) {
+		argc++;
+		argv[1] = arg;
+	}
+	mono_jit_exec(_sr_M_env.domain, _sr_M_env.assembly, argc, argv);
+	ret = mono_environment_exitcode_get();
+	LM_DBG("returned code from mono environment: %d\n", ret);
+
+	memset(&_sr_M_env, 0, sizeof(sr_mono_env_t));
+	return (ret==0)?1:-1;
+}
+
+
+/**
+ *
+ */
+static MonoString* sr_mono_api_version() {
+	return mono_string_new (mono_domain_get(), SRVERSION);
+}
+
+static void sr_mono_log(int level, MonoString *text) {
+	char *logmsg;
+	logmsg = mono_string_to_utf8(text);
+	LOG(level, "%s", logmsg);
+	mono_free(logmsg);
+}
+
+static void sr_mono_err(MonoString *text) {
+	char *logmsg;
+	logmsg = mono_string_to_utf8(text);
+	LOG(L_ERR, "%s", logmsg);
+	mono_free(logmsg);
+}
+
+static void sr_mono_dbg(MonoString *text) {
+	char *logmsg;
+	logmsg = mono_string_to_utf8(text);
+	LOG(L_DBG, "%s", logmsg);
+	mono_free(logmsg);
+}
+
+/**
+ *
+ */
+static int sr_mono_modf(MonoString *nfunc)
+{
+	int ret;
+	int mod_type;
+	struct run_act_ctx ra_ctx;
+	unsigned modver;
+	struct action *act = NULL;
+	sr31_cmd_export_t* expf;
+	sr_mono_env_t *env_M;
+	char *func = NULL;
+
+	env_M = sr_mono_env_get();
+	if(env_M->msg==NULL)
+		goto error;
+
+	func = mono_string_to_utf8(nfunc);
+
+	expf = find_export_record(func, 0, 0, &modver);
+	if (expf==NULL) {
+		LM_ERR("function '%s' is not available\n", func);
+		goto error;
+	}
+	/* check fixups */
+	if (expf->fixup!=NULL && expf->free_fixup==NULL) {
+		LM_ERR("function '%s' has fixup - cannot be used\n", func);
+		goto error;
+	}
+	mod_type = MODULE0_T;
+
+	act = mk_action(mod_type,  1        /* number of (type, value) pairs */,
+					MODEXP_ST, expf,    /* function */
+					NUMBER_ST, 0,       /* parameter number */
+					STRING_ST, NULL,    /* param. 1 */
+					STRING_ST, NULL,    /* param. 2 */
+					STRING_ST, NULL,    /* param. 3 */
+					STRING_ST, NULL,    /* param. 4 */
+					STRING_ST, NULL,    /* param. 5 */
+					STRING_ST, NULL     /* param. 6 */
+			);
+
+	if (act==NULL) {
+		LM_ERR("action structure could not be created for '%s'\n", func);
+		goto error;
+	}
+
+	/* handle fixups */
+	if (expf->fixup) {
+		/* no parameters */
+		if(expf->fixup(0, 0)<0)
+		{
+			LM_ERR("Error in fixup (0) for '%s'\n", func);
+			goto error;
+		}
+	}
+	init_run_actions_ctx(&ra_ctx);
+	ret = do_action(&ra_ctx, act, env_M->msg);
+	pkg_free(act);
+	mono_free(func);
+	return ret;
+
+error:
+	if(func!=NULL)
+		mono_free(func);
+	if(act!=NULL)
+		pkg_free(act);
+	return -127;
+}
+
+
+static const sr_M_export_t _sr_M_export_core[] = {
+	{"SR.Core::APIVersion", sr_mono_api_version},
+	{"SR.Core::Log",        sr_mono_log},
+	{"SR.Core::Err",        sr_mono_err},
+	{"SR.Core::Dbg",        sr_mono_dbg},
+	{"SR.Core::ModF",       sr_mono_modf},
+	{NULL, NULL}
+};
+
+int sr_mono_load_class_core()
+{
+	int i;
+	for(i=0; _sr_M_export_core[i].name!=NULL; i++)
+		mono_add_internal_call(_sr_M_export_core[i].name, _sr_M_export_core[i].method);
+	return 0;
+}
+
+/**
+ *
+ */
+static MonoString *sr_mono_pv_gets (MonoString *pv)
+{
+	str pvn = {0};
+	pv_spec_t *pvs;
+    pv_value_t val;
+	sr_mono_env_t *env_M;
+	int pl;
+
+	env_M = sr_mono_env_get();
+
+	pvn.s = mono_string_to_utf8(pv);
+	if(pvn.s==NULL || env_M->msg==NULL)
+		goto error;
+
+	pvn.len = strlen(pvn.s);
+	LM_DBG("pv get: %s\n", pvn.s);
+	pl = pv_locate_name(&pvn);
+	if(pl != pvn.len)
+	{
+		LM_ERR("invalid pv [%s] (%d/%d)\n", pvn.s, pl, pvn.len);
+		goto error;
+	}
+	pvs = pv_cache_get(&pvn);
+	if(pvs==NULL)
+	{
+		LM_ERR("cannot get pv spec for [%s]\n", pvn.s);
+		goto error;
+	}
+	memset(&val, 0, sizeof(pv_value_t));
+	if(pv_get_spec_value(env_M->msg, pvs, &val) != 0)
+	{
+		LM_ERR("unable to get pv value for [%s]\n", pvn.s);
+		goto error;
+	}
+	if((val.flags&PV_VAL_NULL) || !(val.flags&PV_VAL_STR))
+	{
+		mono_free(pvn.s);
+		return NULL;
+	}
+	mono_free(pvn.s);
+	return mono_string_new_len (mono_domain_get(), val.rs.s, val.rs.len);
+
+error:
+	if(pvn.s!=NULL)
+		mono_free(pvn.s);
+	return NULL;
+}
+
+/**
+ *
+ */
+static int sr_mono_pv_geti (MonoString *pv)
+{
+	str pvn = {0};
+	pv_spec_t *pvs;
+    pv_value_t val;
+	int pl;
+	sr_mono_env_t *env_M;
+
+	env_M = sr_mono_env_get();
+	pvn.s = mono_string_to_utf8(pv);
+
+	if(pvn.s==NULL || env_M->msg==NULL)
+		goto error;
+
+	pvn.len = strlen(pvn.s);
+	LM_DBG("pv get: %s\n", pvn.s);
+	pl = pv_locate_name(&pvn);
+	if(pl != pvn.len)
+	{
+		LM_ERR("invalid pv [%s] (%d/%d)\n", pvn.s, pl, pvn.len);
+		goto error;
+	}
+	pvs = pv_cache_get(&pvn);
+	if(pvs==NULL)
+	{
+		LM_ERR("cannot get pv spec for [%s]\n", pvn.s);
+		goto error;
+	}
+	memset(&val, 0, sizeof(pv_value_t));
+	if(pv_get_spec_value(env_M->msg, pvs, &val) != 0)
+	{
+		LM_ERR("unable to get pv value for [%s]\n", pvn.s);
+		goto error;
+	}
+	if((val.flags&PV_VAL_NULL) || !(val.flags&PV_TYPE_INT))
+	{
+		mono_free(pvn.s);
+		return 0;
+	}
+	mono_free(pvn.s);
+	return val.ri;
+
+error:
+	if(pvn.s!=NULL)
+		mono_free(pvn.s);
+	return 0;
+}
+
+
+/**
+ *
+ */
+static int sr_mono_pv_seti (MonoString *pv, int iv)
+{
+	str pvn = {0};
+	pv_spec_t *pvs;
+    pv_value_t val;
+	int pl;
+	sr_mono_env_t *env_M;
+
+	env_M = sr_mono_env_get();
+	pvn.s = mono_string_to_utf8(pv);
+
+	if(pvn.s==NULL || env_M->msg==NULL)
+		goto error;
+
+	memset(&val, 0, sizeof(pv_value_t));
+	val.ri = iv;
+	val.flags |= PV_TYPE_INT|PV_VAL_INT;
+	
+	pvn.len = strlen(pvn.s);
+	LM_DBG("pv set: %s\n", pvn.s);
+	pl = pv_locate_name(&pvn);
+	if(pl != pvn.len)
+	{
+		LM_ERR("invalid pv [%s] (%d/%d)\n", pvn.s, pl, pvn.len);
+		goto error;
+	}
+	pvs = pv_cache_get(&pvn);
+	if(pvs==NULL)
+	{
+		LM_ERR("cannot get pv spec for [%s]\n", pvn.s);
+		goto error;
+	}
+	if(pv_set_spec_value(env_M->msg, pvs, 0, &val)<0)
+	{
+		LM_ERR("unable to set pv [%s]\n", pvn.s);
+		goto error;
+	}
+
+	mono_free(pvn.s);
+	return 0;
+error:
+	if(pvn.s!=NULL)
+		mono_free(pvn.s);
+	return -1;
+}
+
+/**
+ *
+ */
+static int sr_mono_pv_sets (MonoString *pv, MonoString *sv)
+{
+	str pvn = {0};
+	pv_spec_t *pvs;
+    pv_value_t val;
+	int pl;
+	sr_mono_env_t *env_M;
+
+	env_M = sr_mono_env_get();
+	pvn.s = mono_string_to_utf8(pv);
+
+	if(pvn.s==NULL || env_M->msg==NULL)
+		goto error;
+
+	memset(&val, 0, sizeof(pv_value_t));
+	val.rs.s = mono_string_to_utf8(sv);
+	if(val.rs.s == NULL)
+		goto error;
+
+	val.rs.len = strlen(val.rs.s);
+	val.flags |= PV_VAL_STR;
+	
+	pvn.len = strlen(pvn.s);
+	LM_DBG("pv set: %s\n", pvn.s);
+	pl = pv_locate_name(&pvn);
+	if(pl != pvn.len)
+	{
+		LM_ERR("invalid pv [%s] (%d/%d)\n", pvn.s, pl, pvn.len);
+		goto error;
+	}
+	pvs = pv_cache_get(&pvn);
+	if(pvs==NULL)
+	{
+		LM_ERR("cannot get pv spec for [%s]\n", pvn.s);
+		goto error;
+	}
+	if(pv_set_spec_value(env_M->msg, pvs, 0, &val)<0)
+	{
+		LM_ERR("unable to set pv [%s]\n", pvn.s);
+		return -1;
+	}
+
+	mono_free(pvn.s);
+	return 0;
+error:
+	if(pvn.s!=NULL)
+		mono_free(pvn.s);
+	return -1;
+}
+
+/**
+ *
+ */
+static int sr_mono_pv_unset (MonoString *pv)
+{
+	str pvn = {0};
+	pv_spec_t *pvs;
+    pv_value_t val;
+	int pl;
+	sr_mono_env_t *env_M;
+
+	env_M = sr_mono_env_get();
+	pvn.s = mono_string_to_utf8(pv);
+
+	if(pvn.s==NULL || env_M->msg==NULL)
+		goto error;
+
+	pvn.len = strlen(pvn.s);
+	LM_DBG("pv unset: %s\n", pvn.s);
+	pl = pv_locate_name(&pvn);
+	if(pl != pvn.len)
+	{
+		LM_ERR("invalid pv [%s] (%d/%d)\n", pvn.s, pl, pvn.len);
+		goto error;
+	}
+	pvs = pv_cache_get(&pvn);
+	if(pvs==NULL)
+	{
+		LM_ERR("cannot get pv spec for [%s]\n", pvn.s);
+		goto error;
+	}
+	memset(&val, 0, sizeof(pv_value_t));
+	val.flags |= PV_VAL_NULL;
+	if(pv_set_spec_value(env_M->msg, pvs, 0, &val)<0)
+	{
+		LM_ERR("unable to unset pv [%s]\n", pvn.s);
+		goto error;
+	}
+
+	mono_free(pvn.s);
+	return 0;
+error:
+	if(pvn.s!=NULL)
+		mono_free(pvn.s);
+	return -1;
+}
+
+/**
+ *
+ */
+static int sr_mono_pv_is_null (MonoString *pv)
+{
+	str pvn = {0};
+	pv_spec_t *pvs;
+    pv_value_t val;
+	int pl;
+	sr_mono_env_t *env_M;
+
+	env_M = sr_mono_env_get();
+	pvn.s = mono_string_to_utf8(pv);
+
+	if(pvn.s==NULL || env_M->msg==NULL)
+		goto error;
+
+	pvn.len = strlen(pvn.s);
+	LM_DBG("pv is null test: %s\n", pvn.s);
+	pl = pv_locate_name(&pvn);
+	if(pl != pvn.len)
+	{
+		LM_ERR("invalid pv [%s] (%d/%d)\n", pvn.s, pl, pvn.len);
+		goto error;
+	}
+	pvs = pv_cache_get(&pvn);
+	if(pvs==NULL)
+	{
+		LM_ERR("cannot get pv spec for [%s]\n", pvn.s);
+		goto error;
+	}
+	memset(&val, 0, sizeof(pv_value_t));
+	if(pv_get_spec_value(env_M->msg, pvs, &val) != 0)
+	{
+		LM_INFO("unable to get pv value for [%s]\n", pvn.s);
+		goto error;
+	}
+	mono_free(pvn.s);
+	if(val.flags&PV_VAL_NULL)
+	{
+		return 1;
+	}
+	return 0;
+error:
+	if(pvn.s!=NULL)
+		mono_free(pvn.s);
+	return -1;
+}
+
+/**
+ *
+ */
+static const sr_M_export_t _sr_M_export_pv[] = {
+	{"SR.PV::GetS",      sr_mono_pv_gets},
+	{"SR.PV::GetI",      sr_mono_pv_geti},
+	{"SR.PV::SetI",      sr_mono_pv_seti},
+	{"SR.PV::SetS",      sr_mono_pv_sets},
+	{"SR.PV::Unset",     sr_mono_pv_unset},
+	{"SR.PV::IsNull",    sr_mono_pv_is_null},
+	{NULL, NULL}
+};
+
+int sr_mono_load_class_pv()
+{
+	int i;
+	for(i=0; _sr_M_export_pv[i].name!=NULL; i++)
+		mono_add_internal_call(_sr_M_export_pv[i].name, _sr_M_export_pv[i].method);
+	return 0;
+}
+
+
+/**
+ *
+ */
+static int sr_mono_hdr_append (MonoString *hv)
+{
+	struct lump* anchor;
+	struct hdr_field *hf;
+	char *hdr;
+	str txt = {0};
+	sr_mono_env_t *env_M;
+
+	env_M = sr_mono_env_get();
+	txt.s = mono_string_to_utf8(hv);
+
+	if(txt.s==NULL || env_M->msg==NULL)
+		goto error;
+
+	txt.len = strlen(txt.s);
+
+	LM_DBG("append hf: %s\n", txt.s);
+	if (parse_headers(env_M->msg, HDR_EOH_F, 0) == -1)
+	{
+		LM_ERR("error while parsing message\n");
+		goto error;
+	}
+
+	hf = env_M->msg->last_header;
+	hdr = (char*)pkg_malloc(txt.len);
+	if(hdr==NULL)
+	{
+		LM_ERR("no pkg memory left\n");
+		goto error;
+	}
+	memcpy(hdr, txt.s, txt.len);
+	anchor = anchor_lump(env_M->msg,
+				hf->name.s + hf->len - env_M->msg->buf, 0, 0);
+	if(insert_new_lump_before(anchor, hdr, txt.len, 0) == 0)
+	{
+		LM_ERR("can't insert lump\n");
+		pkg_free(hdr);
+		goto error;
+	}
+	mono_free(txt.s);
+	return 0;
+
+error:
+	if(txt.s!=NULL)
+		mono_free(txt.s);
+	return -1;
+}
+
+/**
+ *
+ */
+static int sr_mono_hdr_remove (MonoString *hv)
+{
+	struct lump* anchor;
+	struct hdr_field *hf;
+	str hname;
+	str txt = {0};
+	sr_mono_env_t *env_M;
+
+	env_M = sr_mono_env_get();
+	txt.s = mono_string_to_utf8(hv);
+
+	if(txt.s==NULL || env_M->msg==NULL)
+		goto error;
+
+	txt.len = strlen(txt.s);
+
+	LM_DBG("remove hf: %s\n", txt.s);
+	if (parse_headers(env_M->msg, HDR_EOH_F, 0) == -1) {
+		LM_ERR("error while parsing message\n");
+		goto error;
+	}
+
+	hname.s = txt.s;
+	hname.len = txt.len;
+	for (hf=env_M->msg->headers; hf; hf=hf->next)
+	{
+		if (cmp_hdrname_str(&hf->name, &hname)==0)
+		{
+			anchor=del_lump(env_M->msg,
+					hf->name.s - env_M->msg->buf, hf->len, 0);
+			if (anchor==0)
+			{
+				LM_ERR("cannot remove hdr %s\n", txt.s);
+				goto error;
+			}
+		}
+	}
+	mono_free(txt.s);
+	return 0;
+
+error:
+	if(txt.s!=NULL)
+		mono_free(txt.s);
+	return -1;
+}
+
+/**
+ *
+ */
+static int sr_mono_hdr_insert (MonoString *hv)
+{
+	struct lump* anchor;
+	struct hdr_field *hf;
+	char *hdr;
+	str txt = {0};
+	sr_mono_env_t *env_M;
+
+	env_M = sr_mono_env_get();
+	txt.s = mono_string_to_utf8(hv);
+
+	if(txt.s==NULL || env_M->msg==NULL)
+		goto error;
+
+	txt.len = strlen(txt.s);
+
+	LM_DBG("insert hf: %s\n", txt.s);
+	hf = env_M->msg->headers;
+	hdr = (char*)pkg_malloc(txt.len);
+	if(hdr==NULL)
+	{
+		LM_ERR("no pkg memory left\n");
+		goto error;
+	}
+	memcpy(hdr, txt.s, txt.len);
+	anchor = anchor_lump(env_M->msg,
+				hf->name.s + hf->len - env_M->msg->buf, 0, 0);
+	if(insert_new_lump_before(anchor, hdr, txt.len, 0) == 0)
+	{
+		LM_ERR("can't insert lump\n");
+		pkg_free(hdr);
+		goto error;
+	}
+	mono_free(txt.s);
+	return 0;
+
+error:
+	if(txt.s!=NULL)
+		mono_free(txt.s);
+	return -1;
+}
+
+/**
+ *
+ */
+static int sr_mono_hdr_append_to_reply (MonoString *hv)
+{
+	str txt = {0};
+	sr_mono_env_t *env_M;
+
+	env_M = sr_mono_env_get();
+	txt.s = mono_string_to_utf8(hv);
+
+	if(txt.s==NULL || env_M->msg==NULL)
+		goto error;
+
+	txt.len = strlen(txt.s);
+
+	LM_DBG("append to reply: %s\n", txt.s);
+
+	if(add_lump_rpl(env_M->msg, txt.s, txt.len, LUMP_RPL_HDR)==0)
+	{
+		LM_ERR("unable to add reply lump\n");
+		goto error;
+	}
+
+	mono_free(txt.s);
+	return 0;
+
+error:
+	if(txt.s!=NULL)
+		mono_free(txt.s);
+	return -1;
+}
+
+
+/**
+ *
+ */
+static const sr_M_export_t _sr_M_export_hdr[] = {
+	{"SR.HDR::Append", sr_mono_hdr_append},
+	{"SR.HDR::Remove", sr_mono_hdr_remove},
+	{"SR.HDR::Insert", sr_mono_hdr_insert},
+	{"SR.HDR::AppendToReply", sr_mono_hdr_append_to_reply},
+	{NULL, NULL}
+};
+
+int sr_mono_load_class_hdr()
+{
+	int i;
+	for(i=0; _sr_M_export_hdr[i].name!=NULL; i++)
+		mono_add_internal_call(_sr_M_export_hdr[i].name, _sr_M_export_hdr[i].method);
+	return 0;
+}
+
+

+ 63 - 0
modules/app_mono/app_mono_api.h

@@ -0,0 +1,63 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2011 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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 _APP_MONO_API_H_
+#define _APP_MONO_API_H_
+
+#include <mono/jit/jit.h>
+#include <mono/metadata/environment.h>
+#include <stdlib.h>
+
+#include "../../parser/msg_parser.h"
+
+typedef struct _sr_mono_env
+{
+	MonoDomain *domain;
+	MonoAssembly *assembly;
+	sip_msg_t *msg;
+	unsigned int flags;
+} sr_mono_env_t;
+
+sr_mono_env_t *sr_mono_env_get(void);
+
+int mono_sr_initialized(void);
+int mono_sr_init_mod(void);
+int mono_sr_init_child(void);
+int mono_sr_init_load(void);
+void mono_sr_destroy(void);
+int mono_sr_init_probe(void);
+int sr_mono_assembly_loaded(void);
+
+int sr_mono_load_script(char *script);
+int sr_mono_register_module(char *mname);
+
+int app_mono_exec(struct sip_msg *msg, char *script, char *param);
+int app_mono_run(sip_msg_t *msg, char *arg);
+
+typedef struct sr_M_export {
+	char *name;
+	void* method;
+} sr_M_export_t;
+
+#endif
+

+ 223 - 0
modules/app_mono/app_mono_mod.c

@@ -0,0 +1,223 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2011 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../mod_fix.h"
+
+#include "app_mono_api.h"
+
+MODULE_VERSION
+
+/** parameters */
+
+/* List of allowed chars for a prefix*/
+static int  mod_init(void);
+static void mod_destroy(void);
+static int  child_init(int rank);
+
+static int w_app_mono_exec(struct sip_msg *msg, char *script, char *mparam);
+static int w_app_mono_run(struct sip_msg *msg, char *mparam, char *extra);
+
+static int fixup_mono_exec(void** param, int param_no);
+
+int app_mono_load_param(modparam_t type, void *val);
+int app_mono_register_param(modparam_t type, void *val);
+
+static param_export_t params[]={
+	{"load",     STR_PARAM|USE_FUNC_PARAM, (void*)app_mono_load_param},
+	{"register", STR_PARAM|USE_FUNC_PARAM, (void*)app_mono_register_param},
+	{0, 0, 0}
+};
+
+static cmd_export_t cmds[]={
+	{"mono_exec", (cmd_function)w_app_mono_exec, 1, fixup_mono_exec,
+		0, ANY_ROUTE},
+	{"mono_exec", (cmd_function)w_app_mono_exec, 2, fixup_mono_exec,
+		0, ANY_ROUTE},
+	{"mono_run",  (cmd_function)w_app_mono_run,  0, 0,
+		0, ANY_ROUTE},
+	{"mono_run", (cmd_function)w_app_mono_run,   1, fixup_spve_null,
+		0, ANY_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+struct module_exports exports = {
+	"app_mono",
+	RTLD_NOW | RTLD_GLOBAL, /* dlopen flags */
+	cmds,
+	params,
+	0,
+	0,              /* exported MI functions */
+	0,              /* exported pseudo-variables */
+	0,              /* extra processes */
+	mod_init,       /* module initialization function */
+	0,              /* response function */
+	mod_destroy,    /* destroy function */
+	child_init      /* per child init function */
+};
+
+
+
+/**
+ * init module function
+ */
+static int mod_init(void)
+{
+	mono_sr_init_mod();
+	return 0;
+}
+
+
+/* each child get a new connection to the database */
+static int child_init(int rank)
+{
+	if(rank==PROC_MAIN || rank==PROC_TCP_MAIN)
+		return 0; /* do nothing for the main process */
+
+	if (rank==PROC_INIT)
+	{
+		/* do a probe before forking */
+		if(mono_sr_init_probe()!=0)
+			return -1;
+		return 0;
+	}
+	if(mono_sr_init_child()<0)
+		return -1;
+	if(mono_sr_init_load()<0)
+		return -1;
+	return 0;
+}
+
+
+static void mod_destroy(void)
+{
+	mono_sr_destroy();
+}
+
+char _mono_buf_stack[2][512];
+
+/**
+ *
+ */
+static int w_app_mono_exec(struct sip_msg *msg, char *script, char *mparam)
+{
+	str s;
+	str p;
+
+	if(!mono_sr_initialized())
+	{
+		LM_ERR("Lua env not intitialized");
+		return -1;
+	}
+	if(fixup_get_svalue(msg, (gparam_p)script, &s)<0)
+	{
+		LM_ERR("cannot get the script\n");
+		return -1;
+	}
+	if(s.len>=511)
+	{
+		LM_ERR("script too long %d\n", s.len);
+		return -1;
+	}
+	if(mparam!=NULL)
+	{
+		if(fixup_get_svalue(msg, (gparam_p)mparam, &p)<0)
+		{
+			LM_ERR("cannot get the parameter\n");
+			return -1;
+		}
+		if(p.len>=511)
+		{
+			LM_ERR("parameter value too long %d\n", p.len);
+			return -1;
+		}
+		memcpy(_mono_buf_stack[1], p.s, p.len);
+		_mono_buf_stack[1][p.len] = '\0';
+	}
+	memcpy(_mono_buf_stack[0], s.s, s.len);
+	_mono_buf_stack[0][s.len] = '\0';
+	return app_mono_exec(msg, _mono_buf_stack[0],
+			(mparam)?_mono_buf_stack[1]:NULL);
+}
+
+/**
+ *
+ */
+static int w_app_mono_run(struct sip_msg *msg, char *mparam, char *extra)
+{
+	str p;
+
+	if(!mono_sr_initialized())
+	{
+		LM_ERR("Lua env not intitialized");
+		return -1;
+	}
+	if(mparam!=NULL)
+	{
+		if(fixup_get_svalue(msg, (gparam_p)mparam, &p)<0)
+		{
+			LM_ERR("cannot get the parameter\n");
+			return -1;
+		}
+		if(p.len>=511)
+		{
+			LM_ERR("parameter value too long %d\n", p.len);
+			return -1;
+		}
+		memcpy(_mono_buf_stack[0], p.s, p.len);
+		_mono_buf_stack[0][p.len] = '\0';
+	}
+	return app_mono_run(msg, (mparam)?_mono_buf_stack[0]:NULL);
+}
+
+
+int app_mono_load_param(modparam_t type, void *val)
+{
+	if(val==NULL)
+		return -1;
+	return sr_mono_load_script((char*)val);
+}
+
+int app_mono_register_param(modparam_t type, void *val)
+{
+	if(val==NULL)
+		return -1;
+	return sr_mono_register_module((char*)val);
+}
+
+static int fixup_mono_exec(void** param, int param_no)
+{
+	if(sr_mono_assembly_loaded())
+	{
+		LM_ERR("cannot use lua_exec(...) when an assembly is loaded\n");
+		return -1;
+	}
+	return fixup_spve_null(param, 1);
+}
+

+ 4 - 0
modules/app_mono/doc/Makefile

@@ -0,0 +1,4 @@
+docs = app_mono.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 36 - 0
modules/app_mono/doc/app_mono.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>app_mono Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<affiliation><orgname>asipto.com</orgname></affiliation>
+	    </author>
+	    <editor>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		    <email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2012</year>
+	    <holder>Daniel-Constantin Mierla (asipto.com)</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+    
+	<xi:include href="app_mono_admin.xml"/>
+    
+</book>

+ 231 - 0
modules/app_mono/doc/app_mono_admin.xml

@@ -0,0 +1,231 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<!-- Module User's Guide -->
+
+<chapter>
+    
+    <title>&adminguide;</title>
+    
+    <section>
+	<title>Overview</title>
+	<para>
+		This module allows executing assemblies of managed code, among most
+		popular being C# (.NET).
+		It uses Mono project (http://www.mono-project.com/) to embed the managed
+		code interpreter inside the SIP server, for a fast execution.
+	</para>
+	<para>
+		Besides C#, other languages can be used to build managed assemblies,
+		such as: Java, Python, VisualBasic.NET, JavaScript. For more details of
+		what kind of languages can be used to build managed
+		assemblies see: http://www.mono-project.com/Languages .
+	</para>
+	<para>
+		A managed assembly can get access to any of &kamailio; config variables
+		and set them, as well as many other functions implemented inside
+		&kamailio; itself, to make the handling of SIP easier from managed code.
+	</para>
+	<para>
+		There are two ways to execute managed code assemlies: load the code at
+		startup and only execute at runtime or load and execute at runtime. It
+		can run in one mode only at a time, a matter of 'load' parameter and
+		used function to execute the code.
+	</para>
+    </section>
+    <section>
+	<title>Dependencies</title>
+	<section>
+	    <title>&kamailio; Modules</title>
+	    <para>
+		The following modules must be loaded before this module:
+	    	<itemizedlist>
+		    <listitem>
+			<para>
+			    <emphasis>none</emphasis>.
+			</para>
+		    </listitem>
+	    	</itemizedlist>
+	    </para>
+	</section>
+	<section>
+	    <title>External Libraries or Applications</title>
+	    <para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+	    	<itemizedlist>
+		    <listitem>
+			<para>
+			    <emphasis>mono-devel</emphasis> - Mono devel toolkit.
+			</para>
+		    </listitem>
+	    	</itemizedlist>
+	    </para>
+	</section>
+    </section>
+    <section>
+	<title>Parameters</title>
+	<section>
+	    <title><varname>load</varname> (string)</title>
+	    <para>
+			Set the path to the Mono assembly to be loaded at startup. Then you
+			can use mono_run(param) to execute the assembly at runtime.
+	    </para>
+	    <para>
+		<emphasis>
+		    Default value is <quote>null</quote>.
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>load</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("app_mono", "load", "/usr/local/etc/kamailio/mono/myscript.exe")
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+	
+    <section>
+	<title>Functions</title>
+ 	<section>
+	    <title>
+		<function moreinfo="none">mono_exec(path [, param])</function>
+	    </title>
+	    <para>
+		Execute the managed code assembly stored in 'path'. The path can be
+		a string with pseudo-variables evaluated at runtime. A second parameter
+		can be optionally gived and passed over to the assembly.
+	    </para>
+		<para>
+		Note that the assembly is loaded every time from the file, so any change
+		to it is immediately visible. This function cannot be used if 'load'
+		parameter is set.
+		</para>
+		<example>
+		<title><function>lua_dofile</function> usage</title>
+		<programlisting format="linespecific">
+...
+mono_exec("/usr/local/etc/kamailio/mono/myscript.exe");
+...
+</programlisting>
+	    </example>
+	</section>
+	
+	<section>
+	    <title>
+		<function moreinfo="none">mono_run([param])</function>
+	    </title>
+	    <para>
+		Execute the assembly specified by 'load' module parameter. The assembly
+		is loaded at startup, changes to it will be effective after &kamailio;
+		restart.
+	    </para>
+	    <para>
+			An optional paraeter can be given and it will be passed over to the
+			assembly. It can be a string with pseudo-variables that are
+			evaluated at runtime.
+	    </para>
+		<example>
+		<title><function>lua_run</function> usage</title>
+		<programlisting format="linespecific">
+...
+if(!mono_run("myparam"))
+{
+    xdbg("SCRIPT: failed to execute mono assembly!\n");
+}
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+	
+    <section>
+	<title>Usage</title>
+	<para>
+	First create a library from the 'SR.cs' file provided
+	in the folder 'modules/app_mono/lib/'. The examples uses the folder
+	/usr/local/etc/kamailio/mono/ to store the Mono specific assemblies
+	to be used by &kamailio;.
+	</para>
+<programlisting format="linespecific">
+...
+mkdir -p /usr/local/etc/kamailio/mono/
+cp modules/app_mono/lib/SR.cs /usr/local/etc/kamailio/mono/
+gmcs -t:library SR.cs
+...
+</programlisting>
+	<para>
+	You should see 'SR.dll' file generated.
+	</para>
+    <para>
+	Create your application in C#/.NET and save it
+	in the same folder with SR.dll. You have to use SR package in your
+	managed source code file -- say you named MySRTest.cs. Also, be
+	aware that the Main() function from the managed assembly is executed,
+	thus be sure you have it defined.
+    </para>
+<programlisting format="linespecific">
+...
+using SR;
+
+namespace MySRTest {
+	class Test {
+		static int Main(string[] args) {
+			SR.Core.Log(1, "Kamailio API Version: " + SR.Core.APIVersion() + "\n");
+			if (args.Length == 1) {
+				SR.Core.Log(1, "Parameter from Kamailio config: "
+					+ args[0] + "\n");
+			}
+			SR.Core.Dbg("Request URI is: " + SR.PV.GetS("$ru") + "\n");
+			SR.PV.SetS("$rU", "123");
+			SR.HDR.AppendToReply("My-Mono-Hdr: ok\r\n");
+			SR.Core.ModF("sl_reply_error");
+			return 1;
+		}
+	}
+}
+...
+</programlisting>
+	<para>
+	You have to compile the  'SR.dll' file generated.
+	</para>
+<programlisting format="linespecific">
+...
+$ gmcs -r:SR.dll MySRTest.cs
+...
+</programlisting>
+    <para>
+		You should see the file 'MySRTest.exe' generated in the folder.
+    </para>
+    <para>
+		In the config file of &kamailio;, load app_mono.so module and use
+		its functions inside the routing blocks.
+    </para>
+<programlisting format="linespecific">
+...
+loadmodule "app_mono.so"
+...
+route {
+    ...
+    mono_exec("/usr/local/etc/kamailio/mono/MySRTest.exe");
+    ...
+}
+...
+</programlisting>
+    <para>
+		The API exported by SR package to Mono applications is documented
+		on &kamailio; wiki site. Also, it is easy to figure out by looking
+		in the source code tree inside the file modules/app_mono/lib/SR.cs.
+    </para>
+    </section>
+</chapter>
+

+ 41 - 0
modules/app_mono/lib/SR.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace SR {
+	public class Core {
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static string APIVersion();
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static void Log(int level, string text);
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static void Err(string text);
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static void Dbg(string text);
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static int ModF(string text);
+	}
+	public class PV {
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static string GetS(string pvn);
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static int GetI(string pvn);
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static int SetS(string pvn, string val);
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static int SetI(string pvn, int val);
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static int Unset(string pvn);
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static int IsNull(string pvn);
+	}
+	public class HDR {
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static int Append(string hv);
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static int Remove(string hv);
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static int Insert(string hv);
+		[MethodImplAttribute(MethodImplOptions.InternalCall)]
+			public extern static int AppendToReply(string hv);
+	}
+}