/*
 * $Id$
 *
 * Copyright (C) 2001-2003 FhG Fokus
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Kamailio 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
 *
 * Kamailio 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

#include "../../core/parser/parse_uri.h"
#include "../../core/trim.h"
#include "../../core/dprint.h"
#include "../../core/str.h"
#include "../../core/ut.h"
#include "CPL_tree.h"
#include "sub_list.h"
#include "cpl_log.h"


static struct node *list = 0;
static xmlDtdPtr dtd;	 /* DTD file */
static xmlValidCtxt cvp; /* validating context */


typedef unsigned short length_type;
typedef length_type *length_type_ptr;

enum
{
	EMAIL_TO,
	EMAIL_HDR_NAME,
	EMAIL_KNOWN_HDR_BODY,
	EMAIL_UNKNOWN_HDR_BODY
};


#define ENCONDING_BUFFER_SIZE 65536

#define FOR_ALL_ATTR(_node, _attr) \
	for((_attr) = (_node)->properties; (_attr); (_attr) = (_attr)->next)

#define check_overflow(_p_, _offset_, _end_, _error_)                \
	do {                                                             \
		if((_p_) + (_offset_) >= (_end_)) {                          \
			LM_ERR("%s:%d: overflow -> buffer to small\n", __FILE__, \
					__LINE__);                                       \
			goto _error_;                                            \
		}                                                            \
	} while(0)

#define set_attr_type(_p_, _type_, _end_, _error_)                \
	do {                                                          \
		check_overflow(_p_, sizeof(length_type), _end_, _error_); \
		*((length_type_ptr)(_p_)) = htons((length_type)(_type_)); \
		(_p_) += sizeof(length_type);                             \
	} while(0)

#define append_short_attr(_p_, _n_, _end_, _error_)               \
	do {                                                          \
		check_overflow(_p_, sizeof(length_type), _end_, _error_); \
		*((length_type_ptr)(_p_)) = htons((length_type)(_n_));    \
		(_p_) += sizeof(length_type);                             \
	} while(0)

#define append_str_attr(_p_, _s_, _end_, _error_)                          \
	do {                                                                   \
		check_overflow(_p_, (_s_).len + 1 * ((((_s_).len) & 0x0001) == 1), \
				_end_, _error_);                                           \
		*((length_type_ptr)(_p_)) = htons((length_type)(_s_).len);         \
		(_p_) += sizeof(length_type);                                      \
		memcpy((_p_), (_s_).s, (_s_).len);                                 \
		(_p_) += (_s_).len + 1 * ((((_s_).len) & 0x0001) == 1);            \
	} while(0)

#define append_double_str_attr(_p_, _s1_, _s2_, _end_, _error_)                \
	do {                                                                       \
		check_overflow(_p_,                                                    \
				(_s1_).len + (_s2_).len                                        \
						+ 1 * ((((_s2_).len + (_s2_).len) & 0x0001) == 1),     \
				_end_, _error_);                                               \
		*((length_type_ptr)(_p_)) =                                            \
				htons((length_type)((_s1_).len) + (_s2_).len);                 \
		(_p_) += sizeof(length_type);                                          \
		memcpy((_p_), (_s1_).s, (_s1_).len);                                   \
		(_p_) += (_s1_).len;                                                   \
		memcpy((_p_), (_s2_).s, (_s2_).len);                                   \
		(_p_) += (_s2_).len + 1 * ((((_s1_).len + (_s2_).len) & 0x0001) == 1); \
	} while(0)

#define get_attr_val(_attr_name_, _val_, _error_)            \
	do {                                                     \
		(_val_).s = (char *)xmlGetProp(node, (_attr_name_)); \
		(_val_).len = strlen((_val_).s);                     \
		/* remove all spaces from begin and end */           \
		trim_spaces_lr((_val_));                             \
		if((_val_).len == 0) {                               \
			LM_ERR("%s:%d: attribute <%s> has an "           \
				   "empty value\n",                          \
					__FILE__, __LINE__, (_attr_name_));      \
			goto _error_;                                    \
		}                                                    \
	} while(0)


#define MAX_EMAIL_HDR_SIZE 7 /*we are looking only for SUBJECT and BODY ;-)*/
#define MAX_EMAIL_BODY_SIZE 512
#define MAX_EMAIL_SUBJECT_SIZE 32

