/*
 * Copyright (C) 2001-2003 FhG Fokus
 *
 * 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
 *
 */

/*!
 * \file
 * \brief Remote-Party-ID related functions
 * \ingroup auth
 * - Module: \ref auth
 */

#include <string.h>
#include <strings.h>
#include "../../core/str.h"
#include "../../core/data_lump.h"
#include "../../core/dprint.h"
#include "../../core/mem/mem.h"
#include "../../core/parser/parse_nameaddr.h"
#include "../../core/parser/parse_uri.h"
#include "../../core/parser/parser_f.h"
#include "../../core/ut.h"
#include "../../core/pvar.h"
#include "checks.h"
#include "rpid.h"


#define RPID_HF_NAME "Remote-Party-ID: "
#define RPID_HF_NAME_LEN (sizeof(RPID_HF_NAME) - 1)

extern str rpid_prefix; /*!< Remote-Party-ID prefix */
extern str rpid_suffix; /*!< Remote-Party-ID suffix */

/* rpid AVP specs */
static unsigned short rpid_avp_type;
static int_str rpid_avp_name;


/*!
 * \brief Parse and set the RPID AVP specs
 * \param rpid_avp_param RPID AVP parameter
 * \return 0 on success, -1 on failure
 */
int init_rpid_avp(char *rpid_avp_param)
{
	pv_spec_t avp_spec;
	str stmp;
	if(rpid_avp_param && *rpid_avp_param) {
		stmp.s = rpid_avp_param;
		stmp.len = strlen(stmp.s);
		if(pv_parse_spec(&stmp, &avp_spec) == 0 || avp_spec.type != PVT_AVP) {
			LM_ERR("malformed or non AVP %s AVP definition\n", rpid_avp_param);
			return -1;
		}

		if(pv_get_avp_name(0, &(avp_spec.pvp), &rpid_avp_name, &rpid_avp_type)
				!= 0) {
			LM_ERR("[%s]- invalid AVP definition\n", rpid_avp_param);
			return -1;
		}
	} else {
		rpid_avp_name.n = 0;
		rpid_avp_type = 0;
	}

	return 0;
}


/*!
 * \brief Gets the RPID avp specs
 * \param rpid_avp_p AVP name
 * \param rpid_avp_type_p AVP type
 */
void get_rpid_avp(int_str *rpid_avp_p, int *rpid_avp_type_p)
{
	*rpid_avp_p = rpid_avp_name;
	*rpid_avp_type_p = rpid_avp_type;
}


/*!
 * \brief Append RPID helper function
 * \param _m SIP message
 * \param _s appended string
 * \note Copy of append_hf_helper from textops
 * \return 0 on success, negative on failure
 */
static inline int append_rpid_helper(struct sip_msg *_m, str *_s)
{
	struct lump *anchor;

	if(parse_headers(_m, HDR_EOH_F, 0) == -1) {
		LM_ERR("failed to parse message\n");
		return -1;
	}

	anchor = anchor_lump(_m, _m->unparsed - _m->buf, 0, 0);
	if(!anchor) {
		LM_ERR("can't get anchor\n");
		return -2;
	}

	if(!insert_new_lump_before(anchor, _s->s, _s->len, 0)) {
		LM_ERR("can't insert lump\n");
		return -3;
	}

	return 0;
}


/*!
 * \brief Append RPID header field to the message
 * \param _m SIP message
 * \param _s1 unused
 * \param _s2 unused
 * \return 1 on success, -1 on failure
 */
