/**
 * $Id$
 *
 * XLOG module
 *
 * Copyright (C) 2001-2003 FhG Fokus
 *
 * This file is part of ser, a free SIP server.
 *
 * ser 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
 *
 * For a license to use the ser software under conditions
 * other than those described here, or to purchase support for this
 * software, please contact iptel.org by e-mail at the following addresses:
 *    info@iptel.org
 *
 * ser 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* History:
 * --------
 * 2004-10-20 - added header name specifier (ramona)
 * 2005-07-04 - added color printing support via escape sequesnces
 *              contributed by Ingo Wolfsberger (ramona)
 * 2005-12-23 - parts from private branch merged (mma)
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "../../dprint.h"
#include "../../mem/mem.h"
#include "../../ut.h"
#include "../../trim.h"
#include "../../dset.h"
#include "../../resolve.h"
#include "../../qvalue.h"
#include "../../usr_avp.h"

#include "../../parser/parse_from.h"
#include "../../parser/parse_uri.h"
#include "../../parser/parse_hname2.h"
#include "../../parser/parse_refer_to.h"

#include "xl_lib.h"
#include <arpa/inet.h>  // inet_ntop

#include "../../select.h"

static str str_null  = STR_STATIC_INIT("<null>");
static str str_empty = STR_STATIC_INIT("");
static str str_per   = STR_STATIC_INIT("%");
static str str_hostname, str_domainname, str_fullname, str_ipaddr;

enum xl_host_t {
	XL_HOST_NULL,
	XL_HOST_NAME,
	XL_HOST_DOMAIN,
	XL_HOST_FULL,
	XL_HOST_IPADDR
};

int msg_id = 0;
time_t msg_tm = 0;
int cld_pid = 0;

#define XLOG_FIELD_DELIM ", "
#define XLOG_FIELD_DELIM_LEN (sizeof(XLOG_FIELD_DELIM) - 1)

#define UNIQUE_ID_LEN 16
static char UNIQUE_ID[UNIQUE_ID_LEN];

#define LOCAL_BUF_SIZE	511
static char local_buf[LOCAL_BUF_SIZE+1];

str* xl_get_nulstr()
{
	return &str_null;
}

static int xl_get_null(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	res->s = str_null.s;
	res->len = str_null.len;
	return 0;
}

static int xl_get_empty(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	res->s = str_empty.s;
	res->len = str_empty.len;
	return 0;
}

static int xl_get_percent(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	res->s = str_per.s;
	res->len = str_per.len;
	return 0;
}

static int xl_get_pid(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	int l = 0;
	char *ch = NULL;

	if(msg==NULL || res==NULL)
		return -1;

	if(cld_pid == 0)
		cld_pid = (int)getpid();
	ch = int2str_base_0pad(cld_pid, &l, hi, hi==10?0:8);

	res->s = ch;
	res->len = l;

	return 0;
}

static int xl_get_times(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	int l = 0;
	char *ch = NULL;

	if(msg==NULL || res==NULL)
		return -1;

	if(msg_id != msg->id || msg_tm==0)
	{
		msg_tm = time(NULL);
		msg_id = msg->id;
	}
	ch = int2str_base_0pad(msg_tm, &l, hi, hi==10?0:8);

	res->s = ch;
	res->len = l;

	return 0;
}
static int xl_get_timef(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	char *ch = NULL;

	if(msg==NULL || res==NULL)
		return -1;
	if(msg_id != msg->id || msg_tm==0)
	{
		msg_tm = time(NULL);
		msg_id = msg->id;
	}

	ch = ctime(&msg_tm);

	res->s = ch;
	res->len = strlen(ch)-1;

	return 0;
}

static int xl_get_msgid(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	int l = 0;
	char *ch = NULL;

	if(msg==NULL || res==NULL)
		return -1;

	ch = int2str_base_0pad(msg->id, &l, hi, hi==10?0:8);
	res->s = ch;
	res->len = l;

	return 0;
}

static int xl_get_method(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(msg->first_line.type == SIP_REQUEST)
	{
		res->s = msg->first_line.u.request.method.s;
		res->len = msg->first_line.u.request.method.len;
	}
	else
		return xl_get_null(msg, res, hp, hi, hf);

	return 0;
}

static int xl_get_status(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(msg->first_line.type == SIP_REPLY)
	{
		res->s = msg->first_line.u.reply.status.s;
		res->len = msg->first_line.u.reply.status.len;
	}
	else
		return xl_get_null(msg, res, hp, hi, hf);

	return 0;
}

static int xl_get_reason(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(msg->first_line.type == SIP_REPLY)
	{
		res->s = msg->first_line.u.reply.reason.s;
		res->len = msg->first_line.u.reply.reason.len;
	}
	else
		return xl_get_null(msg, res, hp, hi, hf);

	return 0;
}

static int xl_get_ruri(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(msg->first_line.type == SIP_REPLY)	/* REPLY doesnt have a ruri */
		return xl_get_null(msg, res, hp, hi, hf);

	if(msg->parsed_uri_ok==0 /* R-URI not parsed*/ && parse_sip_msg_uri(msg)<0)
	{
		LOG(L_ERR, "XLOG: xl_get_ruri: ERROR while parsing the R-URI\n");
		return xl_get_null(msg, res, hp, hi, hf);
	}

	if (msg->new_uri.s!=NULL)
	{
		res->s   = msg->new_uri.s;
		res->len = msg->new_uri.len;
	} else {
		res->s   = msg->first_line.u.request.uri.s;
		res->len = msg->first_line.u.request.uri.len;
	}

	return 0;
}

