Browse Source

app_mono: module relocated to archive

Victor Seva 11 months ago
parent
commit
4c400728b1

+ 17 - 0
src/modules/app_mono/Makefile

@@ -0,0 +1,17 @@
+#
+#
+
+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
+
+include ../../Makefile.modules

+ 223 - 0
src/modules/app_mono/README

@@ -0,0 +1,223 @@
+app_mono Module
+
+Daniel-Constantin Mierla
+
+   asipto.com
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+Alex Balashov
+
+   <[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. mono_exec usage
+   1.3. mono_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 the execution of assemblies of managed code, among
+   the most popular of which is C# (.NET). It uses the Mono project
+   (http://www.mono-project.com/) to embed the managed code interpreter
+   inside the SIP server, providing fast execution.
+
+   Besides C#, other languages can be used to build managed assemblies,
+   such as: Java, Python, VisualBasic.NET, JavaScript. For more details on
+   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 Kamailio config variables and
+   set them. It can also perform many other functions implemented inside
+   Kamailio itself, allowing easier handling of SIP from managed code.
+
+   There are two ways to execute managed code assemblies: load the code at
+   startup and only execute at runtime, or load and execute at runtime.
+   Only one mode at a time may be used. The mode is determined by the
+   'load' parameter and the function used 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. 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 optionally be provided; it will be passed to the assembly.
+
+   Note that the assembly is loaded every time from the file, so any
+   change to it is immediately live. This function cannot be used if
+   'load' parameter is set.
+
+   Example 1.2. mono_exec 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, so changes to it will be effective only after
+   Kamailio restart.
+
+   An optional parameter can be given and it will be passed over to the
+   assembly. It can be a string with pseudo-variables; these will be
+   evaluated at runtime.
+
+   Example 1.3. mono_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 the 'SR.dll' file generated.
+
+   Create your application in C#/.NET and save it in the same folder with
+   SR.dll. You have to use the 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 that 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 Kamailio config file, load the 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 the SR package to Mono applications is documented
+   on the 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.

+ 871 - 0
src/modules/app_mono/app_mono_api.c

@@ -0,0 +1,871 @@
+/**
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "../../core/data_lump.h"
+#include "../../core/data_lump_rpl.h"
+#include "../../core/dprint.h"
+#include "../../core/mem/mem.h"
+#include "../../core/sr_module.h"
+#include "../../core/strutils.h"
+#include "../../core/ut.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) {
+    PKG_MEM_ERROR;
+    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 && 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;
+  struct action *act = NULL;
+  ksr_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);
+  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 + 1);
+  if (hdr == NULL) {
+    PKG_MEM_ERROR;
+    goto error;
+  }
+  memcpy(hdr, txt.s, txt.len);
+  hdr[txt.len] = '\0';
+  anchor =
+      anchor_lump(env_M->msg, hf->name.s + hf->len - env_M->msg->buf, 0, 0);
+  if ((anchor == NULL) ||
+      (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 + 1);
+  if (hdr == NULL) {
+    PKG_MEM_ERROR;
+    goto error;
+  }
+  memcpy(hdr, txt.s, txt.len);
+  hdr[txt.len] = '\0';
+  anchor =
+      anchor_lump(env_M->msg, hf->name.s + hf->len - env_M->msg->buf, 0, 0);
+  if ((anchor == NULL) ||
+      (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;
+}

+ 61 - 0
src/modules/app_mono/app_mono_api.h

@@ -0,0 +1,61 @@
+/**
+ * $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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 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 "../../core/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

+ 190 - 0
src/modules/app_mono/app_mono_mod.c

@@ -0,0 +1,190 @@
+/**
+ * $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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "../../core/dprint.h"
+#include "../../core/mod_fix.h"
+#include "../../core/sr_module.h"
+#include "../../core/ut.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", PARAM_STRING | USE_FUNC_PARAM, (void *)app_mono_load_param},
+    {"register", PARAM_STRING | 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,                                /*·exported·functions·*/
+    params,                              /*·exported·params·*/
+    0,                                   /*·exported·RPC·methods·*/
+    0,                                   /* exported pseudo-variables */
+    0,                                   /*·response·function·*/
+    mod_init,                            /* module initialization function */
+    child_init,                          /* per child init function */
+    mod_destroy,                         /* destroy 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 initialized");
+    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 initialized");
+    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
src/modules/app_mono/doc/Makefile

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

+ 41 - 0
src/modules/app_mono/doc/app_mono.xml

@@ -0,0 +1,41 @@
+<?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 "../../../../doc/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>
+	    <editor>
+		<firstname>Alex</firstname>
+		<surname>Balashov</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>

+ 230 - 0
src/modules/app_mono/doc/app_mono_admin.xml

@@ -0,0 +1,230 @@
+<?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 "../../../../doc/docbook/entities.xml">
+%docentities;
+
+]>
+
+<!-- Module User's Guide -->
+
+<chapter>
+
+    <title>&adminguide;</title>
+
+    <section>
+	<title>Overview</title>
+	<para>
+		This module allows the execution of assemblies of managed code, among
+		the most popular of which is C# (.NET).
+		It uses the Mono project (http://www.mono-project.com/) to embed the managed
+		code interpreter inside the SIP server, providing 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 on
+		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 &kamailio; config variables
+		and set them.  It can also perform many other functions implemented inside
+		&kamailio; itself, allowing easier handling of SIP from managed code.
+	</para>
+	<para>
+		There are two ways to execute managed code assemblies: load the code at
+		startup and only execute at runtime, or load and execute at runtime. Only
+		one mode at a time may be used.  The mode is determined by the 'load'
+		parameter and the function used 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 id="app_mono.p.load">
+	    <title><varname>load</varname> (string)</title>
+	    <para>
+			Set the path to the Mono assembly to be loaded at startup.  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 id="app_mono.f.mono_exec">
+	    <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 optionally be provided; it will be passed to the assembly.
+	    </para>
+		<para>
+		Note that the assembly is loaded every time from the file, so any change
+		to it is immediately live. This function cannot be used if 'load'
+		parameter is set.
+		</para>
+		<example>
+		<title><function>mono_exec</function> usage</title>
+		<programlisting format="linespecific">
+...
+mono_exec("/usr/local/etc/kamailio/mono/myscript.exe");
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section id="app_mono.mono_run">
+	    <title>
+		<function moreinfo="none">mono_run([param])</function>
+	    </title>
+	    <para>
+		Execute the assembly specified by 'load' module parameter. The assembly
+		is loaded at startup, so changes to it will be effective only after &kamailio;
+		restart.
+	    </para>
+	    <para>
+			An optional parameter can be given and it will be passed over to the
+			assembly. It can be a string with pseudo-variables; these will be
+			evaluated at runtime.
+	    </para>
+		<example>
+		<title><function>mono_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 the '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 the 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 that 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 &kamailio; config file, load the 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 the SR package to Mono applications is documented
+		on the &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
src/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);
+}
+}