/**
 * Copyright (C) 2009 SIP-Router.org
 *
 * This file is part of Extensible SIP Router, a free SIP server.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "corex_nio.h"

int nio_route_no;
int nio_min_msg_len;
int nio_is_incoming;

str nio_msg_avp_param;
int_str nio_msg_avp_name;
unsigned short nio_msg_avp_type;

/**
 * init nio function
 */
int nio_intercept_init(void)
{
	int route_no;
	pv_spec_t avp_spec;

	route_no=route_get(&event_rt, "network:msg");

	if (route_no==-1)
	{
		LM_ERR("failed to find event_route[network:msg]\n");
		return -1;
	}

	if (event_rt.rlist[route_no]==0)
	{
		LM_ERR("event_route[network:msg] is empty\n");
		return -1;
	}

	nio_route_no=route_no;

	if (nio_min_msg_len < 0)
	{
		LM_WARN("min_msg_len is less then zero, setting it to zero");
		nio_min_msg_len = 0;
	}

	if (nio_msg_avp_param.s && nio_msg_avp_param.len > 0)
	{
		if (pv_parse_spec(&nio_msg_avp_param, &avp_spec)==0
				|| avp_spec.type!=PVT_AVP)
		{
			LM_ERR("malformed or non AVP %.*s AVP definition\n",
					nio_msg_avp_param.len, nio_msg_avp_param.s);
			return -1;
		}

		if(pv_get_avp_name(0, &(avp_spec.pvp), &nio_msg_avp_name,
					&nio_msg_avp_type)!=0)
		{
			LM_ERR("[%.*s]- invalid AVP definition\n",
					nio_msg_avp_param.len, nio_msg_avp_param.s);
			return -1;
		}
	} else {
		LM_WARN("no AVP defined to store modified message\n");
	}

    /* register network hooks */
    sr_event_register_cb(SREV_NET_DATA_IN, nio_msg_received);
    sr_event_register_cb(SREV_NET_DATA_OUT, nio_msg_sent);
#ifdef USE_TCP
    tcp_set_clone_rcvbuf(1);
#endif
    return 0;
}

/**
 *
 */
int nio_msg_received(sr_event_param_t *evp)
{
    sip_msg_t msg;
    str *obuf;
    char *nbuf = NULL;
    int_str avp_value;
    struct usr_avp *avp;
    struct run_act_ctx ra_ctx;

    obuf = (str*)evp->data;

    if (obuf->len < nio_min_msg_len) {
        return -1;
    }

    memset(&msg, 0, sizeof(sip_msg_t));
    msg.buf = obuf->s;
    msg.len = obuf->len;

    nio_is_incoming = 1;
    init_run_actions_ctx(&ra_ctx);
    run_actions(&ra_ctx, event_rt.rlist[nio_route_no], &msg);

    if(nio_msg_avp_name.n!=0) {
        avp = NULL;
        avp=search_first_avp(nio_msg_avp_type, nio_msg_avp_name,
            &avp_value, 0);
        if(avp!=NULL && is_avp_str_val(avp)) {
            msg.buf = avp_value.s.s;
            msg.len = avp_value.s.len;
            nbuf = nio_msg_update(&msg, (unsigned int*)&obuf->len);
            if(obuf->len>=BUF_SIZE) {
                LM_ERR("new buffer overflow (%d)\n", obuf->len);
                pkg_free(nbuf);
                return -1;
            }
            memcpy(obuf->s, nbuf, obuf->len);
            obuf->s[obuf->len] = '\0';
        } else {
            LM_WARN("no value set for AVP %.*s, using unmodified message\n",
                nio_msg_avp_param.len, nio_msg_avp_param.s);
        }
    }

    if(nbuf!=NULL)
        pkg_free(nbuf);
    free_sip_msg(&msg);
    return 0;
}

/**
 *
 */
int nio_msg_sent(sr_event_param_t *evp)
{
    sip_msg_t msg;
    str *obuf;
    int_str avp_value;
    struct usr_avp *avp;
    struct run_act_ctx ra_ctx;
    str nbuf = STR_NULL;

    obuf = (str*)evp->data;

    if (obuf->len < nio_min_msg_len) {
        return -1;
    }

    memset(&msg, 0, sizeof(sip_msg_t));
    msg.buf = obuf->s;
    msg.len = obuf->len;

    nio_is_incoming = 0;
    init_run_actions_ctx(&ra_ctx);
    run_actions(&ra_ctx, event_rt.rlist[nio_route_no], &msg);

    if(nio_msg_avp_name.n!=0) {
        avp = NULL;
        avp=search_first_avp(nio_msg_avp_type, nio_msg_avp_name,
                &avp_value, 0);
        if(avp!=NULL && is_avp_str_val(avp)) {
            msg.buf = avp_value.s.s;
            msg.len = avp_value.s.len;
            nbuf.s = nio_msg_update(&msg, (unsigned int*)&nbuf.len);
			if(nbuf.s!=NULL) {
				LM_DBG("new outbound buffer generated\n");
				pkg_free(obuf->s);
				obuf->s = nbuf.s;
				obuf->len = nbuf.len;
			} else {
				LM_ERR("failed to generate new outbound buffer\n");
			}
        } else {
            LM_WARN("no value set for AVP %.*s, using unmodified message\n",
                nio_msg_avp_param.len, nio_msg_avp_param.s);
        }
    }

    free_sip_msg(&msg);
    return 0;
}

/**
 *
 */
int nio_check_incoming(void)
{
    return (nio_is_incoming) ? 1 : -1;
}

/**
 *
 */
char* nio_msg_update(sip_msg_t *msg, unsigned int *olen)
{
    struct dest_info dst;

    init_dest_info(&dst);
    dst.proto = PROTO_UDP;
    return build_req_buf_from_sip_req(msg,
            olen, &dst, BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE);
}