int append_rpid_hf(struct sip_msg *_m, char *_s1, char *_s2)
{
	struct usr_avp *avp;
	str rpid_hf, rpid;
	char *at;
	int_str val;

	if(rpid_avp_name.n == 0) {
		LM_ERR("rpid avp not defined\n");
		return -1;
	}

	if((avp = search_first_avp(rpid_avp_type, rpid_avp_name, &val, 0)) == 0) {
		LM_DBG("no rpid AVP\n");
		return -1;
	}

	if(!(avp->flags & AVP_VAL_STR) || !val.s.s || !val.s.len) {
		LM_DBG("empty or non-string rpid, nothing to append\n");
		return -1;
	}

	rpid = val.s;

	rpid_hf.len = RPID_HF_NAME_LEN + rpid_prefix.len + rpid.len
				  + rpid_suffix.len + CRLF_LEN;
	rpid_hf.s = pkg_malloc(rpid_hf.len);
	if(!rpid_hf.s) {
		PKG_MEM_ERROR;
		return -1;
	}

	at = rpid_hf.s;
	memcpy(at, RPID_HF_NAME, RPID_HF_NAME_LEN);
	at += RPID_HF_NAME_LEN;

	memcpy(at, rpid_prefix.s, rpid_prefix.len);
	at += rpid_prefix.len;

	memcpy(at, rpid.s, rpid.len);
	at += rpid.len;

	memcpy(at, rpid_suffix.s, rpid_suffix.len);
	at += rpid_suffix.len;

	memcpy(at, CRLF, CRLF_LEN);

	if(append_rpid_helper(_m, &rpid_hf) < 0) {
		pkg_free(rpid_hf.s);
		return -1;
	}

	return 1;
}


/*!
 * \brief Append RPID header field to the message with parameters
 * \param _m SIP message
 * \param _prefix prefix
 * \param _suffix suffix
 * \return 1 on success, -1 on failure
 */
int append_rpid_hf_p(struct sip_msg *_m, char *_prefix, char *_suffix)
{
	struct usr_avp *avp;
	str rpid_hf, rpid;
	char *at;
	str *p, *s;
	int_str val;

	if(rpid_avp_name.n == 0) {
		LM_ERR("rpid avp not defined\n");
		return -1;
	}

	if((avp = search_first_avp(rpid_avp_type, rpid_avp_name, &val, 0)) == 0) {
		LM_DBG("no rpid AVP\n");
		return -1;
	}

	if(!(avp->flags & AVP_VAL_STR) || !val.s.s || !val.s.len) {
		LM_DBG("empty or non-string rpid, nothing to append\n");
		return -1;
	}

	rpid = val.s;

	p = (str *)_prefix;
	s = (str *)_suffix;

	rpid_hf.len = RPID_HF_NAME_LEN + p->len + rpid.len + s->len + CRLF_LEN;
	rpid_hf.s = pkg_malloc(rpid_hf.len);
	if(!rpid_hf.s) {
		PKG_MEM_ERROR;
		return -1;
	}

	at = rpid_hf.s;
	memcpy(at, RPID_HF_NAME, RPID_HF_NAME_LEN);
	at += RPID_HF_NAME_LEN;

	memcpy(at, p->s, p->len);
	at += p->len;

	memcpy(at, rpid.s, rpid.len);
	at += rpid.len;

	memcpy(at, s->s, s->len);
	at += s->len;

	memcpy(at, CRLF, CRLF_LEN);

	if(append_rpid_helper(_m, &rpid_hf) < 0) {
		pkg_free(rpid_hf.s);
		return -1;
	}

	return 1;
}


/*!
 * \brief Check if URI in RPID AVP contains an E164 user part
 * \param _m SIP message
 * \param _s1 unused
 * \param _s2 unused
 * \return 1 if the URI contains an E164 user part, -1 if not
 */
int is_rpid_user_e164(struct sip_msg *_m, char *_s1, char *_s2)
{
	struct usr_avp *avp;
	name_addr_t parsed;
	str tmp, rpid;
	struct sip_uri uri;
	int_str val;

	if(rpid_avp_name.n == 0) {
		LM_ERR("rpid avp not defined\n");
		return -1;
	}

	if((avp = search_first_avp(rpid_avp_type, rpid_avp_name, &val, 0)) == 0) {
		LM_DBG("no rpid AVP\n");
		goto err;
	}

	if(!(avp->flags & AVP_VAL_STR) || !val.s.s || !val.s.len) {
		LM_DBG("empty or non-string rpid, nothing to append\n");
		return -1;
	}

	rpid = val.s;

	if(find_not_quoted(&rpid, '<')) {
		if(parse_nameaddr(&rpid, &parsed) < 0) {
			LM_ERR("failed to parse RPID\n");
			goto err;
		}
		tmp = parsed.uri;
	} else {
		tmp = rpid;
	}

	if(parse_uri(tmp.s, tmp.len, &uri) < 0) {
		LM_ERR("failed to parse RPID URI\n");
		goto err;
	}

	return siputils_e164_check(&uri.user);

err:
	return -1;
}
