/**
 * Copyright (C) 2012 Daniel-Constantin Mierla (asipto.com)
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * This file 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
 *
 *
 * This file 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 <string.h>

#include "../../core/dprint.h"
#include "../../core/trim.h"
#include "../../core/ut.h"
#include "../../core/pvapi.h"
#include "../../core/sr_module.h"

#include "msrp_parser.h"
#include "msrp_vars.h"

extern int msrp_tls_module_loaded;

/**
 *
 */
int pv_parse_msrp_name(pv_spec_t *sp, str *in)
{
	if(sp == NULL || in == NULL || in->len <= 0)
		return -1;

	switch(in->len) {
		case 3:
			if(strncmp(in->s, "buf", 3) == 0)
				sp->pvp.pvn.u.isname.name.n = 1;
			else
				goto error;
			break;
		case 4:
			if(strncmp(in->s, "body", 4) == 0)
				sp->pvp.pvn.u.isname.name.n = 2;
			else if(strncmp(in->s, "code", 4) == 0)
				sp->pvp.pvn.u.isname.name.n = 3;
			else if(strncmp(in->s, "hdrs", 4) == 0)
				sp->pvp.pvn.u.isname.name.n = 4;
			else
				goto error;
			break;
		case 5:
			if(strncmp(in->s, "msgid", 5) == 0)
				sp->pvp.pvn.u.isname.name.n = 5;
			else if(strncmp(in->s, "conid", 5) == 0)
				sp->pvp.pvn.u.isname.name.n = 21;
			else
				goto error;
			break;
		case 6:
			if(strncmp(in->s, "method", 6) == 0)
				sp->pvp.pvn.u.isname.name.n = 6;
			else if(strncmp(in->s, "buflen", 6) == 0)
				sp->pvp.pvn.u.isname.name.n = 7;
			else if(strncmp(in->s, "sessid", 6) == 0)
				sp->pvp.pvn.u.isname.name.n = 8;
			else if(strncmp(in->s, "reason", 6) == 0)
				sp->pvp.pvn.u.isname.name.n = 9;
			else if(strncmp(in->s, "crthop", 6) == 0)
				sp->pvp.pvn.u.isname.name.n = 12;
			else
				goto error;
			break;
		case 7:
			if(strncmp(in->s, "bodylen", 7) == 0)
				sp->pvp.pvn.u.isname.name.n = 10;
			else if(strncmp(in->s, "transid", 7) == 0)
				sp->pvp.pvn.u.isname.name.n = 11;
			else if(strncmp(in->s, "prevhop", 7) == 0)
				sp->pvp.pvn.u.isname.name.n = 13;
			else if(strncmp(in->s, "nexthop", 7) == 0)
				sp->pvp.pvn.u.isname.name.n = 14;
			else if(strncmp(in->s, "lasthop", 7) == 0)
				sp->pvp.pvn.u.isname.name.n = 15;
			else if(strncmp(in->s, "srcaddr", 7) == 0)
				sp->pvp.pvn.u.isname.name.n = 19;
			else if(strncmp(in->s, "srcsock", 7) == 0)
				sp->pvp.pvn.u.isname.name.n = 20;
			else
				goto error;
			break;
		case 8:
			if(strncmp(in->s, "firsthop", 8) == 0)
				sp->pvp.pvn.u.isname.name.n = 16;
			else if(strncmp(in->s, "prevhops", 8) == 0)
				sp->pvp.pvn.u.isname.name.n = 17;
			else if(strncmp(in->s, "nexthops", 8) == 0)
				sp->pvp.pvn.u.isname.name.n = 18;
			else
				goto error;
			break;
		default:
			/* max is 21 */
			goto error;
	}
	sp->pvp.pvn.type = PV_NAME_INTSTR;
	sp->pvp.pvn.u.isname.type = 0;

	return 0;

error:
	LM_ERR("unknown PV msrp name %.*s\n", in->len, in->s);
	return -1;
}

/**
 *
 */
