/*
 * $Id$
 */



#include "action.h"
#include "config.h"
#include "error.h"
#include "dprint.h"
#include "proxy.h"
#include "forward.h"
#include "udp_server.h"
#include "route.h"

#include <netdb.h>
#include <stdlib.h>

/* ret= 0 if action -> end of lis t(e.g DROP), >0
   and >0 on error */
int do_action(struct action* a, struct sip_msg* msg)
{
	int ret;
	struct sockaddr_in* to;
	struct proxy_l* p;
	struct route_elem* re;

	ret=E_BUG;
	switch (a->type){
		case DROP_T:
				ret=0;
			break;
		case FORWARD_T:
			if ((a->p1_type!= PROXY_ST)|(a->p2_type!=NUMBER_ST)){
				LOG(L_CRIT, "BUG: do_action: bad forward() types %d, %d\n",
						a->p1_type, a->p2_type);
				ret=E_BUG;
				break;
			}
			ret=forward_request(msg, (struct proxy_l*)a->p1.data);
			if (ret>=0) ret=1;
			break;
		case SEND_T:
			to=(struct sockaddr_in*) malloc(sizeof(struct sockaddr));
			if (to==0){
				LOG(L_ERR, "ERROR: do_action: "
							"memory allocation failure\n");
				ret=E_OUT_OF_MEM;
				break;
			}
			if ((a->p1_type!= PROXY_ST)|(a->p2_type!=NUMBER_ST)){
				LOG(L_CRIT, "BUG: do_action: bad send() types %d, %d\n",
						a->p1_type, a->p2_type);
				ret=E_BUG;
				break;
			}
			
			p=(struct proxy_l*)a->p1.data;
			
			to->sin_family = AF_INET;
			to->sin_port=(p->port)?htons(p->port):htons(SIP_PORT);
			if (p->ok==0){
				if (p->host.h_addr_list[p->addr_idx+1])
					p->addr_idx++;
				p->ok=1;
			}
			to->sin_addr.s_addr=*((long*)p->host.h_addr_list[p->addr_idx]);
			p->tx++;
			p->tx_bytes+=msg->len;
			ret=udp_send(msg->orig, msg->len, (struct sockaddr*)to,
					sizeof(struct sockaddr_in));
			free(to);
			if (ret<0){
				p->errors++;
				p->ok=0;
			}else ret=1;
			
			break;
		case LOG_T:
			if ((a->p1_type!=NUMBER_ST)|(a->p2_type!=STRING_ST)){
				LOG(L_CRIT, "BUG: do_action: bad log() types %d, %d\n",
						a->p1_type, a->p2_type);
				ret=E_BUG;
				break;
			}
			LOG(a->p1.number, a->p2.string);
			ret=1;
			break;
		case ERROR_T:
			if ((a->p1_type!=STRING_ST)|(a->p2_type!=STRING_ST)){
				LOG(L_CRIT, "BUG: do_action: bad error() types %d, %d\n",
						a->p1_type, a->p2_type);
				ret=E_BUG;
				break;
			}
			LOG(L_NOTICE, "WARNING: do_action: error(\"%s\", \"%s\") "
					"not implemented yet\n", a->p1.string, a->p2.string);
			ret=1;
			break;
		case ROUTE_T:
			if (a->p1_type!=NUMBER_ST){
				LOG(L_CRIT, "BUG: do_action: bad route() type %d\n",
						a->p1_type);
				ret=E_BUG;
				break;
			}
			if ((a->p1.number>RT_NO)||(a->p1.number<0)){
				LOG(L_ERR, "ERROR: invalid routing table number in"
							"route(%d)\n", a->p1.number);
				ret=E_CFG;
				break;
			}
			re=route_match(msg, &rlist[a->p1.number]);
			if (re==0){
				LOG(L_INFO, "WARNING: do_action: route(%d): no new route"
						" found\n", a->p1.number);
				ret=1;
				break;
			}
			ret=((ret=run_actions(re->actions, msg))<0)?ret:1;
			break;
		case EXEC_T:
			if (a->p1_type!=STRING_ST){
				LOG(L_CRIT, "BUG: do_action: bad exec() type %d\n",
						a->p1_type);
				ret=E_BUG;
				break;
			}
			LOG(L_NOTICE, "WARNING: exec(\"%s\") not fully implemented,"
						" using dumb version...\n", a->p1.string);
			ret=system(a->p1.string);
			if (ret!=0){
				LOG(L_NOTICE, "WARNING: exec() returned %d\n", ret);
			}
			ret=1;
			break;
		default:
			LOG(L_CRIT, "BUG: do_action: unknown type %d\n", a->type);
	}
	return ret;
}



/* returns: 0 on success, -1 on error */
int run_actions(struct action* a, struct sip_msg* msg)
{
	struct action* t;
	int ret;
	static int rec_lev=0;

	rec_lev++;
	if (rec_lev>ROUTE_MAX_REC_LEV){
		LOG(L_ERR, "WARNING: too many recursive routing table lookups (%d)"
					" giving up!\n", rec_lev);
		ret=E_UNSPEC;
		goto error;
	}
		
	if (a==0){
		LOG(L_ERR, "WARNING: run_actions: null action list\n");
		ret=0;
	}

	for (t=a; t!=0; t=t->next){
		ret=do_action(t, msg);
		if(ret==0) break;
		else if (ret<0){ ret=-1; goto error; }
	}
	
	rec_lev--;
	return 0;
	

error:
	rec_lev--;
	return ret;
}