static inline char *decode_mail_url(
		char *p, char *p_end, char *url, unsigned char *nr_attr)
{
	static char buf[MAX_EMAIL_HDR_SIZE];
	char c;
	char foo;
	unsigned short hdr_len;
	unsigned short *len;
	int max_len;
	int status;

	/* init */
	hdr_len = 0;
	max_len = 0;
	status = EMAIL_TO;
	(*nr_attr)++;
	set_attr_type(p, TO_ATTR, p_end, error); /* attr type */
	len = ((unsigned short *)(p));			 /* attr val's len */
	*len = 0;								 /* init the len */
	p += 2;

	/* parse the whole url */
	do {
		/* extract a char from the encoded url */
		if(*url == '+') {
			/* substitute a blank for a plus */
			c = ' ';
			url++;
			/* Look for a hex encoded character */
		} else if((*url == '%') && *(url + 1) && *(url + 2)) {
			/* hex encoded - convert to a char */
			c = hex2int(url[1]);
			foo = hex2int(url[2]);
			if(c == -1 || foo == -1) {
				LM_ERR("non-ASCII escaped "
					   "character in mail url [%.*s]\n",
						3, url);
				goto error;
			}
			c = c << 4 | foo;
			url += 3;
		} else {
			/* normal character - just copy it without changing */
			c = *url;
			url++;
		}

		/* finally we got a character !! */
		switch(c) {
			case '?':
				switch(status) {
					case EMAIL_TO:
						if(*len == 0) {
							LM_ERR("empty TO "
								   "address found in MAIL node!\n");
							goto error;
						}
						if(((*len) & 0x0001) == 1)
							p++;
						*len = htons(*len);
						hdr_len = 0;
						status = EMAIL_HDR_NAME;
						break;
					default:
						goto parse_error;
				}
				break;
			case '=':
				switch(status) {
					case EMAIL_HDR_NAME:
						LM_DBG("hdr [%.*s] found\n", hdr_len, buf);
						if(hdr_len == BODY_EMAILHDR_LEN
								&& strncasecmp(buf, BODY_EMAILHDR_STR, hdr_len)
										   == 0) {
							/* BODY hdr found */
							set_attr_type(p, BODY_ATTR, p_end, error);
							max_len = MAX_EMAIL_BODY_SIZE;
						} else if(hdr_len == SUBJECT_EMAILHDR_LEN
								  && strncasecmp(
											 buf, SUBJECT_EMAILHDR_STR, hdr_len)
											 == 0) {
							/* SUBJECT hdr found */
							set_attr_type(p, SUBJECT_ATTR, p_end, error);
							max_len = MAX_EMAIL_SUBJECT_SIZE;
						} else {
							LM_DBG("unknown hdr -> ignoring\n");
							status = EMAIL_UNKNOWN_HDR_BODY;
							break;
						}
						(*nr_attr)++;
						len = ((unsigned short *)(p)); /* attr val's len */
						*len = 0;					   /* init the len */
						p += 2;
						status = EMAIL_KNOWN_HDR_BODY;
						break;
					default:
						goto parse_error;
				}
				break;
			case '&':
				switch(status) {
					case EMAIL_KNOWN_HDR_BODY:
						if(((*len) & 0x0001) == 1)
							p++;
						*len = htons(*len);
					case EMAIL_UNKNOWN_HDR_BODY:
						hdr_len = 0;
						status = EMAIL_HDR_NAME;
						break;
					default:
						goto parse_error;
				}
				break;
			case 0:
				switch(status) {
					case EMAIL_TO:
						if(*len == 0) {
							LM_ERR("empty TO "
								   "address found in MAIL node!\n");
							goto error;
						}
					case EMAIL_KNOWN_HDR_BODY:
						if(((*len) & 0x0001) == 1)
							p++;
						*len = htons(*len);
					case EMAIL_UNKNOWN_HDR_BODY:
						break;
					default:
						goto parse_error;
				}
				break;
			default:
				switch(status) {
					case EMAIL_TO:
						(*len)++;
						*(p++) = c;
						if(*len == URL_MAILTO_LEN
								&& !strncasecmp(
										p - (*len), URL_MAILTO_STR, (*len))) {
							LM_DBG("MAILTO: found at"
								   " the beginning of TO -> removed\n");
							p -= (*len);
							*len = 0;
						}
						break;
					case EMAIL_KNOWN_HDR_BODY:
						if((*len) < max_len)
							(*len)++;
						*(p++) = c;
						break;
					case EMAIL_HDR_NAME:
						if(hdr_len < MAX_EMAIL_HDR_SIZE)
							hdr_len++;
						buf[hdr_len - 1] = c;
						break;
					case EMAIL_UNKNOWN_HDR_BODY:
						/* do nothing */
						break;
					default:
						goto parse_error;
				}
		}
	} while(c != 0);

	return p;
parse_error:
	LM_ERR("unexpected char [%c] in state %d"
		   " in email url \n",
			*url, status);
error:
	return 0;
}


/* Attr. encoding for ADDRESS node:
 *   | attr_t(2) attr_len(2) attr_val(2*x) |  IS/CONTAINS/SUBDOMAIN_OF attr (NT)
 */
static inline int encode_address_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		switch(attr->name[0]) {
			case 'i':
			case 'I':
				set_attr_type(p, IS_ATTR, buf_end, error);
				break;
			case 'c':
			case 'C':
				set_attr_type(p, CONTAINS_ATTR, buf_end, error);
				break;
			case 's':
			case 'S':
				set_attr_type(p, SUBDOMAIN_OF_ATTR, buf_end, error);
				break;
			default:
				LM_ERR("unknown attribute "
					   "<%s>\n",
						attr->name);
				goto error;
		}
		/* get the value of the attribute */
		get_attr_val(attr->name, val, error);
		/* copy also the \0 from the end of string */
		val.len++;
		append_str_attr(p, val, buf_end, error);
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for ADDRESS_SWITCH node:
 *   | attr1_t(2) attr1_val(2) |                FIELD attr
 *  [| attr2_t(2) attr2_val(2) |]?              SUBFILED attr
 */
static inline int encode_address_switch_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		/* get the value of the attribute */
		get_attr_val(attr->name, val, error);
		switch(attr->name[0]) {
			case 'F':
			case 'f':
				set_attr_type(p, FIELD_ATTR, buf_end, error);
				if(val.s[0] == 'D' || val.s[0] == 'd')
					append_short_attr(p, DESTINATION_VAL, buf_end, error);
				else if(val.s[6] == 'A' || val.s[6] == 'a')
					append_short_attr(
							p, ORIGINAL_DESTINATION_VAL, buf_end, error);
				else if(!val.s[6])
					append_short_attr(p, ORIGIN_VAL, buf_end, error);
				else {
					LM_ERR("unknown"
						   " value <%s> for FIELD attr\n",
							val.s);
					goto error;
				};
				break;
			case 'S':
			case 's':
				set_attr_type(p, SUBFIELD_ATTR, buf_end, error);
				switch(val.s[0]) {
					case 'u':
					case 'U':
						append_short_attr(p, USER_VAL, buf_end, error);
						break;
					case 'h':
					case 'H':
						append_short_attr(p, HOST_VAL, buf_end, error);
						break;
					case 'p':
					case 'P':
						append_short_attr(p, PORT_VAL, buf_end, error);
						break;
					case 't':
					case 'T':
						append_short_attr(p, TEL_VAL, buf_end, error);
						break;
					case 'd':
					case 'D':
						/*append_short_attr(p, DISPLAY_VAL, buf_end, error);
						break;*/  /* NOT YET SUPPORTED BY INTERPRETER */
					case 'a':
					case 'A':
						/*append_short_attr(p, ADDRESS_TYPE_VAL, buf_end,error);
						break;*/  /* NOT YET SUPPORTED BY INTERPRETER */
					default:
						LM_ERR("unknown value <%s> for SUBFIELD attr\n", val.s);
						goto error;
				}
				break;
			default:
				LM_ERR("unknown attribute <%s>\n", attr->name);
				goto error;
		}
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for LANGUAGE node:
 *   | attr_t(2) attr_len(2) attr_val(2*x) |              MATCHES attr  (NNT)
 */