int pv_get_msrp(sip_msg_t *msg, pv_param_t *param, pv_value_t *res)
{
	msrp_frame_t *mf;
	msrp_hdr_t *hdr;
	str_array_t *sar;
	msrp_uri_t uri;
	str s;
	char *p;

	mf = msrp_get_current_frame();
	if(mf == NULL || param == NULL)
		return -1;

	sar = NULL;
	hdr = NULL;

	switch(param->pvn.u.isname.name.n) {
		case 1:
			s.s = mf->buf.s;
			s.len = mf->buf.len;
			return pv_get_strval(msg, param, res, &s);
		case 2:
			if(mf->mbody.s == NULL)
				return pv_get_null(msg, param, res);
			s.s = mf->mbody.s;
			s.len = mf->mbody.len;
			return pv_get_strval(msg, param, res, &s);
		case 3:
			if(mf->fline.msgtypeid == MSRP_REQUEST)
				return pv_get_null(msg, param, res);
			return pv_get_intstrval(msg, param, res,
					MSRP_RPL_CODE(mf->fline.rtypeid), &mf->fline.rtype);
		case 4:
			if(mf->hbody.s == NULL)
				return pv_get_null(msg, param, res);
			s.s = mf->hbody.s;
			s.len = mf->hbody.len;
			return pv_get_strval(msg, param, res, &s);
		case 5:
			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_MESSAGE_ID);
			if(hdr == NULL)
				return pv_get_null(msg, param, res);
			s.s = hdr->body.s;
			s.len = hdr->body.len;
			trim(&s);
			return pv_get_strval(msg, param, res, &s);
		case 6:
			if(mf->fline.msgtypeid == MSRP_REPLY)
				return pv_get_null(msg, param, res);
			return pv_get_strintval(
					msg, param, res, &mf->fline.rtype, mf->fline.rtypeid);
		case 7:
			return pv_get_uintval(msg, param, res, mf->buf.len);
		case 8:
			if(msrp_parse_hdr_to_path(mf) < 0)
				return pv_get_null(msg, param, res);
			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
			if(hdr == NULL)
				return pv_get_null(msg, param, res);
			sar = (str_array_t *)hdr->parsed.data;
			s = sar->list[0];
			trim(&s);
			if(msrp_parse_uri(s.s, s.len, &uri) < 0 || uri.session.len <= 0)
				return pv_get_null(msg, param, res);
			s = uri.session;
			trim(&s);
			return pv_get_strval(msg, param, res, &s);
		case 9:
			if(mf->fline.msgtypeid == MSRP_REQUEST || mf->fline.rtext.s == NULL)
				return pv_get_null(msg, param, res);
			return pv_get_strval(msg, param, res, &mf->fline.rtext);
		case 10:
			return pv_get_uintval(msg, param, res, mf->mbody.len);
		case 11:
			s = mf->fline.transaction;
			trim(&s);
			return pv_get_strval(msg, param, res, &s);
		case 12:
			if(msrp_parse_hdr_to_path(mf) < 0)
				return pv_get_null(msg, param, res);
			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
			if(hdr == NULL)
				return pv_get_null(msg, param, res);
			sar = (str_array_t *)hdr->parsed.data;
			s = sar->list[0];
			trim(&s);
			return pv_get_strval(msg, param, res, &s);
		case 13:
			if(msrp_parse_hdr_from_path(mf) < 0)
				return pv_get_null(msg, param, res);
			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH);
			if(hdr == NULL)
				return pv_get_null(msg, param, res);
			sar = (str_array_t *)hdr->parsed.data;
			s = sar->list[0];
			trim(&s);
			return pv_get_strval(msg, param, res, &s);
		case 14:
			if(msrp_parse_hdr_to_path(mf) < 0)
				return pv_get_null(msg, param, res);
			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
			if(hdr == NULL)
				return pv_get_null(msg, param, res);
			sar = (str_array_t *)hdr->parsed.data;
			if(sar->size < 2)
				return pv_get_null(msg, param, res);
			s = sar->list[1];
			trim(&s);
			return pv_get_strval(msg, param, res, &s);
		case 15:
			if(msrp_parse_hdr_to_path(mf) < 0)
				return pv_get_null(msg, param, res);
			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
			if(hdr == NULL)
				return pv_get_null(msg, param, res);
			sar = (str_array_t *)hdr->parsed.data;
			s = sar->list[sar->size - 1];
			trim(&s);
			return pv_get_strval(msg, param, res, &s);
		case 16:
			if(msrp_parse_hdr_from_path(mf) < 0)
				return pv_get_null(msg, param, res);
			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH);
			if(hdr == NULL)
				return pv_get_null(msg, param, res);
			sar = (str_array_t *)hdr->parsed.data;
			s = sar->list[sar->size - 1];
			trim(&s);
			return pv_get_strval(msg, param, res, &s);
		case 17:
			if(msrp_parse_hdr_from_path(mf) < 0)
				return pv_get_null(msg, param, res);
			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_FROM_PATH);
			if(hdr == NULL)
				return pv_get_null(msg, param, res);
			sar = (str_array_t *)hdr->parsed.data;
			return pv_get_uintval(msg, param, res, sar->size);
		case 18:
			if(msrp_parse_hdr_to_path(mf) < 0)
				return pv_get_null(msg, param, res);
			hdr = msrp_get_hdr_by_id(mf, MSRP_HDR_TO_PATH);
			if(hdr == NULL)
				return pv_get_null(msg, param, res);
			sar = (str_array_t *)hdr->parsed.data;
			return pv_get_uintval(msg, param, res, sar->size - 1);
		case 19:
			if(pv_get_buffer_size() < 100)
				return pv_get_null(msg, param, res);
			s.s = pv_get_buffer();
			p = s.s;
			if(msrp_tls_module_loaded) {
				memcpy(p, "msrps://", 8);
				p += 8;
			} else {
				memcpy(p, "msrp://", 7);
				p += 7;
			}
			strcpy(p, ip_addr2strz(&mf->tcpinfo->rcv->src_ip));
			strcat(p, ":");
			strcat(p, int2str(mf->tcpinfo->rcv->src_port, NULL));
			s.len = strlen(s.s);
			return pv_get_strval(msg, param, res, &s);
		case 20:
			return pv_get_strval(
					msg, param, res, &mf->tcpinfo->rcv->bind_address->sock_str);
		case 21:
			if(mf->tcpinfo->con == NULL)
				return pv_get_null(msg, param, res);
			return pv_get_sintval(msg, param, res, mf->tcpinfo->con->id);
		default:
			return pv_get_null(msg, param, res);
	}

	return 0;
}

