/*
 * $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 "msg_parser.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#ifdef DEBUG_DMALLOC
#include <dmalloc.h>
#endif


/* ret= 0! if action -> end of list(e.g DROP), 
      > 0 to continue processing next actions
   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;
	char* tmp;
	char *new_uri, *end, *crt;
	int len;
	int user;
	struct sip_uri uri;

	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_in));
			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;
		case SET_HOST_T:
		case SET_HOSTPORT_T:
		case SET_USER_T:
		case SET_USERPASS_T:
		case SET_PORT_T:
		case SET_URI_T:
				user=0;
				if (a->p1_type!=STRING_ST){
					LOG(L_CRIT, "BUG: do_action: bad set*() type %d\n",
							a->p1_type);
					ret=E_BUG;
					break;
				}
				if (a->type==SET_URI_T){
					if (msg->new_uri) free(msg->new_uri);
					len=strlen(a->p1.string);
					msg->new_uri=malloc(len+1);
					if (msg->new_uri==0){
						LOG(L_ERR, "ERROR: do_action: memory allocation"
								" failure\n");
						ret=E_OUT_OF_MEM;
						break;
					}
					memcpy(msg->new_uri, a->p1.string, len);
					msg->new_uri[len]=0;
					ret=1;
					break;
				}

				if (msg->new_uri) tmp=msg->new_uri;
				else tmp=msg->first_line.u.request.uri;
				if (parse_uri(tmp, strlen(tmp), &uri)<0){
					LOG(L_ERR, "ERROR: do_action: bad uri <%s>, dropping"
								" packet\n", tmp);
					ret=E_UNSPEC;
					break;
				}
				
				new_uri=malloc(MAX_URI_SIZE);
				if (new_uri==0){
					LOG(L_ERR, "ERROR: do_action: memory allocation "
								" failure\n");
					ret=E_OUT_OF_MEM;
					break;
				}
				end=new_uri+MAX_URI_SIZE;
				crt=new_uri;
				/* begin copying */
				len=strlen("sip:"); if(crt+len>end) goto error_uri;
				memcpy(crt,"sip:",len);crt+=len;
				/* user */
				if ((a->type==SET_USER_T)||(a->type==SET_USERPASS_T))
					tmp=a->p1.string;
				else 
					tmp=uri.user;
				if (tmp){
					len=strlen(tmp); if(crt+len>end) goto error_uri;
					memcpy(crt,tmp,len);crt+=len;
					user=1; /* we have an user field so mark it */
				}
				if (a->type==SET_USERPASS_T) tmp=0;
				else tmp=uri.passwd;
				/* passwd */
				if (tmp){
					len=strlen(":"); if(crt+len>end) goto error_uri;
					memcpy(crt,":",len);crt+=len;
					len=strlen(tmp); if(crt+len>end) goto error_uri;
					memcpy(crt,tmp,len);crt+=len;
				}
				/* host */
				if (user || tmp){ /* add @ */
					len=strlen("@"); if(crt+len>end) goto error_uri;
					memcpy(crt,"@",len);crt+=len;
				}
				if ((a->type==SET_HOST_T) ||(a->type==SET_HOSTPORT_T))
					tmp=a->p1.string;
				else
					tmp=uri.host;
				if (tmp){
					len=strlen(tmp); if(crt+len>end) goto error_uri;
					memcpy(crt,tmp,len);crt+=len;
				}
				/* port */
				if (a->type==SET_HOSTPORT_T) tmp=0;
				else if (a->type==SET_PORT_T) tmp=a->p1.string;
				else tmp=uri.port;
				if (tmp){
					len=strlen(":"); if(crt+len>end) goto error_uri;
					memcpy(crt,":",len);crt+=len;
					len=strlen(tmp); if(crt+len>end) goto error_uri;
					memcpy(crt,tmp,len);crt+=len;
				}
				/* params */
				tmp=uri.params;
				if (tmp){
					len=strlen(";"); if(crt+len>end) goto error_uri;
					memcpy(crt,";",len);crt+=len;
					len=strlen(tmp); if(crt+len>end) goto error_uri;
					memcpy(crt,tmp,len);crt+=len;
				}
				/* headers */
				tmp=uri.headers;
				if (tmp){
					len=strlen("?"); if(crt+len>end) goto error_uri;
					memcpy(crt,"?",len);crt+=len;
					len=strlen(tmp); if(crt+len>end) goto error_uri;
					memcpy(crt,tmp,len);crt+=len;
				}
				*crt=0; /* null terminate the thing */
				/* copy it to the msg */
				if (msg->new_uri) free(msg->new_uri);
				msg->new_uri=new_uri;
				ret=1;
				break;

		default:
			LOG(L_CRIT, "BUG: do_action: unknown type %d\n", a->type);
	}
	return ret;
	
error_uri:
	LOG(L_ERR, "ERROR: do_action: set*: uri too long\n");
	if (new_uri) free(new_uri);
	return E_UNSPEC;
}



/* 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;
}