static int xl_get_contact(struct sip_msg* msg, str* res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(msg->contact==NULL && parse_headers(msg, HDR_CONTACT_F, 0)==-1)
	{
		DBG("XLOG: xl_get_contact: no contact header\n");
		return xl_get_null(msg, res, hp, hi, hf);
	}

	if(!msg->contact || !msg->contact->body.s || msg->contact->body.len<=0)
    {
		DBG("XLOG: xl_get_contact: no contact header!\n");
		return xl_get_null(msg, res, hp, hi, hf);
	}

	res->s = msg->contact->body.s;
	res->len = msg->contact->body.len;


//	res->s = ((struct to_body*)msg->contact->parsed)->uri.s;
//	res->len = ((struct to_body*)msg->contact->parsed)->uri.len;

	return 0;
}


static int xl_get_from(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(parse_from_header(msg)==-1)
	{
		LOG(L_ERR, "XLOG: xl_get_from: ERROR cannot parse FROM header\n");
		return xl_get_null(msg, res, hp, hi, hf);
	}

	if(msg->from==NULL || get_from(msg)==NULL)
		return xl_get_null(msg, res, hp, hi, hf);

	res->s = get_from(msg)->uri.s;
	res->len = get_from(msg)->uri.len;

	return 0;
}

static int xl_get_from_tag(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(parse_from_header(msg)==-1)
	{
		LOG(L_ERR, "XLOG: xl_get_from: ERROR cannot parse FROM header\n");
		return xl_get_null(msg, res, hp, hi, hf);
	}


	if(msg->from==NULL || get_from(msg)==NULL
			|| get_from(msg)->tag_value.s==NULL)
		return xl_get_null(msg, res, hp, hi, hf);

	res->s = get_from(msg)->tag_value.s;
	res->len = get_from(msg)->tag_value.len;

	return 0;
}


static int xl_get_to(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(msg->to==NULL && parse_headers(msg, HDR_TO_F, 0)==-1)
	{
		LOG(L_ERR, "XLOG: xl_get_to: ERROR cannot parse TO header\n");
		return xl_get_null(msg, res, hp, hi, hf);
	}
	if(msg->to==NULL || get_to(msg)==NULL)
		return xl_get_null(msg, res, hp, hi, hf);

	res->s = get_to(msg)->uri.s;
	res->len = get_to(msg)->uri.len;

	return 0;
}

static int xl_get_to_tag(struct sip_msg* msg, str* res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(msg->to==NULL && ((parse_headers(msg, HDR_TO_F, 0)==-1) ||
				(msg->to==NULL)) )
	{
		LOG(L_ERR, "XLOG: xl_get_to: ERROR cannot parse TO header\n");
		return xl_get_null(msg, res, hp, hi, hf);
	}

	if (get_to(msg)->tag_value.len <= 0)
		return xl_get_null(msg, res, hp, hi, hf);

	res->s = get_to(msg)->tag_value.s;
	res->len = get_to(msg)->tag_value.len;

	return 0;
}

static int xl_get_cseq(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(msg->cseq==NULL && ((parse_headers(msg, HDR_CSEQ_F, 0)==-1) ||
				(msg->cseq==NULL)) )
	{
		LOG(L_ERR, "XLOG: xl_get_cseq: ERROR cannot parse CSEQ header\n");
		return xl_get_null(msg, res, hp, hi, hf);
	}

	res->s = get_cseq(msg)->number.s;
	res->len = get_cseq(msg)->number.len;

	return 0;
}

static int xl_get_msg_buf(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	res->s = msg->buf;
	res->len = msg->len;

	return 0;
}

static int xl_get_msg_len(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	int l = 0;
	char *ch = NULL;

	if(msg==NULL || res==NULL)
		return -1;

	ch = int2str(msg->len, &l);
	res->s = ch;
	res->len = l;

	return 0;
}

static int xl_get_flags(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	int l = 0;
	char *ch = NULL;

	if(msg==NULL || res==NULL)
		return -1;

	ch = int2str(msg->flags, &l);
	res->s = ch;
	res->len = l;

	return 0;
}

static int xl_get_callid(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(msg->callid==NULL && ((parse_headers(msg, HDR_CALLID_F, 0)==-1) ||
				(msg->callid==NULL)) )
	{
		LOG(L_ERR, "XLOG: xl_get_callid: ERROR cannot parse Call-Id header\n");
		return xl_get_null(msg, res, hp, hi, hf);
	}

	res->s = msg->callid->body.s;
	res->len = msg->callid->body.len;
	trim(res);

	return 0;
}

static int xl_get_srcip(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	res->s = ip_addr2a(&msg->rcv.src_ip);
	res->len = strlen(res->s);

	return 0;
}

static int xl_get_srcport(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	int l = 0;
	char *ch = NULL;

	if(msg==NULL || res==NULL)
		return -1;

	ch = int2str(msg->rcv.src_port, &l);
	res->s = ch;
	res->len = l;

	return 0;
}