/**
 *
 */
int pv_set_msrp(sip_msg_t *msg, pv_param_t *param, int op, pv_value_t *val)
{
	return 0;
}

enum _tr_msrp_type
{
	TR_MSRP_NONE = 0,
	TR_MSRPURI
};
enum _tr_msrpuri_subtype
{
	TR_MSRPURI_NONE = 0,
	TR_MSRPURI_USER,
	TR_MSRPURI_HOST,
	TR_MSRPURI_PORT,
	TR_MSRPURI_SESSION,
	TR_MSRPURI_PROTO,
	TR_MSRPURI_USERINFO,
	TR_MSRPURI_PARAMS,
	TR_MSRPURI_SCHEME
};

static str _tr_empty = {"", 0};
static str _tr_msrpuri = {0, 0};
static msrp_uri_t _tr_parsed_msrpuri;

/**
 *
 */
int tr_msrp_eval_msrpuri(
		struct sip_msg *msg, tr_param_t *tp, int subtype, pv_value_t *val)
{
	if(val == NULL || (!(val->flags & PV_VAL_STR)) || val->rs.len <= 0)
		return -1;

	if(_tr_msrpuri.len == 0 || _tr_msrpuri.len != val->rs.len
			|| strncmp(_tr_msrpuri.s, val->rs.s, val->rs.len) != 0) {
		if(val->rs.len > _tr_msrpuri.len) {
			if(_tr_msrpuri.s)
				pkg_free(_tr_msrpuri.s);
			_tr_msrpuri.s =
					(char *)pkg_malloc((val->rs.len + 1) * sizeof(char));
			if(_tr_msrpuri.s == NULL) {
				LM_ERR("no more private memory\n");
				memset(&_tr_msrpuri, 0, sizeof(str));
				memset(&_tr_parsed_msrpuri, 0, sizeof(msrp_uri_t));
				return -1;
			}
		}
		_tr_msrpuri.len = val->rs.len;
		memcpy(_tr_msrpuri.s, val->rs.s, val->rs.len);
		_tr_msrpuri.s[_tr_msrpuri.len] = '\0';
		/* reset old values */
		memset(&_tr_parsed_msrpuri, 0, sizeof(msrp_uri_t));
		/* parse uri */
		if(msrp_parse_uri(_tr_msrpuri.s, _tr_msrpuri.len, &_tr_parsed_msrpuri)
				!= 0) {
			LM_ERR("invalid uri [%.*s]\n", val->rs.len, val->rs.s);
			pkg_free(_tr_msrpuri.s);
			memset(&_tr_msrpuri, 0, sizeof(str));
			memset(&_tr_parsed_msrpuri, 0, sizeof(msrp_uri_t));
			return -1;
		}
	}
	memset(val, 0, sizeof(pv_value_t));
	val->flags = PV_VAL_STR;