static inline int encode_lang_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)

{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	char *end;
	char *val_bk;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		/* there is only one attribute -> MATCHES */
		if(attr->name[0] != 'M' && attr->name[0] != 'm') {
			LM_ERR("unknown attribute "
				   "<%s>\n",
					attr->name);
			goto error;
		}
		val.s = val_bk = (char *)xmlGetProp(node, attr->name);
		/* parse the language-tag */
		for(end = val.s, val.len = 0;; end++) {
			/* trim all spaces from the beginning of the tag */
			if(!val.len && (*end == ' ' || *end == '\t'))
				continue;
			/* we cannot have more than 2 attrs - LANG_TAG and LANG_SUBTAG */
			if((*nr_attr) >= 2)
				goto lang_error;
			if(((*end) | 0x20) >= 'a' && ((*end) | 0x20) <= 'z') {
				val.len++;
				continue;
			} else if(*end == '*' && val.len == 0 && (*nr_attr) == 0
					  && (*end == ' ' || *end == '\t' || *end == 0)) {
				val.len++;
				set_attr_type(p, MATCHES_TAG_ATTR, buf_end, error);
			} else if(val.len && (*nr_attr) == 0 && *end == '-') {
				set_attr_type(p, MATCHES_TAG_ATTR, buf_end, error);
			} else if(val.len && ((*nr_attr) == 0 || (*nr_attr) == 1)
					  && (*end == ' ' || *end == '\t' || *end == 0)) {
				set_attr_type(p,
						(!(*nr_attr)) ? MATCHES_TAG_ATTR : MATCHES_SUBTAG_ATTR,
						buf_end, error);
			} else
				goto lang_error;
			(*nr_attr)++;
			/*LM_DBG("----> language tag=%d; %d [%.*s]\n",*(p-1),
				val.len,val.len,end-val.len);*/
			val.s = end - val.len;
			append_str_attr(p, val, buf_end, error);
			val.len = 0;
			if(*end == 0)
				break;
		}
	}

	return p - p_orig;
lang_error:
	LM_ERR("bad value for language_tag <%s>\n", val_bk);
error:
	return -1;
}


/* Attr. encoding for PRIORITY node:
 *   | attr1_t(2) attr1_val(2) |                  LESS/GREATER/EQUAL attr
 *  [| attr2_t(2) attr2_len(2) attr_val(2*x) |]?  PRIOSTR attr (NT)
 */
static inline int encode_priority_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		/* attribute's name */
		switch(attr->name[0]) {
			case 'L':
			case 'l':
				set_attr_type(p, LESS_ATTR, buf_end, error);
				break;
			case 'G':
			case 'g':
				set_attr_type(p, GREATER_ATTR, buf_end, error);
				break;
			case 'E':
			case 'e':
				set_attr_type(p, EQUAL_ATTR, buf_end, error);
				break;
			default:
				LM_ERR("unknown attribute <%s>\n", attr->name);
				goto error;
		}
		/* attribute's encoded value */
		get_attr_val(attr->name, val, error);
		if(val.len == EMERGENCY_STR_LEN
				&& !strncasecmp(val.s, EMERGENCY_STR, val.len)) {
			append_short_attr(p, EMERGENCY_VAL, buf_end, error);
		} else if(val.len == URGENT_STR_LEN
				  && !strncasecmp(val.s, URGENT_STR, val.len)) {
			append_short_attr(p, URGENT_VAL, buf_end, error);
		} else if(val.len == NORMAL_STR_LEN
				  && !strncasecmp(val.s, NORMAL_STR, val.len)) {
			append_short_attr(p, NORMAL_VAL, buf_end, error);
		} else if(val.len == NON_URGENT_STR_LEN
				  && !strncasecmp(val.s, NON_URGENT_STR, val.len)) {
			append_short_attr(p, NON_URGENT_VAL, buf_end, error);
		} else {
			append_short_attr(p, UNKNOWN_PRIO_VAL, buf_end, error);
			set_attr_type(p, PRIOSTR_ATTR, buf_end, error);
			val.len++; /* append \0 also */
			append_str_attr(p, val, buf_end, error);
		}
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for STRING_SWITCH node:
 *  [| attr1_t(2) attr1_len(2) attr_val(2*x) |]?  IS attr  (NT)
 *  [| attr2_t(2) attr2_len(2) attr_val(2*x) |]?  CONTAINS attr (NT)
 */
static inline int encode_string_switch_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		/* there is only one attribute -> MATCHES */
		if(attr->name[0] != 'F' && attr->name[0] != 'f') {
			LM_ERR("unknown attribute <%s>\n", attr->name);
			goto error;
		}
		set_attr_type(p, FIELD_ATTR, buf_end, error);
		/* attribute's encoded value */
		get_attr_val(attr->name, val, error);
		switch(val.s[0]) {
			case 'S':
			case 's':
				append_short_attr(p, SUBJECT_VAL, buf_end, error);
				break;
			case 'O':
			case 'o':
				append_short_attr(p, ORGANIZATION_VAL, buf_end, error);
				break;
			case 'U':
			case 'u':
				append_short_attr(p, USER_AGENT_VAL, buf_end, error);
				break;
			case 'D':
			case 'd':
				append_short_attr(p, DISPLAY_VAL, buf_end, error);
				break;
			default:
				LM_ERR("unknown "
					   "value <%s> for FIELD\n",
						attr->name);
				goto error;
		}
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for STRING node:
 *  [| attr1_t(2) attr1_len(2) attr_val(2*x) |]?  IS attr  (NT)
 *  [| attr2_t(2) attr2_len(2) attr_val(2*x) |]?  CONTAINS attr (NT)
 */