static int xl_get_rcvip(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(msg->rcv.bind_address==NULL
			|| msg->rcv.bind_address->address_str.s==NULL)
		return xl_get_null(msg, res, hp, hi, hf);

	res->s   = msg->rcv.bind_address->address_str.s;
	res->len = msg->rcv.bind_address->address_str.len;

	return 0;
}

static int xl_get_rcvport(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(msg->rcv.bind_address==NULL
			|| msg->rcv.bind_address->port_no_str.s==NULL)
		return xl_get_null(msg, res, hp, hi, hf);

	res->s   = msg->rcv.bind_address->port_no_str.s;
	res->len = msg->rcv.bind_address->port_no_str.len;

	return 0;
}

static int xl_get_useragent(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;
	if(msg->user_agent==NULL && ((parse_headers(msg, HDR_USERAGENT_F, 0)==-1)
			 || (msg->user_agent==NULL)))
	{
		DBG("XLOG: xl_get_useragent: User-Agent header not found\n");
		return xl_get_null(msg, res, hp, hi, hf);
	}

	res->s = msg->user_agent->body.s;
	res->len = msg->user_agent->body.len;
	trim(res);

	return 0;
}

static int xl_get_refer_to(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	if(msg==NULL || res==NULL)
		return -1;

	if(parse_refer_to_header(msg)==-1)
	{
		LOG(L_ERR, "XLOG: xl_get_refer_to: ERROR cannot parse Refer-To header\n");
		return xl_get_null(msg, res, hp, hi, hf);
	}

	if(msg->refer_to==NULL || get_refer_to(msg)==NULL)
		return xl_get_null(msg, res, hp, hi, hf);

	res->s = get_refer_to(msg)->uri.s;
	res->len = get_refer_to(msg)->uri.len;

	return 0;
}

static int xl_get_dset(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
    if(msg==NULL || res==NULL)
	return -1;

    res->s = print_dset(msg, &res->len);

    if ((res->s) == NULL) return xl_get_null(msg, res, hp, hi, hf);

    res->len -= CRLF_LEN;

    return 0;
}

#define COL_BUF 10

#define append_sstring(p, end, str) \
        do{\
                if ((p)+(sizeof(str)-1)<=(end)){\
                        memcpy((p), str, sizeof(str)-1); \
                        (p)+=sizeof(str)-1; \
                }else{ \
                        /* overflow */ \
                        LOG(L_ERR, "XLOG: append_sstring overflow\n"); \
                        goto error;\
                } \
        } while(0)

static int xl_get_color(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	static char color[COL_BUF];
	char* p;
	char* end;

	p = color;
	end = p + COL_BUF;

	/* excape sequenz */
	append_sstring(p, end, "\033[");

	if(hp->s[0]!='_')
	{
		if (islower((unsigned char)hp->s[0]))
		{
			/* normal font */
			append_sstring(p, end, "0;");
		} else {
			/* bold font */
			append_sstring(p, end, "1;");
			hp->s[0] += 32;
		}
	}

	/* foreground */
	switch(hp->s[0])
	{
		case 'x':
			append_sstring(p, end, "39;");
		break;
		case 's':
			append_sstring(p, end, "30;");
		break;
		case 'r':
			append_sstring(p, end, "31;");
		break;
		case 'g':
			append_sstring(p, end, "32;");
		break;
		case 'y':
			append_sstring(p, end, "33;");
		break;
		case 'b':
			append_sstring(p, end, "34;");
		break;
		case 'p':
			append_sstring(p, end, "35;");
		break;
		case 'c':
			append_sstring(p, end, "36;");
		break;
		case 'w':
			append_sstring(p, end, "37;");
		break;
		default:
			LOG(L_ERR, "XLOG: exit foreground\n");
			return xl_get_empty(msg, res, hp, hi, hf);
	}

	/* background */
	switch(hp->s[1])
	{
		case 'x':
			append_sstring(p, end, "49");
		break;
		case 's':
			append_sstring(p, end, "40");
		break;
		case 'r':
			append_sstring(p, end, "41");
		break;
		case 'g':
			append_sstring(p, end, "42");
		break;
		case 'y':
			append_sstring(p, end, "43");
		break;
		case 'b':
			append_sstring(p, end, "44");
		break;
		case 'p':
			append_sstring(p, end, "45");
		break;
		case 'c':
			append_sstring(p, end, "46");
		break;
		case 'w':
			append_sstring(p, end, "47");
		break;
		default:
			LOG(L_ERR, "XLOG: exit background\n");
			return xl_get_empty(msg, res, hp, hi, hf);
	}

	/* end */
	append_sstring(p, end, "m");

	res->s = color;
	res->len = p-color;
	return 0;

error:
	return -1;
}

static int xl_get_branch(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	str branch;
	qvalue_t q;

	if(msg==NULL || res==NULL)
		return -1;

	if(msg->first_line.type == SIP_REPLY)
		return xl_get_null(msg, res, hp, hi, hf);


	init_branch_iterator();
	branch.s = next_branch(&branch.len, &q, 0, 0, 0, 0);
	if (!branch.s) {
		return xl_get_null(msg, res, hp, hi, hf);
	}

	res->s = branch.s;
	res->len = branch.len;

	return 0;
}

