/*
 * $Id$
 *
 */

#include "../../dprint.h"
#include "../../config.h"
#include "../../parser/parser_f.h"
#include "../../ut.h"
#include "../../timer.h"
#include "../../hash_func.h"
#include "../../globals.h"
#include "../../dset.h"
#include "t_funcs.h"
#include "t_hooks.h"
#include "t_msgbuilder.h"
#include "ut.h"
#include "t_cancel.h"
#include "t_lookup.h"
#include "t_fwd.h"
#include "fix_lumps.h"


#ifdef _OBSOLETED
#define shm_free_lump( _lmp) \
	do{\
		if ((_lmp)) {\
			if ((_lmp)->op==LUMP_ADD && (_lmp)->u.value )\
				shm_free((_lmp)->u.value);\
			shm_free((_lmp));\
		}\
	}while(0);
#endif

char *print_uac_request( struct cell *t, struct sip_msg *i_req,
	int branch, str *uri, int *len, struct socket_info *send_sock )
{
	char *buf, *shbuf;

	shbuf=0;

	/* ... we calculate branch ... */	
	if (!t_setbranch( t, i_req, branch )) {
		LOG(L_ERR, "ERROR: print_uac_request: branch computation failed\n");
		goto error01;
	}

	/* ... update uri ... */
	i_req->new_uri=*uri;

	/* ... give apps a chance to change things ... */
	callback_event( TMCB_REQUEST_OUT, t, i_req, -i_req->REQ_METHOD);

	/* ... and build it now */
	buf=build_req_buf_from_sip_req( i_req, len, send_sock );
	if (!buf) {
		LOG(L_ERR, "ERROR: print_uac_request: no pkg_mem\n"); 
		ser_error=E_OUT_OF_MEM;
		goto error01;
	}
	/*	clean Via's we created now -- they would accumulate for
		other branches  and for  shmem i_req they would mix up
	 	shmem with pkg_mem
	*/
#ifdef OBSOLETED
	if (branch) for(b=i_req->add_rm,b1=0;b;b1=b,b=b->next)
		if (b->type==HDR_VIA) {
			for(a=b->before;a;)
				{c=a->before;free_lump(a);pkg_free(a);a=c;}
			for(a=b->after;a;)
				{c=a->after;free_lump(a);pkg_free(a);a=c;}
			if (b1) b1->next = b->next;
			else i_req->add_rm = b->next;
			free_lump(b);pkg_free(b);
		}
#endif
	free_via_lump(&i_req->add_rm);

	shbuf=(char *)shm_malloc(*len);
	if (!shbuf) {
		ser_error=E_OUT_OF_MEM;
		LOG(L_ERR, "ERROR: print_uac_request: no shmem\n");
		goto error02;
	}
	memcpy( shbuf, buf, *len );

error02:
	pkg_free( buf );
error01:
	return shbuf;
}

/* introduce a new uac to transaction; returns its branch id (>=0)
   or error (<0); it doesn't send a message yet -- a reply to it
   might itnerfere with the processes of adding multiple branches
*/
int add_uac( struct cell *t, struct sip_msg *request, str *uri, 
	struct proxy_l *proxy )
{

	int ret;
	short temp_proxy;
	union sockaddr_union to;
	unsigned short branch;
	struct socket_info* send_sock;
	char *shbuf;
	unsigned int len;

	branch=t->nr_of_outgoings;
	if (branch==MAX_BRANCHES) {
		LOG(L_ERR, "ERROR: add_uac: maximum number of branches exceeded\n");
		ret=E_CFG;
		goto error;
	}

	/* check existing buffer -- rewriting should never occur */
	if (t->uac[branch].request.buffer) {
		LOG(L_CRIT, "ERROR: add_uac: buffer rewrite attempt\n");
		ret=ser_error=E_BUG;
		goto error;
	}

	/* check DNS resolution */
	if (proxy) temp_proxy=0; else {
		proxy=uri2proxy( uri );
		if (proxy==0)  {
			ret=E_BAD_ADDRESS;
			goto error;
		}
		temp_proxy=1;
	}

	if (proxy->ok==0) {
		if (proxy->host.h_addr_list[proxy->addr_idx+1])
			proxy->addr_idx++;
		else proxy->addr_idx=0;
		proxy->ok=1;
	}