static inline int encode_string_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		switch(attr->name[0]) {
			case 'I':
			case 'i':
				set_attr_type(p, IS_ATTR, buf_end, error);
				break;
			case 'C':
			case 'c':
				set_attr_type(p, CONTAINS_ATTR, buf_end, error);
				break;
			default:
				LM_ERR("unknown "
					   "attribute <%s>\n",
						attr->name);
				goto error;
		}
		/* attribute's encoded value */
		get_attr_val(attr->name, val, error);
		val.len++; /* grab also the \0 */
		append_str_attr(p, val, buf_end, error);
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for TIME_SWITCH node:
 *  [| attr1_t(2) attr1_len(2) attr_val(2*x) |]?  TZID attr  (NT)
 *  [| attr2_t(2) attr2_len(2) attr_val(2*x) |]?  TZURL attr (NT)
 */
static inline int encode_time_switch_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	static str tz_str = {"TZ=", 3};
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		switch(attr->name[2]) {
			case 'I':
			case 'i':
				set_attr_type(p, TZID_ATTR, buf_end, error);
				/* attribute's encoded value */
				get_attr_val(attr->name, val, error);
				val.len++; /* grab also the \0 */
				append_double_str_attr(p, tz_str, val, buf_end, error);
				break;
			case 'U':
			case 'u':
				/* set_attr_type(p, TZURL_ATTR, buf_end, error);
				 * is a waste of space to copy the url - the interpreter doesn't
				 * use it at all ;-) */
				break;
			default:
				LM_ERR("unknown attribute <%s>\n", attr->name);
				goto error;
		}
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for TIME node:
 *   | attr1_t(2) attr1_len(2) attr1_val(2*x) |       DSTART attr  (NT)
 *  [| attr2_t(2) attr2_len(2) attr2_val(2*x) |]?     DTEND attr (NT)
 *  [| attr3_t(2) attr3_len(2) attr3_val(2*x) |]?     DURATION attr (NT)
 *  [| attr4_t(2) attr4_len(2) attr4_val(2*x) |]?     FREQ attr (NT)
 *  [| attr5_t(2) attr5_len(2) attr5_val(2*x) |]?     WKST attr (NT)
 *  [| attr6_t(2) attr6_len(2) attr6_val(2*x) |]?     BYYEARDAY attr (NT)
 *  [| attr7_t(2) attr7_len(2) attr7_val(2*x) |]?     COUNT attr (NT)
 *  [| attr8_t(2) attr8_len(2) attr8_val(2*x) |]?     BYSETPOS attr (NT)
 *  [| attr9_t(2) attr9_len(2) attr9_val(2*x) |]?     BYMONTH attr (NT)
 *  [| attr10_t(2) attr10_len(2) attr_val10(2*x) |]?  BYMONTHDAY attr (NT)
 *  [| attr11_t(2) attr11_len(2) attr_val11(2*x) |]?  BYMINUTE attr (NT)
 *  [| attr12_t(2) attr12_len(2) attr_val12(2*x) |]?  INTERVAL attr (NT)
 *  [| attr13_t(2) attr13_len(2) attr_val13(2*x) |]?  UNTIL attr (NT)
 *  [| attr14_t(2) attr14_len(2) attr_val14(2*x) |]?  BYSECOND attr (NT)
 *  [| attr15_t(2) attr15_len(2) attr_val15(2*x) |]?  BYHOUR attr (NT)
 *  [| attr16_t(2) attr16_len(2) attr_val16(2*x) |]?  BYDAY attr (NT)
 *  [| attr17_t(2) attr17_len(2) attr_val17(2*x) |]?  BYWEEKNO attr (NT)
 */
static inline int encode_time_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		switch(attr->name[4]) {
			case 0:
				if(attr->name[0] == 'F' || attr->name[0] == 'f')
					set_attr_type(p, FREQ_ATTR, buf_end, error);
				else if(attr->name[0] == 'W' || attr->name[0] == 'w')
					set_attr_type(p, WKST_ATTR, buf_end, error);
				break;
			case 'a':
			case 'A':
				if(attr->name[0] == 'D' || attr->name[0] == 'd')
					set_attr_type(p, DTSTART_ATTR, buf_end, error);
				else if(attr->name[0] == 'B' || attr->name[0] == 'b')
					set_attr_type(p, BYYEARDAY_ATTR, buf_end, error);
				break;
			case 't':
			case 'T':
				if(attr->name[0] == 'D' || attr->name[0] == 'd')
					set_attr_type(p, DURATION_ATTR, buf_end, error);
				else if(attr->name[0] == 'C' || attr->name[0] == 'c')
					set_attr_type(p, COUNT_ATTR, buf_end, error);
				else if(attr->name[0] == 'B' || attr->name[0] == 'b')
					set_attr_type(p, BYSETPOS_ATTR, buf_end, error);
				break;
			case 'n':
			case 'N':
				if(!attr->name[7])
					set_attr_type(p, BYMONTH_ATTR, buf_end, error);
				else if(attr->name[7] == 'D' || attr->name[7] == 'd')
					set_attr_type(p, BYMONTHDAY_ATTR, buf_end, error);
				else if(attr->name[7] == 'e' || attr->name[7] == 'E')
					set_attr_type(p, BYMINUTE_ATTR, buf_end, error);
				break;
			case 'd':
			case 'D':
				set_attr_type(p, DTEND_ATTR, buf_end, error);
				break;
			case 'r':
			case 'R':
				set_attr_type(p, INTERVAL_ATTR, buf_end, error);
				break;
			case 'l':
			case 'L':
				set_attr_type(p, UNTIL_ATTR, buf_end, error);
				break;
			case 'c':
			case 'C':
				set_attr_type(p, BYSECOND_ATTR, buf_end, error);
				break;
			case 'u':
			case 'U':
				set_attr_type(p, BYHOUR_ATTR, buf_end, error);
				break;
			case 'y':
			case 'Y':
				set_attr_type(p, BYDAY_ATTR, buf_end, error);
				break;
			case 'e':
			case 'E':
				set_attr_type(p, BYWEEKNO_ATTR, buf_end, error);
				break;
			default:
				LM_ERR("unknown attribute <%s>\n", attr->name);
				goto error;
		}
		/* attribute's encoded value */
		get_attr_val(attr->name, val, error);
		val.len++; /* grab also the \0 */
		append_str_attr(p, val, buf_end, error);
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for LOOKUP node:
 *  | attr1_t(2) attr1_len(2) attr1_val(2*x) |      SOURCE attr  (NT)
 * [| attr2_t(2) attr2_val(2) |]?                   CLEAR attr
 */