#define Q_PARAM ">;q="
#define Q_PARAM_LEN (sizeof(Q_PARAM) - 1)

static int xl_get_branches(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	str uri;
	qvalue_t q;
	int len, cnt, i;
	unsigned int qlen;
	char *p, *qbuf;

	if(msg==NULL || res==NULL)
		return -1;

	if(msg->first_line.type == SIP_REPLY)
		return xl_get_null(msg, res, hp, hi, hf);

	cnt = len = 0;

	init_branch_iterator();
	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0)))
	{
		cnt++;
		len += uri.len;
		if (q != Q_UNSPECIFIED)
		{
			len += 1 + Q_PARAM_LEN + len_q(q);
		}
	}

	if (cnt == 0)
		return xl_get_empty(msg, res, hp, hi, hf);

	len += (cnt - 1) * XLOG_FIELD_DELIM_LEN;

	if (len + 1 > LOCAL_BUF_SIZE)
	{
		LOG(L_ERR, "ERROR:xl_get_branches: local buffer length exceeded\n");
		return xl_get_null(msg, res, hp, hi, hf);
	}

	i = 0;
	p = local_buf;

	init_branch_iterator();
	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0)))
	{
		if (i)
		{
			memcpy(p, XLOG_FIELD_DELIM, XLOG_FIELD_DELIM_LEN);
			p += XLOG_FIELD_DELIM_LEN;
		}

		if (q != Q_UNSPECIFIED)
		{
			*p++ = '<';
		}

		memcpy(p, uri.s, uri.len);
		p += uri.len;
		if (q != Q_UNSPECIFIED)
		{
			memcpy(p, Q_PARAM, Q_PARAM_LEN);
			p += Q_PARAM_LEN;

			qbuf = q2str(q, &qlen);
			memcpy(p, qbuf, qlen);
			p += qlen;
		}
		i++;
	}

	res->s = &(local_buf[0]);
	res->len = len;

	return 0;
}

static int xl_get_nexthop(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	*res=*GET_NEXT_HOP(msg);
	return 0;
}

#define XLOG_PRINT_ALL	-2
#define XLOG_PRINT_LAST	-1

static int xl_get_header(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	struct hdr_field *hdrf, *hdrf0;
	char *p;

	if(msg==NULL || res==NULL)
		return -1;

	if(hp==NULL || hp->len==0)
		return xl_get_null(msg, res, hp, hi, hf);

	hdrf0 = NULL;
	p = local_buf;

	/* we need to be sure we have parsed all headers */
	parse_headers(msg, HDR_EOH_F, 0);
	for (hdrf=msg->headers; hdrf; hdrf=hdrf->next)
	{
		if(hp->s==NULL)
		{
			if (hp->len!=hdrf->type)
				continue;
		} else {
			if (hdrf->name.len!=hp->len)
				continue;
			if (strncasecmp(hdrf->name.s, hp->s, hdrf->name.len)!=0)
				continue;
		}

		hdrf0 = hdrf;
		if(hi==XLOG_PRINT_ALL)
		{
			if(p!=local_buf)
			{
				if(p-local_buf+XLOG_FIELD_DELIM_LEN+1>LOCAL_BUF_SIZE)
				{
					LOG(L_ERR,
						"ERROR:xl_get_header: local buffer length exceeded\n");
					return xl_get_null(msg, res, hp, hi, hf);
				}
				memcpy(p, XLOG_FIELD_DELIM, XLOG_FIELD_DELIM_LEN);
				p += XLOG_FIELD_DELIM_LEN;
			}

			if(p-local_buf+hdrf0->body.len+1>LOCAL_BUF_SIZE)
			{
				LOG(L_ERR,
					"ERROR:xl_get_header: local buffer length exceeded!\n");
				return xl_get_null(msg, res, hp, hi, hf);
			}
			memcpy(p, hdrf0->body.s, hdrf0->body.len);
			p += hdrf0->body.len;
			continue;
		}

		if(hi==0)
			goto done;
		if(hi>0)
			hi--;
	}

done:
	if(hi==XLOG_PRINT_ALL)
	{
		*p = 0;
		res->s = local_buf;
		res->len = p - local_buf;
		return 0;
	}

	if(hdrf0==NULL || hi>0)
		return xl_get_null(msg, res, hp, hi, hf);
	res->s = hdrf0->body.s;
	res->len = hdrf0->body.len;
	trim(res);
	return 0;
}

static int inc_hex(char* c)
{
	switch (*c) {
	case '9':
		*c='a';
		return 0;
	case 'f':
		*c='0';
		return 1;
	default:
		(*c)++;
		return 0;
	}
}

static int xl_get_unique(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	int i;

	for (i=UNIQUE_ID_LEN-1; i && inc_hex(&UNIQUE_ID[i--]););
	res->s = &UNIQUE_ID[0];
	res->len = UNIQUE_ID_LEN;
	return 0;
}

static int xl_get_host(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	switch (hi) {
	case XL_HOST_NAME:
		*res = str_hostname;
		return 0;
	case XL_HOST_DOMAIN:
		*res = str_domainname;
		return 0;
	case XL_HOST_FULL:
		*res = str_fullname;
		return 0;
	case XL_HOST_IPADDR:
		*res = str_ipaddr;
		return 0;
	default:
		return xl_get_null(msg, res, hp, hi, hf);
	}
}