	hostent2su( &to, &proxy->host, proxy->addr_idx, 
		proxy->port ? htons(proxy->port):htons(SIP_PORT));

	send_sock=get_send_socket( &to );
	if (send_sock==0) {
		LOG(L_ERR, "ERROR: add_uac: can't fwd to af %d "
			" (no corresponding listening socket)\n",
			to.s.sa_family );
		ret=ser_error=E_NO_SOCKET;
		goto error01;
	}

	/* now message printing starts ... */
	shbuf=print_uac_request( t, request, branch, uri, 
		&len, send_sock );
	if (!shbuf) {
		ret=ser_error=E_OUT_OF_MEM;
		goto error01;
	}

	/* things went well, move ahead and install new buffer! */
	t->uac[branch].request.to=to;
	t->uac[branch].request.send_sock=send_sock;
	t->uac[branch].request.buffer=shbuf;
	t->uac[branch].request.buffer_len=len;
	t->uac[branch].uri.s=t->uac[branch].request.buffer+
		request->first_line.u.request.method.len+1;
	t->uac[branch].uri.len=uri->len;
	t->nr_of_outgoings++;

	/* update stats */
	proxy->tx++;
	proxy->tx_bytes+=len;

	/* done! */	
	ret=branch;
		
error01:
	if (temp_proxy) {
		free_proxy( proxy );
		free( proxy );
	}
error:
	return ret;
}

int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, 
	struct cell *t_invite, int branch )
{
	int ret;
	char *shbuf;
	int len;

	if (t_cancel->uac[branch].request.buffer) {
		LOG(L_CRIT, "ERROR: e2e_cancel_branch: buffer rewrite attempt\n");
		ret=ser_error=E_BUG;
		goto error;
	}	

	/* note -- there is a gap in proxy stats -- we don't update 
	   proxy stats with CANCEL (proxy->ok, proxy->tx, etc.)
	*/

	/* print */
	shbuf=print_uac_request( t_cancel, cancel_msg, branch, 
		&t_invite->uac[branch].uri, &len, 
		t_invite->uac[branch].request.send_sock);
	if (!shbuf) {
		LOG(L_ERR, "ERROR: e2e_cancel_branch: printing e2e cancel failed\n");
		ret=ser_error=E_OUT_OF_MEM;
		goto error;
	}
	
	/* install buffer */
	t_cancel->uac[branch].request.to=t_invite->uac[branch].request.to;
	t_cancel->uac[branch].request.send_sock=t_invite->uac[branch].request.send_sock;
	t_cancel->uac[branch].request.buffer=shbuf;
	t_cancel->uac[branch].request.buffer_len=len;
	t_cancel->uac[branch].uri.s=t_cancel->uac[branch].request.buffer+
		cancel_msg->first_line.u.request.method.len+1;
	t_cancel->uac[branch].uri.len=t_invite->uac[branch].uri.len;
	

	/* success */
	ret=1;


error:
	return ret;
}

void e2e_cancel( struct sip_msg *cancel_msg, 
	struct cell *t_cancel, struct cell *t_invite )
{
	branch_bm_t cancel_bm;
	int i;
	int lowest_error;
	str backup_uri;
	int ret;

	cancel_bm=0;
	lowest_error=0;

	backup_uri=cancel_msg->new_uri;
	/* determine which branches to cancel ... */
	which_cancel( t_invite, &cancel_bm );
	/* ... and install CANCEL UACs */
	for (i=0; i<t_invite->nr_of_outgoings; i++)
		if (cancel_bm & (1<<i)) {
			ret=e2e_cancel_branch(cancel_msg, t_cancel, t_invite, i);
			if (ret<0) cancel_bm &= ~(1<<i);
			if (ret<lowest_error) lowest_error=ret;
		}
	t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings;
	t_cancel->label=t_invite->label;
	cancel_msg->new_uri=backup_uri;

	/* send them out */
	for (i=0; i<t_cancel->nr_of_outgoings; i++) {
		if (cancel_bm & (1<<i)) {
			if (SEND_BUFFER( &t_cancel->uac[i].request)==-1) {
				LOG(L_ERR, "ERROR: e2e_cancel: send failed\n");
			}
			start_retr( &t_cancel->uac[i].request );
		}
	}