static inline int encode_lookup_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		/* get attribute's value */
		get_attr_val(attr->name, val, error);
		if(!strcasecmp((const char *)attr->name, "source")) {
			/* this param will not be copied, since it has only one value ;-)*/
			if(val.len != SOURCE_REG_STR_LEN
					|| strncasecmp(val.s, SOURCE_REG_STR, val.len)) {
				LM_ERR("unsupported value"
					   " <%.*s> in SOURCE param\n",
						val.len, val.s);
				goto error;
			}
		} else if(!strcasecmp((const char *)attr->name, "clear")) {
			(*nr_attr)++;
			set_attr_type(p, CLEAR_ATTR, buf_end, error);
			if(val.len == 3 && !strncasecmp(val.s, "yes", 3))
				append_short_attr(p, YES_VAL, buf_end, error);
			else if(val.len == 2 && !strncasecmp(val.s, "no", 2))
				append_short_attr(p, NO_VAL, buf_end, error);
			else {
				LM_ERR("unknown value "
					   "<%.*s> for attribute CLEAR\n",
						val.len, val.s);
				goto error;
			}
		} else if(!strcasecmp((const char *)attr->name, "timeout")) {
			LM_WARN("unsupported param TIMEOUT; skipping\n");
		} else {
			LM_ERR("unknown attribute <%s>\n", attr->name);
			goto error;
		}
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for LOCATION node:
 *  | attr1_t(2) attr1_len(2) attr1_val(2*x) |      URL attr  (NT)
 * [| attr2_t(2) attr2_val(2) |]?                   PRIORITY attr
 * [| attr3_t(2) attr3_val(2) |]?                   CLEAR attr
 */
static inline int encode_location_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	struct sip_uri uri;
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	unsigned short nr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		/* get attribute's value */
		get_attr_val(attr->name, val, error);
		switch(attr->name[0]) {
			case 'U':
			case 'u':
				set_attr_type(p, URL_ATTR, buf_end, error);
				/* check if it's a valid SIP URL -> just call
				 * parse uri function and see if returns error ;-) */
				if(parse_uri(val.s, val.len, &uri) != 0) {
					LM_ERR("<%s> is not a valid SIP URL\n", val.s);
					goto error;
				}
				val.len++; /*copy also the \0 */
				append_str_attr(p, val, buf_end, error);
				break;
			case 'P':
			case 'p':
				set_attr_type(p, PRIORITY_ATTR, buf_end, error);
				if(val.s[0] == '0')
					nr = 0;
				else if(val.s[0] == '1')
					nr = 10;
				else
					goto prio_error;
				if(val.s[1] != '.')
					goto prio_error;
				if(val.s[2] < '0' || val.s[2] > '9')
					goto prio_error;
				nr += val.s[2] - '0';
				if(nr > 10)
					goto prio_error;
				append_short_attr(p, nr, buf_end, error);
				break;
			case 'C':
			case 'c':
				set_attr_type(p, CLEAR_ATTR, buf_end, error);
				if(val.s[0] == 'y' || val.s[0] == 'Y')
					append_short_attr(p, YES_VAL, buf_end, error);
				else
					append_short_attr(p, NO_VAL, buf_end, error);
				break;
			default:
				LM_ERR("unknown attribute <%s>\n", attr->name);
				goto error;
		}
	}

	return p - p_orig;
prio_error:
	LM_ERR("invalid priority <%s>\n", val.s);
error:
	return -1;
}


/* Attr. encoding for REMOVE_LOCATION node:
 * [| attr1_t(2) attr1_len(2) attr1_val(2*x) |]?    LOCATION attr  (NT)
 */
static inline int encode_rmvloc_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	struct sip_uri uri;
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		switch(attr->name[0]) {
			case 'L':
			case 'l':
				set_attr_type(p, LOCATION_ATTR, buf_end, error);
				/* get the value of the attribute */
				get_attr_val(attr->name, val, error);
				/* check if it's a valid SIP URL -> just call
				 * parse uri function and see if returns error ;-) */
				if(parse_uri(val.s, val.len, &uri) != 0) {
					LM_ERR("<%s> is not a valid SIP URL\n", val.s);
					goto error;
				}
				val.len++; /*copy also the \0 */
				append_str_attr(p, val, buf_end, error);
				break;
			case 'P':
			case 'p':
			case 'V':
			case 'v':
				/* as the interpreter ignores PARAM and VALUE attributes, we will
				 * do the same ;-) */
				break;
			default:
				LM_ERR("unknown attribute <%s>\n", attr->name);
				goto error;
		}
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for PROXY node:
 * [| attr1_t(2) attr1_val(2) |]?                   RECURSE attr
 * [| attr2_t(2) attr2_val(2) |]?                   TIMEOUT attr
 * [| attr3_t(2) attr3_val(2) |]?                   ORDERING attr
 */