static int xl_get_select(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	int i;
	if ((i=run_select(res, (select_t*)hp->s, msg))==1)
		return xl_get_null(msg, res, hp, hi, hf);
	
	return i;
}

static void xl_free_select(str *hp)
{
	if (hp && hp->s)
		free_select((select_t*)hp->s);
}

static int xl_get_avp(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	int_str name, val;
	struct usr_avp *avp, *lavp;
	struct search_state st;

	if(msg==NULL || res==NULL || hp==NULL)
	return -1;

	name.s=*hp;
if (0){
	lavp=NULL;
	for(avp=search_first_avp(AVP_NAME_STR, name, NULL, &st); avp; avp=search_next_avp(&st, NULL)) {
		lavp=avp;
		if (hi>0)
			hi--;
		else if (hi==0)
			break;
	}

	if (lavp && (hi<=0)) {
		get_avp_val(lavp, &val);
		*res=val.s;
		return 0;
	}
}
	if ((avp=search_avp_by_index(hf, name, &val, hi))) {
		if (avp->flags & AVP_VAL_STR) {
			*res=val.s;
		} else {
			res->s=int2str(val.n, &res->len);
		}
		return 0;
	}

	return xl_get_null(msg, res, hp, hi, hf);
}

/* print special characters, like \r, \n, \t,... */
static int xl_get_special(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
{
	static char	c;

	if(msg==NULL || res==NULL)
		return -1;

	c = (char)hi;
	res->s = &c;
	res->len = 1;
	return 0;
}

int xl_parse_format(char *s, xl_elog_p *el)
{
	char *p, c;
	int n = 0;
	xl_elog_p e, e0;
	struct hdr_field  hdr;
	str name;
	int avp_flags, avp_index;
	int_str avp_name;
	select_t *sel;
	
	if(s==NULL || el==NULL)
		return -1;

	DBG("XLOG: xl_parse_format: parsing [%s]\n", s);

	p = s;
	*el = NULL;
	e = e0 = NULL;

	while(*p)
	{
		e0 = e;
		e = pkg_malloc(sizeof(xl_elog_t));
		if(!e)
			goto error;
		memset(e, 0, sizeof(xl_elog_t));
		n++;
		if(*el == NULL)
			*el = e;
		if(e0)
			e0->next = e;

		e->text.s = p;
		while(*p && *p!='%' && *p!='\\')
			p++;
		e->text.len = p - e->text.s;
		if(*p == '\0')
			break;

		if (*p == '\\') {
			p++;
			switch(*p)
			{
				case '\\':
					e->itf = xl_get_special;
					e->hindex = '\\';
					break;
				case 'r':
					e->itf = xl_get_special;
					e->hindex = '\r';
					break;
				case 'n':
					e->itf = xl_get_special;
					e->hindex = '\n';
					break;
				case 't':
					e->itf = xl_get_special;
					e->hindex = '\t';
					break;
				default:
					/* not a special character, it will be just
					written to the result as it is */
					e->itf = xl_get_special;
					e->hindex = *p;
			}
			goto cont;
		}

		p++;
		switch(*p)
		{
			case 'b':
				p++;
				switch(*p)
				{
					case 'r':
						e->itf = xl_get_branch;
					break;
					case 'R':
						e->itf = xl_get_branches;
					break;
					default:
						e->itf = xl_get_null;
				}
			break;
			case 'c':
				p++;
				switch(*p)
				{
					case 'i':
						e->itf = xl_get_callid;
					break;
					case 's':
						e->itf = xl_get_cseq;
					break;
					case 't':
						e->itf = xl_get_contact;
					break;
					default:
						e->itf = xl_get_null;
				}
			break;
			case 'C':
				p++;
				e->hparam.s = p;

				/* foreground */
				switch(*p)
                {
					case 'x':
					case 's': case 'r': case 'g':
					case 'y': case 'b': case 'p':
					case 'c': case 'w': case 'S':
					case 'R': case 'G': case 'Y':
					case 'B': case 'P': case 'C':
					case 'W':
					break;
					default:
						e->itf = xl_get_empty;
						goto error;
				}
				p++;

				/* background */
				switch(*p)
				{
					case 'x':
					case 's': case 'r': case 'g':
					case 'y': case 'b': case 'p':
					case 'c': case 'w':
					break;
					default:
						e->itf = xl_get_empty;
						goto error;
				}

				/* end */
				e->hparam.len = 2;
				e->itf = xl_get_color;
			break;
			case 'd':
				p++;
				switch(*p)
				{
					case 's':
						e->itf = xl_get_dset;
					break;
					default:
						e->itf = xl_get_null;
				}
			break;
			case 'f':
				p++;
				switch(*p)
				{
					case 'u':
						e->itf = xl_get_from;
					break;
					case 't':
						e->itf = xl_get_from_tag;
					break;
					default:
						e->itf = xl_get_null;
				}
			break;
			case 'H':
				p++;
				e->itf = xl_get_host;
				switch(*p)
				{
					case 'n':
						e->hindex = XL_HOST_NAME;
					break;
					case 'd':
						e->hindex = XL_HOST_DOMAIN;
					break;
					case 'f':
						e->hindex = XL_HOST_FULL;
					break;
					case 'i':
						e->hindex = XL_HOST_IPADDR;
					break;
					default:
						e->hindex = XL_HOST_NULL;
						break;
				}
				break;
			case 'm':
				p++;
				switch(*p)
				{
					case 'b':
						e->itf = xl_get_msg_buf;
					break;
					case 'f':
						e->itf = xl_get_flags;
					break;
					case 'i':
						e->itf = xl_get_msgid;
						e->hindex = 10;
					break;
					case 'l':
						e->itf = xl_get_msg_len;
					break;
					case 'x':
						e->itf = xl_get_msgid;
						e->hindex = 16;
					break;
					default:
						e->itf = xl_get_null;
				}
				break;
			case 'n':
				p++;
				switch(*p)
				{
					case 'h':
						e->itf = xl_get_nexthop;
					break;
					default:
						e->itf = xl_get_null;
				}
				break;
			case 'p':
				p++;
				switch(*p)
				{
					case 'p':
						e->itf = xl_get_pid;
						e->hindex = 10;
					break;
					case 'x':
						e->itf = xl_get_pid;
						e->hindex = 16;
					break;
					default:
						e->itf = xl_get_null;
				}
			break;
			case 'r':
				p++;
				switch(*p)
				{
					case 'm':
						e->itf = xl_get_method;
					break;
					case 'u':
						e->itf = xl_get_ruri;
					break;
					case 's':
						e->itf = xl_get_status;
					break;
					case 'r':
						e->itf = xl_get_reason;
					break;
					case 't':
						e->itf = xl_get_refer_to;
					break;
					default:
						e->itf = xl_get_null;
				}
			break;
			case 'R':
				p++;
				switch(*p)
				{
					case 'i':
						e->itf = xl_get_rcvip;
					break;
					case 'p':
						e->itf = xl_get_rcvport;
					break;
					default:
					e->itf = xl_get_null;
				}
			break;
			case 's':
				p++;
				switch(*p)
				{
					case 'i':
						e->itf = xl_get_srcip;
					break;
					case 'p':
						e->itf = xl_get_srcport;
					break;
					default:
					e->itf = xl_get_null;
				}
			break;
			case 't':
				p++;
				switch(*p)
				{
					case 'u':
						e->itf = xl_get_to;
					break;
					case 't':
						e->itf = xl_get_to_tag;
					break;
					default:
						e->itf = xl_get_null;
				}
			break;
			case 'T':
				p++;
				switch(*p)
				{
					case 's':
						e->itf = xl_get_times;
						e->hindex = 10;
					break;
					case 'f':
						e->itf = xl_get_timef;
					break;
					case 'x':
						e->itf = xl_get_times;
						e->hindex = 16;
					break;
					default:
						e->itf = xl_get_null;
				}
			break;
			case 'u':
				p++;
				switch(*p)
				{
					case 'a':
						e->itf = xl_get_useragent;
					break;
					case 'q':
						e->itf = xl_get_unique;
					break;
					default:
						e->itf = xl_get_null;
				}
			break;
			case '{':
				p++;
				/* we expect a letter */
				if((*p < 'A' || *p > 'Z') && (*p < 'a' || *p > 'z'))
				{
					LOG(L_ERR, "xlog: xl_parse_format: error parsing format"
						" [%s] pos [%d]\n", s, (int)(p-s));
					goto error;
				}
				e->hparam.s = p;
				while(*p && *p!='}' && *p!='[')
					p++;
				if(*p == '\0')
				{
					LOG(L_ERR, "xlog: xl_parse_format: error parsing format"
						" [%s] expecting '}' after position [%d]\n", s,
						(int)(e->hparam.s-s));
					goto error;
				}

				e->hparam.len = p - e->hparam.s;
				/* check if we have index */
				if(*p == '[')
				{
					p++;
					if(*p=='-')
					{
						p++;
						if(*p!='1')
						{
							LOG(L_ERR, "xlog: xl_parse_format: error"
								" parsing format [%s] -- only -1 is accepted"
								" as a negative index\n", s);
								goto error;
						}
						e->hindex = XLOG_PRINT_LAST;
						p++;
					} else if (*p=='*') {
						e->hindex = XLOG_PRINT_ALL;
						p++;
					} else {
						while(*p>='0' && *p<='9')
						{
							e->hindex = e->hindex * 10 + *p - '0';
							p++;
						}
					}
					if(*p != ']')
					{
						LOG(L_ERR, "xlog: xl_parse_format: error parsing format"
							" [%s] expecting ']' after position [%d]\n", s,
							(int)(e->hparam.s - s + e->hparam.len));
						goto error;
					}
					p++;
				}
				if(*p != '}')
				{
					LOG(L_ERR, "xlog: xl_parse_format: error parsing format"
						" [%s] expecting '}' after position [%d]!\n", s,
						(int)(e->hparam.s-s));
					goto error;
				}

				DBG("xlog: xl_parse_format: header name [%.*s] index [%d]\n",
						e->hparam.len, e->hparam.s, e->hindex);

				/* optimize for known headers -- fake header name */
				c = e->hparam.s[e->hparam.len];
				e->hparam.s[e->hparam.len] = ':';
				e->hparam.len++;
				/* ugly hack for compact header names -- !!fake length!!
				 * -- parse_hname2 expects name buffer length >= 4
				 */
				if (parse_hname2(e->hparam.s,
						e->hparam.s + ((e->hparam.len<4)?4:e->hparam.len),
						&hdr)==0)
				{
					LOG(L_ERR,"xlog: xl_parse_format: strange error\n");
					goto error;
				}
				e->hparam.len--;
				e->hparam.s[e->hparam.len] = c;
				if (hdr.type!=HDR_OTHER_T && hdr.type!=HDR_ERROR_T)
				{
					LOG(L_INFO,"INFO:xlog: xl_parse_format: using "
						"hdr type (%d) instead of <%.*s>\n",
						hdr.type, e->hparam.len, e->hparam.s);
					e->hparam.len = hdr.type;
					e->hparam.s = NULL;
				}
				e->itf = xl_get_header;
			break;
			case '<':
				p++;
				/* we expect a letter */
				if((*p < 'A' || *p > 'Z') && (*p < 'a' || *p > 'z'))
				{
					LOG(L_ERR, "xlog: xl_parse_format: error parsing format"
						" [%s] pos [%d]\n", s, (int)(p-s));
					goto error;
				}
				e->hparam.s = p;
				while(*p && *p!='>' && *p!='[')
					p++;
				if(*p == '\0')
				{
					LOG(L_ERR, "xlog: xl_parse_format: error parsing format"
						" [%s] expecting '>' after position [%d]\n", s,
						(int)(e->hparam.s-s));
					goto error;
				}

				e->hparam.len = p - e->hparam.s;
				/* check if we have index */
				if(*p == '[')
				{
					p++;
					if(*p=='-')
					{
						p++;
						if(*p!='1')
						{
							LOG(L_ERR, "xlog: xl_parse_format: error"
								" parsing format [%s] -- only -1 is accepted"
								" as a negative index\n", s);
								goto error;
						}
						e->hindex = -1;
						p++;
					}
					else
					{
						while(*p>='0' && *p<='9')
						{
							e->hindex = e->hindex * 10 + *p - '0';
							p++;
						}
					}
					if(*p != ']')
					{
						LOG(L_ERR, "xlog: xl_parse_format: error parsing format"
							" [%s] expecting ']' after position [%d]\n", s,
							(int)(e->hparam.s - s + e->hparam.len));
						goto error;
					}
					p++;
				}
				if(*p != '>')
				{
					LOG(L_ERR, "xlog: xl_parse_format: error parsing format"
						" [%s] expecting '>' after position [%d]!\n", s,
						(int)(e->hparam.s-s));
					goto error;
				}

				DBG("xlog: xl_parse_format: AVP [%.*s] index [%d]\n",
						e->hparam.len, e->hparam.s, e->hindex);

				e->itf = xl_get_avp;
			break;
			case '$':
				p++;
				name.s=p;
				while ( (*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p>='0' && *p<='9') || (*p=='_') || (*p=='+') || (*p=='-') || (*p=='[') || (*p==']') || (*p=='.') ) p++;
				name.len=p-name.s;
				p--;
				if (parse_avp_name(&name, &avp_flags, &avp_name, &avp_index) < 0) {
					ERR("error while parsing AVP name\n");
					goto error;
				}
				e->itf = xl_get_avp;
				e->hflags=avp_flags;
				e->hparam.s=name.s;
				e->hparam.len=name.len;
				e->hindex=avp_index;
				DBG("flags %x  name %.*s  index %d\n", avp_flags, avp_name.s.len, avp_name.s.s, avp_index);
				break;
			case '@':
					/* fill select structure and call resolve_select */
				DBG("xlog: xl_parse_format: @\n");
				n=parse_select(&p, &sel);
				if (n<0) {
					ERR("xlog: xl_parse_format: parse_select returned error\n");
					goto error;
				}
				e->itf = xl_get_select;
				e->hparam.s = (char*)sel;
				e->free_f = xl_free_select;
				p--;
				break;
			case '%':
				e->itf = xl_get_percent;
				break;
			case ' ':	/* enables spaceless terminating of avp, e.g. "blah%$avp% text goes on" */
			case '|':
				e->itf = xl_get_empty;
				break;
			default:
				e->itf = xl_get_null;
		}

cont:
		if(*p == '\0')
			break;
		p++;
	}
	DBG("XLOG: xl_parse_format: format parsed OK: [%d] items\n", n);

	return 0;

error:
	xl_elog_free_all(*el);
	*el = NULL;
	return -1;
}

int xl_print_log(struct sip_msg* msg, xl_elog_p log, char *buf, int *len)
{
	int n, h;
	str tok;
	xl_elog_p it;
	char *cur;

	if(msg==NULL || log==NULL || buf==NULL || len==NULL)
		return -1;

	if(*len <= 0)
		return -1;

	*buf = '\0';
	cur = buf;

	h = 0;
	n = 0;
	for (it=log; it; it=it->next)
	{
		/* put the text */
		if(it->text.s && it->text.len>0)
		{
			if(n+it->text.len < *len)
			{
				memcpy(cur, it->text.s, it->text.len);
				n += it->text.len;
				cur += it->text.len;
			}
			else
				goto overflow;
		}
		/* put the value of the specifier */
		if(it->itf
				/* && ((*it->itf != xl_get_color) || (log_stderr!=0)) */
				&& !((*it->itf)(msg, &tok, &(it->hparam), it->hindex, it->hflags)))
		{
			if(n+tok.len < *len)
			{
				memcpy(cur, tok.s, tok.len);
				n += tok.len;
				cur += tok.len;

				/* check for color entries to reset later */
				if (*it->itf == xl_get_color) {
					h = 1;
				}
			}
			else
				goto overflow;
		}
	}

	/* reset to default after entry */
	if (h == 1)
	{
		h = sizeof("\033[0m")-1;
		if (n+h < *len)
		{
			memcpy(cur, "\033[0m", h);
			n += h;
			cur += h;
			} else {
				goto overflow;
			}
	}

	goto done;

overflow:
	LOG(L_ERR,
		"XLOG:xl_print_log: buffer overflow -- increase the buffer size...\n");
	LOG(L_ERR, "Pos: %d, Add: %d, Len: %d, Buf:%.*s\n", n, tok.len, *len, n, buf);
	return -1;

done:
	DBG("XLOG: xl_print_log: final buffer length %d\n", n);
	*cur = '\0';
	*len = n;
	return 0;
}

int xl_elog_free_all(xl_elog_p log)
{
	xl_elog_p t;
	while(log)
	{
		t = log;
		log = log->next;
		if (t->free_f)
			(*t->free_f)(&(t->hparam));
		pkg_free(t);
	}
	return 0;
}

int xl_mod_init()
{
#ifdef HOST_NAME_MAX
#define HOSTNAME_MAX HOST_NAME_MAX
#else
#define HOSTNAME_MAX 256
#endif
	char *s, *d;
	struct hostent *he;
	int i;

	s=(char*)pkg_malloc(HOSTNAME_MAX);
	if (!s) return -1;
	if (gethostname(s, HOSTNAME_MAX)<0) {
		str_fullname.len = 0;
		str_fullname.s = NULL;
		str_hostname.len = 0;
		str_hostname.s = NULL;
		str_domainname.len = 0;
		str_domainname.s = NULL;
	} else {
		str_fullname.len = strlen(s);
		s = pkg_realloc(s, str_fullname.len+1); /* this will leave the ending \0 */
		if (!s) { /* should never happen because decreasing size */
			pkg_free(s);
			return -1;
		}
		str_fullname.s = s;

		d=strchr(s, '.');
		if (d) {
			str_hostname.len=d-s;
			str_hostname.s=s;
			str_domainname.len=str_fullname.len-str_hostname.len-1;
			str_domainname.s=d+1;
		} else {
			str_hostname=str_fullname;
			str_domainname.len=0;
			str_domainname.s=NULL;
		}
		s=(char*)pkg_malloc(HOSTNAME_MAX);
		if (!s) {
			pkg_free(str_fullname.s);
			return -1;
		}
	}

	str_ipaddr.len=0;
	str_ipaddr.s=NULL;
	if (str_fullname.len) {
		he=resolvehost(str_fullname.s);
		if (he) {
			if ((strlen(he->h_name)!=str_fullname.len) || strncmp(he->h_name, str_fullname.s, str_fullname.len)) {
				LOG(L_WARN, "WARNING: xl_mod_init: DIFFERENT hostname '%.*s' and gethostbyname '%s'\n", str_fullname.len, ZSW(str_hostname.s), he->h_name);
			}

			if (he->h_addr_list) {
				for (i=0; he->h_addr_list[i]; i++) {
					if (inet_ntop(he->h_addrtype, he->h_addr_list[i], s, HOSTNAME_MAX)) {
						if (str_ipaddr.len==0) {
							str_ipaddr.len=strlen(s);
							str_ipaddr.s=(char*)pkg_malloc(str_ipaddr.len);
							if (str_ipaddr.s) {
								memcpy(str_ipaddr.s, s, str_ipaddr.len);
							} else {
								str_ipaddr.len=0;
								LOG(L_ERR, "ERROR: xl_mod_init: No memory left for str_ipaddr\n");
							}
						} else if (strncmp(str_ipaddr.s, s, str_ipaddr.len)!=0) {
							LOG(L_WARN, "WARNING: xl_mod_init: more IP %s not used\n", s);
						}
					}
				}
			} else {
				LOG(L_WARN, "WARNING: xl_mod_init: can't resolve hostname's address\n");
			}

		}
	}
	pkg_free(s);

	DBG("Hostname:   %.*s\n", str_hostname.len, ZSW(str_hostname.s));
	DBG("Domainname: %.*s\n", str_domainname.len, ZSW(str_domainname.s));
	DBG("Fullname:   %.*s\n", str_fullname.len, ZSW(str_fullname.s));
	DBG("IPaddr:     %.*s\n", str_ipaddr.len, ZSW(str_ipaddr.s));

	return 0;
}

int xl_child_init(int rank)
{
	int i, x, rb, cb;

	for (i=RAND_MAX, rb=0; i; rb++, i>>=1);

	cb=x=0; /* x asiignment to make gcc happy */
	for (i=0; i<UNIQUE_ID_LEN; i++) {
		if (!cb) {
			cb=rb;
			x=rand();
		}
		UNIQUE_ID[i]=fourbits2char[x&0x0F];
		x>>=rb;
		cb-=rb;
	}

	return 0;
}