/**
 * 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 <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>

#include "../../core/sr_module.h"
#include "../../core/dprint.h"
#include "../../core/ut.h"
#include "../../core/mem/mem.h"
#include "../../core/data_lump.h"
#include "../../core/data_lump_rpl.h"
#include "../../core/strutils.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;
}