static inline int encode_proxy_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	unsigned int nr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		/* get the value of the attribute */
		get_attr_val(attr->name, val, error);
		switch(attr->name[0]) {
			case 'R':
			case 'r':
				set_attr_type(p, RECURSE_ATTR, buf_end, error);
				if(val.s[0] == 'y' || val.s[0] == 'Y')
					append_short_attr(p, YES_VAL, buf_end, error);
				else if(val.s[0] == 'n' || val.s[0] == 'N')
					append_short_attr(p, NO_VAL, buf_end, error);
				else {
					LM_ERR("unknown value "
						   "<%s> for attribute RECURSE\n",
							val.s);
					goto error;
				}
				break;
			case 'T':
			case 't':
				set_attr_type(p, TIMEOUT_ATTR, buf_end, error);
				if(str2int(&val, &nr) == -1) {
					LM_ERR("bad value <%.*s>"
						   " for attribute TIMEOUT\n",
							val.len, val.s);
					goto error;
				}
				append_short_attr(p, (unsigned short)nr, buf_end, error);
				break;
			case 'O':
			case 'o':
				set_attr_type(p, ORDERING_ATTR, buf_end, error);
				switch(val.s[0]) {
					case 'p':
					case 'P':
						append_short_attr(p, PARALLEL_VAL, buf_end, error);
						break;
					case 'S':
					case 's':
						append_short_attr(p, SEQUENTIAL_VAL, buf_end, error);
						break;
					case 'F':
					case 'f':
						append_short_attr(p, FIRSTONLY_VAL, buf_end, error);
						break;
					default:
						LM_ERR("unknown "
							   "value <%s> for attribute ORDERING\n",
								val.s);
						goto error;
				}
				break;
			default:
				LM_ERR("unknown attribute <%s>\n", attr->name);
				goto error;
		}
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for REJECT node:
 *  | attr1_t(2) attr1_val(2) |                      STATUS attr
 * [| attr2_t(2) attr2_len(2) attr2_val(2*x)|]?      REASON attr (NT)
 */
static inline int encode_reject_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	unsigned int nr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		/* get the value of the attribute */
		get_attr_val(attr->name, val, error);
		switch(attr->name[0]) {
			case 'R':
			case 'r':
				set_attr_type(p, REASON_ATTR, buf_end, error);
				val.len++; /* grab also the /0 */
				append_str_attr(p, val, buf_end, error);
				break;
			case 'S':
			case 's':
				set_attr_type(p, STATUS_ATTR, buf_end, error);
				if(str2int(&val, &nr) == -1) {
					/*it was a non numeric value */
					if(val.len == BUSY_STR_LEN
							&& !strncasecmp(val.s, BUSY_STR, val.len)) {
						append_short_attr(p, BUSY_VAL, buf_end, error);
					} else if(val.len == NOTFOUND_STR_LEN
							  && !strncasecmp(val.s, NOTFOUND_STR, val.len)) {
						append_short_attr(p, NOTFOUND_VAL, buf_end, error);
					} else if(val.len == ERROR_STR_LEN
							  && !strncasecmp(val.s, ERROR_STR, val.len)) {
						append_short_attr(p, ERROR_VAL, buf_end, error);
					} else if(val.len == REJECT_STR_LEN
							  && !strncasecmp(val.s, REJECT_STR, val.len)) {
						append_short_attr(p, REJECT_VAL, buf_end, error);
					} else {
						LM_ERR("bad val. <%s> for STATUS\n", val.s);
						goto error;
					}
				} else if(nr < 400 || nr > 700) {
					LM_ERR("bad code <%d> for STATUS\n", nr);
					goto error;
				} else {
					append_short_attr(p, nr, buf_end, error);
				}
				break;
			default:
				LM_ERR("unknown attribute <%s>\n", attr->name);
				goto error;
		}
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for REDIRECT node:
 *  | attr1_t(2) attr1_val(2) |                      STATUS attr
 * [| attr2_t(2) attr2_len(2) attr2_val(2*x)|]?      REASON attr (NT)
 */
static inline int encode_redirect_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		if(attr->name[0] == 'p' || attr->name[0] == 'P') {
			set_attr_type(p, PERMANENT_ATTR, buf_end, error);
			/* get the value */
			get_attr_val(attr->name, val, error);
			if(val.s[0] == 'y' || val.s[0] == 'Y')
				append_short_attr(p, YES_VAL, buf_end, error);
			else if(val.s[0] == 'n' || val.s[0] == 'N')
				append_short_attr(p, NO_VAL, buf_end, error);
			else {
				LM_ERR("bad val. <%s> for PERMANENT\n", val.s);
				goto error;
			}
		} else {
			LM_ERR("unknown attribute <%s>\n", attr->name);
			goto error;
		}
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for LOG node:
 *  [| attr1_t(2) attr1_len(2) attr1_val(2*x) |]?       NAME attr  (NT)
 *  [| attr2_t(2) attr2_len(2) attr2_val(2*x) |]?       COMMENT attr (NT)
 */
static inline int encode_log_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		/* get the value of the attribute */
		get_attr_val(attr->name, val, error);
		switch(attr->name[0]) {
			case 'n':
			case 'N':
				if(val.len > MAX_NAME_SIZE)
					val.len = MAX_NAME_SIZE;
				set_attr_type(p, NAME_ATTR, buf_end, error);
				break;
			case 'c':
			case 'C':
				if(val.len > MAX_COMMENT_SIZE)
					val.len = MAX_COMMENT_SIZE;
				set_attr_type(p, COMMENT_ATTR, buf_end, error);
				break;
			default:
				LM_ERR("unknown attribute <%s>\n", attr->name);
				goto error;
		}
		/* be sure there is a \0 at the end of string */
		val.s[val.len++] = 0;
		append_str_attr(p, val, buf_end, error);
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for MAIL node:
 *   | attr1_t(2) attr1_len(2) attr1_val(2*x) |        TO_ATTR attr  (NNT)
 *  [| attr2_t(2) attr2_len(2) attr2_val(2*x) |]?      SUBJECT_ATTR attr (NNT)
 *  [| attr3_t(2) attr3_len(2) attr3_val(2*x) |]?      BODY_ATTR attr (NNT)
 */