	/* if error occured, let it know upstream (final reply
	   will also move the transaction on wait state
	*/
	if (lowest_error<0) {
		LOG(L_ERR, "ERROR: cancel error\n");
		t_reply( t_cancel, cancel_msg, 500, "cancel error");
	/* if there are pending branches, let upstream know we
	   are working on it
	*/
	} else if (cancel_bm) {
		DBG("DEBUG: e2e_cancel: e2e cancel proceeding\n");
		t_reply( t_cancel, cancel_msg, 100, "trying to cancel" );
	/* if the transaction exists, but there is no more pending
	   branch, tell usptream we're done
	*/
	} else {
		DBG("DEBUG: e2e_cancel: e2e cancel -- no more pending branches\n");
		t_reply( t_cancel, cancel_msg, 200, "ok, no more pending branches" );
	}
}


/* function returns:
 *       1 - forward successfull
 *      -1 - error during forward
 */
int t_forward_nonack( struct cell *t, struct sip_msg* p_msg , 
	struct proxy_l * proxy )
{
	str          backup_uri;
	int branch_ret, lowest_ret;
	str current_uri;
	branch_bm_t	added_branches;
	int first_branch;
	int i;
	struct cell *t_invite;

	/* make -Wall happy */
	current_uri.s=0;

	t->kr|=REQ_FWDED;

	if (p_msg->REQ_METHOD==METHOD_CANCEL) {
		t_invite=t_lookupOriginalT( hash_table, p_msg );
		if (t_invite!=T_NULL) {
			e2e_cancel( p_msg, t, t_invite );
			UNREF(t_invite);
			return 1;
		}
	}

	/* backup current uri ... add_uac changes it */
	backup_uri = p_msg->new_uri;
	/* if no more specific error code is known, use this */
	lowest_ret=E_BUG;
	/* branches added */
	added_branches=0;
	/* branch to begin with */
	first_branch=t->nr_of_outgoings;

	/* on first-time forwarding, use current uri, later only what
	   is in additional branches (which may be continuously refilled
	*/
	if (first_branch==0) {
		branch_ret=add_uac( t, p_msg, 
			p_msg->new_uri.s ? &p_msg->new_uri :  
				&p_msg->first_line.u.request.uri,
			proxy );
		if (branch_ret>=0) 
			added_branches |= 1<<branch_ret;
		else
			lowest_ret=branch_ret;
	}

	init_branch_iterator();
	while((current_uri.s=next_branch( &current_uri.len))) {
		branch_ret=add_uac( t, p_msg, &current_uri, proxy );
		/* pick some of the errors in case things go wrong;
		   note that picking lowest error is just as good as
		   any other algorithm which picks any other negative
		   branch result */
		if (branch_ret>=0) 
			added_branches |= 1<<branch_ret;
		else
			lowest_ret=branch_ret;
	}
	/* consume processed branches */
	clear_branches();

	/* restore original URI */
	p_msg->new_uri=backup_uri;

	/* don't forget to clear all branches processed so far */

	/* things went wrong ... no new branch has been fwd-ed at all */
	if (added_branches==0)
		return lowest_ret;

	/* if someone set on_negative, store in in T-context */
	t->on_negative=get_on_negative();

	/* send them out now */
	for (i=first_branch; i<t->nr_of_outgoings; i++) {
		if (added_branches & (1<<i)) {
			if (SEND_BUFFER( &t->uac[i].request)==-1) {
				LOG(L_ERR, "ERROR: add_uac: sending request failed\n");
				if (proxy) { proxy->errors++; proxy->ok=0; }
			}
			start_retr( &t->uac[i].request );
		}
	}
	return 1;
}	

int t_replicate(struct sip_msg *p_msg,  struct proxy_l *proxy )
{
	/* this is a quite horrible hack -- we just take the message
	   as is, including Route-s, Record-route-s, and Vias ,
	   forward it downstream and prevent replies received
	   from relaying by setting the replication/local_trans bit;

		nevertheless, it should be good enough for the primary
		customer of this function, REGISTER replication

		if we want later to make it thoroughly, we need to
		introduce delete lumps for all the header fields above
	*/
	return t_relay_to(p_msg, proxy, 1 /* replicate */);
}