/*
 * $Id$
 *
 * message printing
 */

#include "hash_func.h"
#include "t_funcs.h"
#include "../../dprint.h"
#include "../../config.h"
#include "../../parser/parser_f.h"
#include "../../ut.h"
#include "../../parser/msg_parser.h"



#define  append_mem_block(_d,_s,_len) \
		do{\
			memcpy((_d),(_s),(_len));\
			(_d) += (_len);\
		}while(0);


/* Builds a CANCEL request based on an INVITE request. CANCEL is send
 * to same address as the INVITE */
int t_build_and_send_CANCEL(struct cell *Trans,unsigned int branch)
{
	struct sip_msg      *p_msg;
	struct hdr_field    *hdr;
	char                *cancel_buf, *p, *via;
	unsigned int         len, via_len;

	if ( !Trans->uac[branch].rpl_received )
	{
		DBG("DEBUG: t_build_and_send_CANCEL: no response ever received"
			" : dropping local cancel! \n");
		return 1;
	}

	if (Trans->uac[branch].request.cancel!=NO_CANCEL)
	{
		DBG("DEBUG: t_build_and_send_CANCEL: branch (%d)was already canceled"
			" : dropping local cancel! \n",branch);
		return 1;
	}

	cancel_buf = 0;
	via = 0;
	p_msg = Trans->uas.request;

	/* method, separators, version */
	len=SIP_VERSION_LEN + CANCEL_LEN + 2 /* spaces */ + CRLF_LEN;
	/* if URL was overridden .... */
	if (Trans->uac[branch].uri.s)
		len+=Trans->uac[branch].uri.len;
	else
	/* ... otherwise use the inbound URL */
		len+=REQ_LINE(p_msg).uri.len;

	/*via*/
	if ( add_branch_label(Trans,p_msg,branch)==-1 )
		goto error;
	via = via_builder(p_msg , &via_len, Trans->uac[branch].request.send_sock );
	if (!via)
	{
		LOG(L_ERR, "ERROR: t_build_and_send_CANCEL: "
			"no via header got from builder\n");
		goto error;
	}
	len+= via_len;
	/*headers*/
	for ( hdr=p_msg->headers ; hdr ; hdr=hdr->next ) {
		if (hdr->type==HDR_FROM || hdr->type==HDR_CALLID 
			|| hdr->type==HDR_TO )
			len += ((hdr->body.s+hdr->body.len ) - hdr->name.s ) + CRLF_LEN ;
		else if (hdr->type==HDR_CSEQ)
			len += hdr->name.len + 2 + ((struct cseq_body*)hdr->parsed)->number.len +
				1+CANCEL_LEN+CRLF_LEN;
	}
	/* User Agent, Conteny Length, EoM */
	len += USER_AGENT_LEN + CRLF_LEN +
		CONTENT_LEN_LEN + CRLF_LEN +
		CRLF_LEN;

	cancel_buf=sh_malloc( len+1 );
	if (!cancel_buf)
	{
		LOG(L_ERR, "ERROR: t_build_and_send_CANCEL: cannot allocate memory\n");
		goto error;
	}
	p = cancel_buf;

	append_mem_block( p, CANCEL " ", CANCEL_LEN +1 );
	if (Trans->uac[branch].uri.s) {
		append_mem_block( p, Trans->uac[branch].uri.s, 
			Trans->uac[branch].uri.len);
	} else {
		append_mem_block(p,REQ_LINE(p_msg).uri.s,
			REQ_LINE(p_msg).uri.len );
	}
	append_mem_block( p, " " SIP_VERSION CRLF, 1+SIP_VERSION_LEN+CRLF_LEN );

	/* insert our via */
	append_mem_block(p,via,via_len);

	/*other headers*/
	for ( hdr=p_msg->headers ; hdr ; hdr=hdr->next )
	{
		if(hdr->type==HDR_FROM||hdr->type==HDR_CALLID||hdr->type==HDR_TO)
		{
			append_mem_block(p,hdr->name.s,
				((hdr->body.s+hdr->body.len)-hdr->name.s) );
			append_mem_block(p, CRLF, CRLF_LEN );
		} else if ( hdr->type==HDR_CSEQ )
		{
			append_mem_block(p,hdr->name.s, hdr->name.len );
			append_mem_block(p,": ", 2 );
			append_mem_block(p, ((struct cseq_body*)hdr->parsed)->number.s,
				((struct cseq_body*)hdr->parsed)->number.len );
			append_mem_block(p, " " CANCEL CRLF, 1+CANCEL_LEN+CRLF_LEN);
		}
	}

	/* User Agent header, Content Length, EoM */
	append_mem_block(p,USER_AGENT CRLF CONTENT_LEN CRLF CRLF ,
		USER_AGENT_LEN + CRLF_LEN + CONTENT_LEN_LEN + CRLF_LEN + CRLF_LEN);
	*p=0;

	if (Trans->uac[branch].request.cancel) {
		shm_free( cancel_buf );
		LOG(L_WARN, "send_cancel: Warning: CANCEL already sent out\n");
		goto error;
	}

	Trans->uac[branch].request.activ_type = TYPE_LOCAL_CANCEL;
	Trans->uac[branch].request.cancel = cancel_buf;
	Trans->uac[branch].request.cancel_len = len;

	/*sets and starts the FINAL RESPONSE timer */
	set_timer(hash_table,&(Trans->uac[branch].request.fr_timer),FR_TIMER_LIST);
	/* sets and starts the RETRANS timer */
	Trans->uac[branch].request.retr_list = RT_T1_TO_1;
	set_timer(hash_table,&(Trans->uac[branch].request.retr_timer),RT_T1_TO_1);
	DBG("DEBUG: T_build_and_send_CANCEL : sending cancel...\n");
	SEND_CANCEL_BUFFER( &(Trans->uac[branch].request) );

	pkg_free(via);
	return 1;
error:
	if (via) pkg_free(via);
	return -1;
}