static inline int encode_mail_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		/* there is only one attribute -> URL */
		if(attr->name[0] != 'u' && attr->name[0] != 'U') {
			LM_ERR("unknown attribute <%s>\n", attr->name);
			goto error;
		}
		p = decode_mail_url(
				p, buf_end, (char *)xmlGetProp(node, attr->name), nr_attr);
		if(p == 0)
			goto error;
	}

	return p - p_orig;
error:
	return -1;
}


/* Attr. encoding for SUBACTION node:
 */
static inline int encode_subaction_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	str val;

	FOR_ALL_ATTR(node, attr)
	{
		/* there is only one attribute -> ID */
		if((attr->name[0] | 0x20) == 'i' && ((attr->name[1] | 0x20) == 'd')
				&& attr->name[2] == 0) {
			/* get the value of the attribute */
			get_attr_val(attr->name, val, error);
			if((list = append_to_list(list, node_ptr, val.s)) == 0) {
				LM_ERR("failed to add "
					   "subaction into list -> pkg_malloc failed?\n");
				goto error;
			}
		} else {
			LM_ERR("unknown attribute <%s>\n", attr->name);
			goto error;
		}
	}

	return 0;
error:
	return -1;
}


/* Attr. encoding for SUB node:
 *   | attr1_t(2) attr1_val(2) |              REF_ATTR attr
 */
static inline int encode_sub_attr(
		xmlNodePtr node, char *node_ptr, char *buf_end)
{
	xmlAttrPtr attr;
	char *p, *p_orig;
	unsigned char *nr_attr;
	char *sub_ptr;
	str val;

	nr_attr = &(NR_OF_ATTR(node_ptr));
	*nr_attr = 0;
	p = p_orig = ATTR_PTR(node_ptr);

	FOR_ALL_ATTR(node, attr)
	{
		(*nr_attr)++;
		/* there is only one attribute -> REF */
		if(strcasecmp("ref", (char *)attr->name) != 0) {
			LM_ERR("unknown attribute <%s>\n", attr->name);
			goto error;
		}
		set_attr_type(p, REF_ATTR, buf_end, error);
		/* get the value of the attribute */
		get_attr_val(attr->name, val, error);
		if((sub_ptr = search_the_list(list, val.s)) == 0) {
			LM_ERR("unable to find declaration "
				   "of subaction <%s>\n",
					val.s);
			goto error;
		}
		append_short_attr(
				p, (unsigned short)(node_ptr - sub_ptr), buf_end, error);
	}

	return p - p_orig;
error:
	return -1;
}


/* Returns :  -1 - error
 *            >0 - subtree size of the given node
 */
int encode_node(xmlNodePtr node, char *p, char *p_end)
{
	xmlNodePtr kid;
	unsigned short sub_tree_size;
	int attr_size;
	int kid_size;
	int foo;

	/* counting the kids */
	for(kid = node->children, foo = 0; kid; kid = kid->next)
		if(kid->type == XML_ELEMENT_NODE)
			foo++;
	check_overflow(p, GET_NODE_SIZE(foo), p_end, error);
	NR_OF_KIDS(p) = foo;

	/* size of the encoded attributes */
	attr_size = 0;

	/* init the number of attributes */
	NR_OF_ATTR(p) = 0;

	/* encode node name */
	switch(node->name[0]) {
		case 'a':
		case 'A':
			switch(node->name[7]) {
				case 0:
					NODE_TYPE(p) = ADDRESS_NODE;
					attr_size = encode_address_attr(node, p, p_end);
					break;
				case '-':
					NODE_TYPE(p) = ADDRESS_SWITCH_NODE;
					attr_size = encode_address_switch_attr(node, p, p_end);
					break;
				default:
					NODE_TYPE(p) = ANCILLARY_NODE;
					break;
			}
			break;
		case 'B':
		case 'b':
			NODE_TYPE(p) = BUSY_NODE;
			break;
		case 'c':
		case 'C':
			NODE_TYPE(p) = CPL_NODE;
			break;
		case 'd':
		case 'D':
			NODE_TYPE(p) = DEFAULT_NODE;
			break;
		case 'f':
		case 'F':
			NODE_TYPE(p) = FAILURE_NODE;
			break;
		case 'i':
		case 'I':
			NODE_TYPE(p) = INCOMING_NODE;
			break;
		case 'l':
		case 'L':
			switch(node->name[2]) {
				case 'g':
				case 'G':
					NODE_TYPE(p) = LOG_NODE;
					attr_size = encode_log_attr(node, p, p_end);
					break;
				case 'o':
				case 'O':
					NODE_TYPE(p) = LOOKUP_NODE;
					attr_size = encode_lookup_attr(node, p, p_end);
					break;
				case 'c':
				case 'C':
					NODE_TYPE(p) = LOCATION_NODE;
					attr_size = encode_location_attr(node, p, p_end);
					break;
				default:
					if(node->name[8]) {
						NODE_TYPE(p) = LANGUAGE_SWITCH_NODE;
					} else {
						NODE_TYPE(p) = LANGUAGE_NODE;
						attr_size = encode_lang_attr(node, p, p_end);
					}
					break;
			}
			break;
		case 'm':
		case 'M':
			NODE_TYPE(p) = MAIL_NODE;
			attr_size = encode_mail_attr(node, p, p_end);
			break;
		case 'n':
		case 'N':
			switch(node->name[3]) {
				case 'F':
				case 'f':
					NODE_TYPE(p) = NOTFOUND_NODE;
					break;
				case 'N':
				case 'n':
					NODE_TYPE(p) = NOANSWER_NODE;
					break;
				default:
					NODE_TYPE(p) = NOT_PRESENT_NODE;
					break;
			}
			break;
		case 'o':
		case 'O':
			if(node->name[1] == 't' || node->name[1] == 'T') {
				NODE_TYPE(p) = OTHERWISE_NODE;
			} else {
				NODE_TYPE(p) = OUTGOING_NODE;
			}
			break;
		case 'p':
		case 'P':
			if(node->name[2] == 'o' || node->name[2] == 'O') {
				NODE_TYPE(p) = PROXY_NODE;
				attr_size = encode_proxy_attr(node, p, p_end);
			} else if(node->name[8]) {
				NODE_TYPE(p) = PRIORITY_SWITCH_NODE;
			} else {
				NODE_TYPE(p) = PRIORITY_NODE;
				attr_size = encode_priority_attr(node, p, p_end);
			}
			break;
		case 'r':
		case 'R':
			switch(node->name[2]) {
				case 'j':
				case 'J':
					NODE_TYPE(p) = REJECT_NODE;
					attr_size = encode_reject_attr(node, p, p_end);
					break;
				case 'm':
				case 'M':
					NODE_TYPE(p) = REMOVE_LOCATION_NODE;
					attr_size = encode_rmvloc_attr(node, p, p_end);
					break;
				default:
					if(node->name[8]) {
						NODE_TYPE(p) = REDIRECTION_NODE;
					} else {
						NODE_TYPE(p) = REDIRECT_NODE;
						attr_size = encode_redirect_attr(node, p, p_end);
					}
					break;
			}
			break;
		case 's':
		case 'S':
			switch(node->name[3]) {
				case 0:
					NODE_TYPE(p) = SUB_NODE;
					attr_size = encode_sub_attr(node, p, p_end);
					break;
				case 'c':
				case 'C':
					NODE_TYPE(p) = SUCCESS_NODE;
					break;
				case 'a':
				case 'A':
					NODE_TYPE(p) = SUBACTION_NODE;
					attr_size = encode_subaction_attr(node, p, p_end);
					break;
				default:
					if(node->name[6]) {
						NODE_TYPE(p) = STRING_SWITCH_NODE;
						attr_size = encode_string_switch_attr(node, p, p_end);
					} else {
						NODE_TYPE(p) = STRING_NODE;
						attr_size = encode_string_attr(node, p, p_end);
					}
					break;
			}
			break;
		case 't':
		case 'T':
			if(node->name[4]) {
				NODE_TYPE(p) = TIME_SWITCH_NODE;
				attr_size = encode_time_switch_attr(node, p, p_end);
			} else {
				NODE_TYPE(p) = TIME_NODE;
				attr_size = encode_time_attr(node, p, p_end);
			}
			break;
		default:
			LM_ERR("unknown node <%s>\n", node->name);
			goto error;
	}

	/* compute the total length of the node (including attributes) */
	if(attr_size < 0)
		goto error;
	sub_tree_size = SIMPLE_NODE_SIZE(p) + (unsigned short)attr_size;

	/* encrypt all the kids */
	for(kid = node->children, foo = 0; kid; kid = kid->next) {
		if(kid->type != XML_ELEMENT_NODE)
			continue;
		SET_KID_OFFSET(p, foo, sub_tree_size);
		kid_size = encode_node(kid, p + sub_tree_size, p_end);
		if(kid_size <= 0)
			goto error;
		sub_tree_size += (unsigned short)kid_size;
		foo++;
	}

	return sub_tree_size;
error:
	return -1;
}