	switch(subtype) {
		case TR_MSRPURI_USER:
			val->rs = (_tr_parsed_msrpuri.user.s) ? _tr_parsed_msrpuri.user
												  : _tr_empty;
			break;
		case TR_MSRPURI_USERINFO:
			val->rs = (_tr_parsed_msrpuri.userinfo.s)
							  ? _tr_parsed_msrpuri.userinfo
							  : _tr_empty;
			break;
		case TR_MSRPURI_HOST:
			val->rs = (_tr_parsed_msrpuri.host.s) ? _tr_parsed_msrpuri.host
												  : _tr_empty;
			break;
		case TR_MSRPURI_PORT:
			val->rs = (_tr_parsed_msrpuri.port.s) ? _tr_parsed_msrpuri.port
												  : _tr_empty;
			break;
		case TR_MSRPURI_SESSION:
			val->rs = (_tr_parsed_msrpuri.session.s)
							  ? _tr_parsed_msrpuri.session
							  : _tr_empty;
			break;
		case TR_MSRPURI_PROTO:
			val->rs = (_tr_parsed_msrpuri.proto.s) ? _tr_parsed_msrpuri.proto
												   : _tr_empty;
			break;
		case TR_MSRPURI_PARAMS:
			val->rs = (_tr_parsed_msrpuri.params.s) ? _tr_parsed_msrpuri.params
													: _tr_empty;
			break;
		case TR_MSRPURI_SCHEME:
			val->rs = (_tr_parsed_msrpuri.scheme.s) ? _tr_parsed_msrpuri.scheme
													: _tr_empty;
			break;
		default:
			LM_ERR("unknown subtype %d\n", subtype);
			return -1;
	}
	return 0;
}

/**
 *
 */
char *tr_parse_msrpuri(str *in, trans_t *t)
{
	str name;
	char *p;

	if(in == NULL || t == NULL)
		return NULL;

	p = in->s;
	name.s = in->s;
	t->type = TR_MSRPURI;
	t->trf = tr_msrp_eval_msrpuri;

	/* find next token */
	while(is_in_str(p, in) && *p != TR_PARAM_MARKER && *p != TR_RBRACKET)
		p++;
	if(*p == '\0') {
		LM_ERR("invalid transformation: %.*s\n", in->len, in->s);
		goto error;
	}
	name.len = p - name.s;
	trim(&name);

	if(name.len == 4 && strncasecmp(name.s, "user", 4) == 0) {
		t->subtype = TR_MSRPURI_USER;
		goto done;
	} else if(name.len == 4 && strncasecmp(name.s, "host", 4) == 0) {
		t->subtype = TR_MSRPURI_HOST;
		goto done;
	} else if(name.len == 4 && strncasecmp(name.s, "port", 4) == 0) {
		t->subtype = TR_MSRPURI_PORT;
		goto done;
	} else if(name.len == 7 && strncasecmp(name.s, "session", 7) == 0) {
		t->subtype = TR_MSRPURI_SESSION;
		goto done;
	} else if(name.len == 5 && strncasecmp(name.s, "proto", 5) == 0) {
		t->subtype = TR_MSRPURI_PROTO;
		goto done;
	} else if(name.len == 6 && strncasecmp(name.s, "scheme", 6) == 0) {
		t->subtype = TR_MSRPURI_SCHEME;
		goto done;
	} else if(name.len == 6 && strncasecmp(name.s, "params", 6) == 0) {
		t->subtype = TR_MSRPURI_PARAMS;
		goto done;
	} else if(name.len == 8 && strncasecmp(name.s, "userinfo", 8) == 0) {
		t->subtype = TR_MSRPURI_USERINFO;
		goto done;
	}


	LM_ERR("unknown transformation: %.*s/%.*s/%d!\n", in->len, in->s, name.len,
			name.s, name.len);
error:
	return NULL;
done:
	t->name = name;
	return p;
}