/* Builds an ACK request based on an INVITE request. ACK is send
 * to same address */
char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,int *ret_len)
{
	struct sip_msg      *p_msg , *r_msg;
	struct hdr_field    *hdr;
	char                *ack_buf, *p, *via;
	unsigned int         len, via_len;

	ack_buf = 0;
	via =0;
	p_msg = trans->uas.request;
	r_msg = rpl;

	if ( parse_headers(rpl,HDR_TO)==-1 || !rpl->to )
	{
		LOG(L_ERR, "ERROR: t_build_ACK: "
			"cannot generate a HBH ACK if key HFs in reply missing\n");
		goto error;
	}

	len = 0;
	/*first line's len */
	len += 4/*reply code and one space*/+
		p_msg->first_line.u.request.version.len+CRLF_LEN;
	/*uri's len*/
	if (trans->uac[branch].uri.s)
		len += trans->uac[branch].uri.len +1;
	else
		len += p_msg->first_line.u.request.uri.len +1;
	/*adding branch param*/
	if ( add_branch_label( trans , trans->uas.request , branch)==-1 )
		goto error;
	/*via*/
	via = via_builder(p_msg , &via_len, trans->uac[branch].request.send_sock );
	if (!via)
	{
		LOG(L_ERR, "ERROR: t_build_ACK: "
			"no via header got from builder\n");
		goto error;
	}
	len+= via_len;
	/*headers*/
	for ( hdr=p_msg->headers ; hdr ; hdr=hdr->next )
		if (hdr->type==HDR_FROM||hdr->type==HDR_CALLID||hdr->type==HDR_CSEQ)
			len += ((hdr->body.s+hdr->body.len ) - hdr->name.s ) + CRLF_LEN ;
		else if ( hdr->type==HDR_TO )
			len += ((r_msg->to->body.s+r_msg->to->body.len ) -
				r_msg->to->name.s ) + CRLF_LEN ;
	/* CSEQ method : from INVITE-> ACK */
	len -= 3  ;
	/* end of message */
	len += CRLF_LEN; /*new line*/

	ack_buf = sh_malloc(len+1);
	if (!ack_buf)
	{
		LOG(L_ERR, "ERROR: t_build_and_ACK: cannot allocate memory\n");
		goto error1;
	}
	p = ack_buf;

	/* first line */
	memcpy( p , "ACK " , 4);
	p += 4;
	/* uri */
	if ( trans->uac[branch].uri.s )
	{
		memcpy(p,trans->uac[branch].uri.s,trans->uac[branch].uri.len);
		p +=trans->uac[branch].uri.len;
	}else{
		memcpy(p,p_msg->orig+(p_msg->first_line.u.request.uri.s-p_msg->buf),
			p_msg->first_line.u.request.uri.len );
		p += p_msg->first_line.u.request.uri.len;
	}
	/* SIP version */
	*(p++) = ' ';
	memcpy(p,p_msg->orig+(p_msg->first_line.u.request.version.s-p_msg->buf),
		p_msg->first_line.u.request.version.len );
	p += p_msg->first_line.u.request.version.len;
	memcpy( p, CRLF, CRLF_LEN );
	p+=CRLF_LEN;

	/* insert our via */
	memcpy( p , via , via_len );
	p += via_len;

	/*other headers*/
	for ( hdr=p_msg->headers ; hdr ; hdr=hdr->next )
	{
		if ( hdr->type==HDR_FROM || hdr->type==HDR_CALLID  )
		{
			memcpy( p , p_msg->orig+(hdr->name.s-p_msg->buf) ,
				((hdr->body.s+hdr->body.len ) - hdr->name.s ) );
			p += ((hdr->body.s+hdr->body.len ) - hdr->name.s );
			memcpy( p, CRLF, CRLF_LEN );
			p+=CRLF_LEN;
		}
		else if ( hdr->type==HDR_TO )
		{
			memcpy( p , r_msg->orig+(r_msg->to->name.s-r_msg->buf) ,
				((r_msg->to->body.s+r_msg->to->body.len)-r_msg->to->name.s));
			p+=((r_msg->to->body.s+r_msg->to->body.len)-r_msg->to->name.s);
			memcpy( p, CRLF, CRLF_LEN );
			p+=CRLF_LEN;
		}
		else if ( hdr->type==HDR_CSEQ )
		{
			memcpy( p , p_msg->orig+(hdr->name.s-p_msg->buf) ,
				((((struct cseq_body*)hdr->parsed)->method.s)-hdr->name.s));
			p+=((((struct cseq_body*)hdr->parsed)->method.s)-hdr->name.s);
			memcpy( p , "ACK" CRLF, 3+CRLF_LEN );
			p += 3+CRLF_LEN;
		}
	}

	/* end of message */
	memcpy( p , CRLF , CRLF_LEN );
	p += CRLF_LEN;

	pkg_free( via );
	DBG("DEBUG: t_build_ACK: ACK generated\n");

	*(ret_len) = p-ack_buf;
	return ack_buf;

error1:
	pkg_free(via );
error:
	return 0;
}