#define BAD_XML "CPL script is not a valid XML document"
#define BAD_XML_LEN (sizeof(BAD_XML) - 1)
#define BAD_CPL "CPL script doesn't respect CPL grammar"
#define BAD_CPL_LEN (sizeof(BAD_CPL) - 1)
#define NULL_CPL "Empty CPL script"
#define NULL_CPL_LEN (sizeof(NULL_CPL) - 1)
#define ENC_ERR "Encoding of the CPL script failed"
#define ENC_ERR_LEN (sizeof(ENC_ERR) - 1)

int encodeCPL(str *xml, str *bin, str *log)
{
	static char buf[ENCONDING_BUFFER_SIZE];
	xmlDocPtr doc;
	xmlNodePtr cur;

	doc = 0;
	list = 0;

	/* reset all the logs (if any) to catch some possible err/warn/notice
	 * from the parser/validater/encoder */
	reset_logs();

	/* parse the xml */
	doc = xmlParseDoc((unsigned char *)xml->s);
	if(!doc) {
		append_log(1, MSG_ERR BAD_XML LF, MSG_ERR_LEN + BAD_XML_LEN + LF_LEN);
		LM_ERR(BAD_XML "\n");
		goto error;
	}

	/* check the xml against dtd */
	if(xmlValidateDtd(&cvp, doc, dtd) != 1) {
		append_log(1, MSG_ERR BAD_CPL LF, MSG_ERR_LEN + BAD_CPL_LEN + LF_LEN);
		LM_ERR(BAD_CPL "\n");
		goto error;
	}

	cur = xmlDocGetRootElement(doc);
	if(!cur) {
		append_log(1, MSG_ERR NULL_CPL LF, MSG_ERR_LEN + NULL_CPL_LEN + LF_LEN);
		LM_ERR(NULL_CPL "\n");
		goto error;
	}

	bin->len = encode_node(cur, buf, buf + ENCONDING_BUFFER_SIZE);
	if(bin->len < 0) {
		append_log(1, MSG_ERR ENC_ERR LF, MSG_ERR_LEN + ENC_ERR_LEN + LF_LEN);
		LM_ERR(ENC_ERR "\n");
		goto error;
	}

	xmlFreeDoc(doc);
	if(list)
		delete_list(list);
	/* compile the log buffer */
	compile_logs(log);
	bin->s = buf;
	return 1;
error:
	if(doc)
		xmlFreeDoc(doc);
	if(list)
		delete_list(list);
	/* compile the log buffer */
	compile_logs(log);
	return 0;
}


/* loads and parse the dtd file; a validating context is created */
int init_CPL_parser(char *DTD_filename)
{
	dtd = xmlParseDTD(NULL, (unsigned char *)DTD_filename);
	if(!dtd) {
		LM_ERR("DTD not parsed successfully\n");
		return -1;
	}
	cvp.userData = (void *)stderr;
	cvp.error = (xmlValidityErrorFunc) /*err_print*/ fprintf;
	cvp.warning = (xmlValidityWarningFunc) /*err_print*/ fprintf;

	return 1;
}
