/*
 * $Id$
 *
 * The initial version of this code was written by Dragos Vingarzan
 * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
 * Fruanhofer Institute. It was and still is maintained in a separate
 * branch of the original SER. We are therefore migrating it to
 * Kamailio/SR and look forward to maintaining it from here on out.
 * 2011/2012 Smile Communications, Pty. Ltd.
 * ported/maintained/improved by 
 * Jason Penton (jason(dot)penton(at)smilecoms.com and
 * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
 * effort to add full IMS support to Kamailio/SR using a new and
 * improved architecture
 * 
 * NB: Alot of this code was originally part of OpenIMSCore,
 * FhG Fokus. 
 * Copyright (C) 2004-2006 FhG Fokus
 * Thanks for great work! This is an effort to 
 * break apart the various CSCF functions into logically separate
 * components. We hope this will drive wider use. We also feel
 * that in this way the architecture is more complete and thereby easier
 * to manage in the Kamailio/SR environment
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * 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 "../../core/parser/parse_uri.h"
#include "userdata_parser.h"
#include "../../core/parser/parse_hname2.h"

int ctxtInit=0;							/**< the XML context		*/
static xmlDtdPtr dtd;
static xmlValidCtxtPtr dtdCtxt;
static xmlSchemaValidCtxtPtr xsdCtxt=0;	/**< Schema Validating context */
static xmlSchemaPtr xsd;

/**
 *	Duplicate a string into shm and trim leading&trailing spaces and surrounding quotes.
 * @param dest - destination
 * @param src - source
 */
void space_quotes_trim_dup(str *dest,char * src) {
	int i = 0;
	//right space trim
	if (src == NULL) return ;
	dest->len = strlen(src);
	i = dest->len - 1;
	while((src[i] == ' '||src[i]=='\t') && i > 0) {
		dest->len--;
		i--;
	}
	//left space trim
	i = 0;
	while((src[i] == ' '||src[i]=='\t') && i<dest->len)
		i++;

	while(i<dest->len &&(src[i]=='\"'&&src[dest->len-1]=='\"')){
		i++;
		if (i<dest->len) dest->len--;
	}

	dest->len -= i;
	if (dest->len<=0) return;
	dest->s = shm_malloc(dest->len);
	memcpy(dest->s, src+i , dest->len);
}

/**
 * Converts strings to integer values.
 * - False -> 0
 * - True  -> 1
 * @param x - input value
 * @returns int value
 */
static inline char ifc_tBool2char(xmlChar *x)
{
	int r=0;	
	while(x[r]){
		switch(x[r]){
			case '0': return 0;
			case '1': return 1;
			case 't': case 'T': return 1;
			case 'f': case 'F': return 0;
		}
		r++;
	}
	return 0;
}

/**
 * Converts strings to integer values.
 * - SESSION_CONTINUED   -> 0
 * - SESSION_TERMINATED  -> 1
 * @param x - input value
 * @returns int value
 */
static inline char ifc_tDefaultHandling2char(xmlChar *x)
{
	char r;	
	r = strtol((char*)x, (char **)NULL, 10);
	if (errno==EINVAL){
		while(x[0]){
			if (x[0]=='c'||x[0]=='C') return 0;//SESSION_CONTINUED
			if (x[0]=='r'||x[0]=='R') return 1;//SESSION_TERMINATED
			x++;
		}
		return 0;
	} 
	else return (char)r; 
}

/**
 * Converts strings to integer values.
 * - ORIGINATING_SESSION      -> 0
 * - TERMINATING_REGISTERED   -> 1
 * - TERMINATING_UNREGISTERED -> 2
 * @param x - input value
 * @returns int value
 */
static inline char ifc_tDirectionOfRequest2char(xmlChar *x)
{
	int r;	
	r = strtol((char*)x, (char **)NULL, 10);
	if (errno==EINVAL){
		while(x[0]){
			if (x[0]=='o'||x[0]=='O') return 0;//ORIGINATING_SESSION
			if (x[0]=='s'||x[0]=='S') return 1;//TERMINATING_REGISTERED
			if (x[0]=='u'||x[0]=='U') return 2;//TERMINATING_UNREGISTERED
			x++;
		}
		return 0;
	} 
	else return (char)r; 
}

/**
 * Converts strings to integer values.
 * - REGISTERED     -> 0
 * - UNREGISTERED   -> 1
 * - error (any)	 -> -1
 * @param x - input value
 * @returns int value
 */
static inline char ifc_tProfilePartIndicator2char(xmlChar *x)
{
	int r;	
	if (x==0||x[0]==0) return -1;
	r = strtol((char*)x, (char **)NULL, 10);
	if (errno==EINVAL){
		while(x[0]){
			if (x[0]=='r'||x[0]=='R') return 0;//REGISTERED
			if (x[0]=='u'||x[0]=='U') return 1;//UNREGISTERED
			x++;
		}
		return 0;
	} 
	else return (char)r; 
}

/**
 * Duplicate a string into shm and trim leading&trailing spaces.
 * @param dest - destination
 * @param src - source
 */
static inline void space_trim_dup(str *dest, char *src)
{
    	int i;
	dest->s=0;
	dest->len=0;
	if (!src) return;
	dest->len = strlen(src);
	i = dest->len-1;
	while((src[i]==' '||src[i]=='\t') && i>0) 
		i--;
	i=0;
	while((src[i]==' '||src[i]=='\t') && i<dest->len)
		i++;
	dest->len -= i;
        
	dest->s = shm_malloc(dest->len);
	if (!dest->s) {
		LM_ERR("Out of memory allocating %d bytes\n",dest->len);
		dest->len=0;
		return;
	}
	memcpy(dest->s,src+i,dest->len);
}

/**
 *      Parse an Application Server Extension.
 * @param doc - the XML document
 * @param node - the current node
 * @param as - structure to fill
 * @returns 1 on success, 0 on failure
 */
static int parse_application_server_extension(xmlDocPtr doc,xmlNodePtr node,ims_application_server *as)
{
        xmlNodePtr child;

        for(child=node->children ; child ; child=child->next)
                if (child->type==XML_ELEMENT_NODE)
                        switch (child->name[0]) {
                                case 'I':case 'i':      {//IncludeRegisterRequest / IncludeRegisterResponse
                                        switch (child->name[17]) {
                                                case 'Q':case 'q':  //IncludeRegisterRequest
                                                        as->include_register_request = 1;
                                                        break;
                                                case 'S':case 's':  //IncludeRegisterResponse
                                                        as->include_register_response = 1;
                                                        break;
                                        }
                                        break;
                                }
                        }
        return 1;
}

/**
 *	Parse an Application Server.
 * @param doc - the XML document
 * @param node - the current node
 * @param as - structure to fill
 * @returns 1 on success, 0 on failure
 */
static int parse_application_server(xmlDocPtr doc,xmlNodePtr node,ims_application_server *as)
{
	xmlNodePtr child;
	xmlChar *x;
	as->server_name.s=NULL;as->server_name.len=0;
	as->default_handling=IFC_NO_DEFAULT_HANDLING;
	as->service_info.s=NULL;as->service_info.len=0;
	as->include_register_request = 0;
	as->include_register_response = 0;

	for(child=node->children ; child ; child=child->next)
		if (child->type==XML_ELEMENT_NODE)
			switch (child->name[0]) {
				case 'S':case 's':	{//ServerName / ServiceInfo
					switch (child->name[4]) {
						case 'E':case 'e':  //ServerName
							x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
                                                        space_trim_dup(&(as->server_name),(char*)x);
							xmlFree((char*)x);
							break;
						case 'I':case 'i':  //ServiceInfo
							x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
                                                        space_trim_dup(&(as->service_info),(char*)x);
							xmlFree(x);
							break;
					}
					break;
				}
				case 'D':case 'd': //DefaultHandling
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					as->default_handling=ifc_tDefaultHandling2char(x);
					xmlFree(x);
					break;
				case 'E':case 'e': //Extension
					parse_application_server_extension(doc,child,as);
					break;
			}
	return 1;
}

/**
 *	Parse SPT for SIP Header.
 * @param doc - the XML document
 * @param node - the current node
 * @param sh - structure to fill
 * @returns 1 on success, 0 on failure
 */
static int parse_sip_header(xmlDocPtr doc,xmlNodePtr node,ims_sip_header *sh)
{
	xmlNodePtr child;
	xmlChar *x;
	char c[256];
	int len;
	struct hdr_field hf;
	sh->header.s=NULL;sh->header.len=0;
	sh->content.s=NULL;sh->content.len=0;

	for(child=node->children ; child ; child=child->next)
		if (child->type==XML_ELEMENT_NODE)
			switch (child->name[0]) {
				case 'H':case 'h':	//Header
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					len = strlen((char*)x);		
					memcpy(c,x,len);
					c[len++]=':';
					c[len]=0;
					space_trim_dup(&(sh->header),(char*)x);
					parse_hname2(c,c+(len<4?4:len),&hf);
					sh->type=(short)hf.type;
					//LOG(L_CRIT,"[%.*s(%d)]\n",sh->header.len,sh->header.s,sh->type);
					xmlFree(x);
					break;
				case 'C':case 'c':	//Content
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					space_quotes_trim_dup(&(sh->content),(char*)x);
					xmlFree(x);
					break;
			}
	return 1;
}

/**
 *	Parse SPT for Session Description.
 * @param doc - the XML document
 * @param node - the current node
 * @param sd - structure to fill
 * @returns 1 on success, 0 on failure
 */
static int parse_session_desc(xmlDocPtr doc,xmlNodePtr node,ims_session_desc *sd)
{
	xmlNodePtr child;
	xmlChar *x;
	sd->line.s=NULL;sd->line.len=0;
	sd->content.s=NULL;sd->content.len=0;

	for(child=node->children ; child ; child=child->next)
		if (child->type==XML_ELEMENT_NODE)
			switch (child->name[0]) {
				case 'L':case 'l':	//Line
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					space_trim_dup(&(sd->line),(char*)x);
					xmlFree(x);
					break;
				case 'C':case 'c':	//Content
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					space_quotes_trim_dup(&(sd->content),(char*)x);
					xmlFree(x);
					break;
			}
	return 1;
}

/**
 *	Parse a Service Point Trigger Extension (RegistrationType).
 * @param doc - the XML document
 * @param node - the current node
 * @param spt - structure to fill
 * @returns 1 on success, 0 on failure
 */
static int parse_spt_extension(xmlDocPtr doc,xmlNodePtr node,ims_spt *spt)
{
	xmlNodePtr child;
	xmlChar *x;

	for(child=node->children ; child ; child=child->next) {
		if (child->type==XML_ELEMENT_NODE && (child->name[0]=='R' || child->name[0]=='r')) {
			x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
			switch(atoi((char*)x)) {
				case 0:
					spt->registration_type |= IFC_INITIAL_REGISTRATION;
					break;
				case 1:
					spt->registration_type |= IFC_RE_REGISTRATION;
					break;
				case 2:
					spt->registration_type |= IFC_DE_REGISTRATION;
					break;
			}								
			xmlFree(x);
		}
	}
//	LOG(L_CRIT,"INFO:"M_NAME":parse_spt_extension: spt->registration_type=%d\n",spt->registration_type);
	return 1;			
}

/**
 *	Parse a Service Point Trigger.
 * @param doc - the XML document
 * @param node - the current node
 * @param spt_to - structure to fill
 * @param spt_cnt - structure to fill with the spt count
 * @returns 1 on success, 0 on failure
 */
static int parse_spt(xmlDocPtr doc,xmlNodePtr node,ims_spt *spt_to,unsigned short *spt_cnt)
{
	xmlNodePtr child,saved=0;
	xmlChar *x;

	ims_spt *spt,*spt2;
	int group;
	
	spt = spt_to + *spt_cnt;
	
	spt->condition_negated=0;
	spt->group=0;
	spt->type=IFC_UNKNOWN;
	spt->registration_type=0;

	for(child=node->children ; child ; child=child->next)
		if (child->type==XML_ELEMENT_NODE)
			switch (child->name[0]) {
				case 'C':case 'c': //ConditionNegated
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					spt->condition_negated=ifc_tBool2char(x);
					xmlFree(x);
					break;
				case 'G':case 'g': //Group
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					spt->group=atoi((char*)x);
					xmlFree(x);
					break;
				case 'R':case 'r': //RequestUri
					spt->type=IFC_REQUEST_URI;
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					space_trim_dup(&(spt->request_uri),(char*)x);
					xmlFree(x);
					break;
				case 'E':case 'e': //Extension
				    parse_spt_extension(doc,child,spt);
					break;
				case 'M':case 'm': //method
					spt->type=IFC_METHOD;
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					space_trim_dup(&(spt->method),(char*)x);
					xmlFree(x);
					break;
				case 'S':case 's': {//SIPHeader/SessionCase/SessionDescription
					switch(child->name[7]) {
						case 'E':case 'e'://SIP_HEADER
							spt->type=IFC_SIP_HEADER;
							parse_sip_header(doc,child,&(spt->sip_header));
							saved = child;
							break;
						case 'C':case 'c'://Session Case
							spt->type=IFC_SESSION_CASE;
							x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
							spt->session_case=ifc_tDirectionOfRequest2char(x);
							xmlFree(x);
							break;
						case 'D':case 'd'://Session Description
							spt->type=IFC_SESSION_DESC;
							parse_session_desc(doc,child,&(spt->session_desc));
							saved = child;
							break;
					}

				}
					break;
			}
	*spt_cnt=*spt_cnt+1;

	/* adding the other nodes for multiple groups */			
	for(child=node->children ; child ; child=child->next)
		if (child->type==XML_ELEMENT_NODE)
			switch (child->name[0]) {
				case 'G':case 'g': //Group
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					group=atoi((char*)x);
					xmlFree(x);
					if (group != spt->group){
						spt2 = spt_to + *spt_cnt;
						spt2->condition_negated = spt->condition_negated;
						spt2->group = group;
						spt2->type = spt->type;
						switch(spt2->type){
							case IFC_REQUEST_URI:
								spt2->request_uri.len = spt->request_uri.len;
								spt2->request_uri.s = shm_malloc(spt2->request_uri.len);
								if (!spt2->request_uri.s){
									LM_ERR("Out of memory allocating %d bytes\n",spt->request_uri.len);
									break;
								}
								memcpy(spt2->request_uri.s,spt->request_uri.s,spt->request_uri.len);
								break;
							case IFC_METHOD:
								spt2->method.len = spt->method.len;
								spt2->method.s = shm_malloc(spt2->method.len);
								if (!spt2->method.s){
									LM_ERR("Out of memory allocating %d bytes\n",spt->method.len);
									break;
								}
								memcpy(spt2->method.s,spt->method.s,spt->method.len);
								break;
							case IFC_SIP_HEADER:
								parse_sip_header(doc,saved,&(spt2->sip_header));
								break;
							case IFC_SESSION_CASE:
								spt2->session_case = spt->session_case;
								break;
							case IFC_SESSION_DESC:
								parse_session_desc(doc,saved,&(spt2->session_desc));
								break;								
						}
						spt2->registration_type = spt->registration_type;
						*spt_cnt = *spt_cnt+1;						
					}
					break;
			}
	return 1;			
}


/**
 *	Parse a Trigger Point.
 * @param doc - the XML document
 * @param node - the current node
 * @param tp - structure to fill
 * @returns 1 on success, 0 on failure
 * \todo An effective sort for the priority
 */
static int parse_trigger_point(xmlDocPtr doc,xmlNodePtr node,ims_trigger_point *tp)
{
	xmlNodePtr child,child2;
	xmlChar *x;
	unsigned short spt_cnt=0;
	int i,j;
	ims_spt spttemp;
	tp->condition_type_cnf=IFC_DNF;//0
	tp->spt=NULL;
	tp->spt_cnt=0;

	for(child=node->children ; child ; child=child->next)
		if (child->type==XML_ELEMENT_NODE)
			switch (child->name[0]) {
				case 'C':case 'c': //ConditionTypeCNF
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					tp->condition_type_cnf=ifc_tBool2char(x);
					xmlFree(x);
					break;
				case 'S':case 's': //SPT - Service Point Trigger
					// COUNT all in another groups
					for(child2=child->children ; child2 ; child2=child2->next)
						if (child2->type==XML_ELEMENT_NODE)
							switch (child2->name[0]) {
								case 'G':case 'g':
									spt_cnt++;
							}
					break;
			}
	tp->spt = (ims_spt*) shm_malloc(sizeof(ims_spt)*spt_cnt);
	if (!tp->spt){
		LM_ERR("Out of memory allocating %lx bytes\n",sizeof(ims_spt)*spt_cnt);
		return 0;
	}
	for(child=node->children ; child ; child=child->next)
		if (child->type==XML_ELEMENT_NODE)
			switch (child->name[0]) {
				case 'S':case 's': //SPT - Service Point Trigger
					parse_spt(doc,child,tp->spt,&(tp->spt_cnt));
					/*i=0;
					while(i<tp->spt_cnt&&tp->spt[i].group<spttemp.group)
						i++;
					for(j=tp->spt_cnt-1;j>=i;j--)
						tp->spt[j+1]=tp->spt[j];
					tp->spt[i]=spttemp;
					tp->spt_cnt++;*/
			
					break;
			}
	
	j=1;
	while(j){
		j=0;
		for(i=0;i<tp->spt_cnt-1;i++)
			if (tp->spt[i].group > tp->spt[i+1].group){
				j=1;
				spttemp = tp->spt[i];
				tp->spt[i]=tp->spt[i+1];
				tp->spt[i+1]=spttemp;
			}			
	}
	return 1;
}

/**
 *	Parse a Filter Criteria.
 * @param doc - the XML document
 * @param node - the current node
 * @param fc - structure to fill
 * @returns 1 on success, 0 on failure
 */
static int parse_filter_criteria(xmlDocPtr doc,xmlNodePtr node,ims_filter_criteria *fc)
{
	xmlNodePtr child;
	xmlChar *x;
	char k;
	fc->priority=0;
	fc->trigger_point=NULL;
	fc->profile_part_indicator=NULL;
	//fc->apllication_server init mai tarziu
	for(child=node->children ; child ; child=child->next)
		if (child->type==XML_ELEMENT_NODE)
			switch (child->name[3]) {
				case 'O':case 'o':	//Priority
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					fc->priority=atoi((char*)x);
					xmlFree(x);
					break;
				case 'G':case 'g':	//TriggerPoint
					fc->trigger_point=(ims_trigger_point*) shm_malloc(sizeof(ims_trigger_point));
					if (!fc->trigger_point){
						LM_ERR("Out of memory allocating %lx bytes\n",sizeof(ims_trigger_point));
						break;
					}
					if (!parse_trigger_point(doc,child,fc->trigger_point)){
						shm_free(fc->trigger_point);
						fc->trigger_point=0;
						return 0;
					}
					break;
				case 'L':case 'l':	//ApplicationServer
					parse_application_server(doc,child,&(fc->application_server));
					break;
				case 'F':case 'f':	//ProfilePartIndicator
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					k = ifc_tProfilePartIndicator2char(x);
					if (k<0) break;
					fc->profile_part_indicator=(char*)shm_malloc(sizeof(char));
					if (!fc->profile_part_indicator){
						LM_ERR("Out of memory allocating %lx bytes\n",sizeof(ims_trigger_point));
						break;
					}
					*fc->profile_part_indicator=k;
					xmlFree(x);
					break;
			}
	return 1;
}



/**
 * Parse the Public Identity.
 * @param doc - the XML document
 * @param root - the current node
 * @param pi - structure to fill
 * @returns 1 on success, 0 on failure , 2 if its a wildcardpsi
 */
static int parse_public_identity(xmlDocPtr doc, xmlNodePtr root, ims_public_identity *pi)
{
	xmlNodePtr child;
	xmlNodePtr grandson;
	xmlChar *x;
	int return_code=1;
	
	for(child=root->children;child;child=child->next)
		if (child->type==XML_ELEMENT_NODE)
			switch (child->name[0]){
				case 'I': case 'i':
					if (!pi->public_identity.len){
						x = xmlNodeListGetString(doc,child->xmlChildrenNode,1);
						space_trim_dup(&(pi->public_identity),(char*)x);
						xmlFree(x);
					}					
					break;
				case 'B': case 'b':
					x = xmlNodeListGetString(doc,child->xmlChildrenNode,1);
					pi->barring = ifc_tBool2char(x);
					xmlFree(x);
					break;
				//lets add something 
				case 'E' : case 'e':
					// that would be Extension
					// here i need to parse Identity Type 
					// if its two then  wildcardedpsi
					// and then extension!!!
					// I have to check how you parse this shit

					for(grandson=child->children;grandson;grandson=grandson->next)
					{
												
						if (grandson->type==XML_ELEMENT_NODE)
						{
							switch (grandson->name[0]) {
								case 'I' : case 'i':
									//identity type 0 public identity 1 distinct psi 2 wildcard psi
									//x = xmlNodeListGetString(doc,grandson->xmlChildrenNode,1);
									// i need to compare x with 2, but i have to trim leading 
									// space characters or tabs			
									//xmlFree(x);
									break;
								case 'W' : case 'w':
									//wildcardpsi
									if(!scscf_support_wildcardPSI) {
										LOG(L_ERR,"Configured without support for Wildcard PSI and got one from HSS\n");
										LOG(L_ERR,"the identity will be stored but never be matched, please include the parameter to support wildcard PSI in the config file\n");
									}
									
									x = xmlNodeListGetString(doc,grandson->xmlChildrenNode,1);
									space_trim_dup(&(pi->wildcarded_psi),(char*)x);
									
									xmlFree(x);
									return_code=2;
									break;
								default :
									break;
							}
						}
					}
										
					break;				
			}

	return return_code;
}

/**
 *	Parse a Core Network Service Authorization.
 * @param doc - the XML document
 * @param node - the current node
 * @param cn - structure to fill
 * @returns 1 on success, 0 on failure
 */
static int parse_cn_service_auth(xmlDocPtr doc,xmlNodePtr node,ims_cn_service_auth *cn)
{
	xmlNodePtr child;
	xmlChar *x;
	cn->subscribed_media_profile_id=-1;
	for(child=node->children ; child ; child=child->next)
		if (child->type==XML_ELEMENT_NODE)
			switch (child->name[0]) {
				case 'S':case 's':	//BarringIndication
					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
					cn->subscribed_media_profile_id=atoi((char*)x);
					xmlFree(x);
					return 1;
					break;

			}
	return 0;
}

/**
 *	Parse a Core Network Service Profile.
 * @param doc - the XML document
 * @param root - the current node
 * @param sp - structure to fill
 * @returns 1 on success, 0 on failure
 */
static int parse_service_profile(xmlDocPtr doc, xmlNodePtr root, ims_service_profile *sp) {
    xmlNodePtr child;
    xmlChar *x;
    unsigned short pi_cnt = 0, ifc_cnt = 0, sh_cnt = 0;
    int i, j;
    ims_filter_criteria fctemp;
    int returncode = 0;

    for (child = root->children; child; child = child->next)
        if (child->type == XML_ELEMENT_NODE) {
            LM_DBG("child name is [%s]\n", child->name);
            switch (child->name[0]) {
                case 'P': case 'p':
                    pi_cnt++;
                    break;
                case 'i':case 'I': //InitialFilterCriteria
                    ifc_cnt++;
                    break;
                case 'c':case 'C': //CoreNetworkServiceAuthorization
                    sp->cn_service_auth = (ims_cn_service_auth*) shm_malloc(
                            sizeof (ims_cn_service_auth));
                    break;
                case 's':case 'S': //SharedIFCSet
                    sh_cnt++;
                    break;

            }

        }

    sp->public_identities = shm_malloc(pi_cnt * sizeof (ims_public_identity));
    if (!sp->public_identities) {
        LM_ERR("Out of memory allocating %lx bytes\n", pi_cnt * sizeof (ims_public_identity));
        return 0;
    }
    memset(sp->public_identities, 0, pi_cnt * sizeof (ims_public_identity));
    
    sp->filter_criteria = (ims_filter_criteria*) shm_malloc(sizeof (ims_filter_criteria) * ifc_cnt);
    if (!sp->filter_criteria) {
        LM_ERR("Out of memory allocating %lx bytes\n", ifc_cnt * sizeof (ims_filter_criteria));
        return 0;
    }
    memset(sp->filter_criteria, 0, ifc_cnt * sizeof (ims_filter_criteria));
    
    sp->shared_ifc_set = (int*) shm_malloc(sizeof (int) *sh_cnt);
    if (!sp->shared_ifc_set) {
        LM_ERR("Out of memory allocating %lx bytes\n", sh_cnt * sizeof (int));
        return 0;
    }
    memset(sp->shared_ifc_set, 0, sh_cnt * sizeof (int));
    
    for (child = root->children; child; child = child->next)
        if (child->type == XML_ELEMENT_NODE)
            switch (child->name[0]) {
                case 'P': case 'p':
                    returncode = parse_public_identity(doc, child, &(sp->public_identities[sp->public_identities_cnt]));
                    if (returncode)
                        sp->public_identities_cnt++;
                    break;
                case 'I':case 'i': //InitialFilterCriteria
                    if (!parse_filter_criteria(doc, child, &(fctemp)))
                        break;
                    i = 0;
                    while (i < sp->filter_criteria_cnt && sp->filter_criteria[i].priority < fctemp.priority)
                        i++;
                    for (j = sp->filter_criteria_cnt - 1; j >= i; j--)
                        sp->filter_criteria[j + 1] = sp->filter_criteria[j];
                    sp->filter_criteria[i] = fctemp;
                    sp->filter_criteria_cnt++;
                    break;
                case 'C':case 'c': //CoreNetworkServiceAuthorization
                    if (!parse_cn_service_auth(doc, child, sp->cn_service_auth)) {
                        shm_free(sp->cn_service_auth);
                        sp->cn_service_auth = 0;
                    }
                    break;
                case 'S':case 's': //SharedIFCSet
                    x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
                    sp->shared_ifc_set[sp->shared_ifc_set_cnt++] = atoi((char*) x);
                    xmlFree(x);
                    break;
            }
    if (returncode == 2)
        return 2; // i need to know if there is a wildcardpsi hiding in the public_identity
    return 1;
}

/**
 *	Parse an IMS Subscription.
 * @param doc - the XML document
 * @param root - the current node
 * @returns the ims_subscription* on success or NULL on error
 */
static ims_subscription* parse_ims_subscription(xmlDocPtr doc, xmlNodePtr root)
{
	xmlNodePtr child;
	xmlChar *x;
	ims_subscription *s;
	unsigned short sp_cnt=0;
	int rc;
	
	if (!root) return 0;
	while(root->type!=XML_ELEMENT_NODE || strcasecmp((char*)root->name,"IMSSubscription")!=0){
		root = root->next;
	}
	if (!root) {
		LM_ERR("No IMSSubscription node found\n");
		return 0;
	}
	s = (ims_subscription*) shm_malloc(sizeof(ims_subscription));
	if (!s) {
		LM_ERR("Out of memory allocating %lx bytes\n",sizeof(ims_subscription));
		return 0;
	}
	memset(s,0,sizeof(ims_subscription));
	for(child=root->children;child;child=child->next)
		if (child->type==XML_ELEMENT_NODE)
			switch (child->name[0]){
				case 'P':case 'p':  /* Private Identity */
					if (!s->private_identity.len){
						x = xmlNodeListGetString(doc,child->xmlChildrenNode,1);
						space_trim_dup(&(s->private_identity),(char*)x);
						xmlFree(x);
					}
					break;
				case 'S':case 's':	/* Service Profile */
					sp_cnt++;
					break;					
			}
	s->service_profiles = (ims_service_profile*) shm_malloc(sp_cnt * sizeof(ims_service_profile));
	if (!s->service_profiles) {
		LM_ERR("Out of memory allocating %lx bytes\n",sp_cnt*sizeof(ims_service_profile));
		return s;	
	}
	memset(s->service_profiles,0,sp_cnt * sizeof(ims_service_profile));
	for(child=root->children;child;child=child->next)
		if (child->type==XML_ELEMENT_NODE)
			if (child->name[0]=='S' || child->name[0]=='s')
			{
				rc=parse_service_profile(doc,child,&(s->service_profiles[s->service_profiles_cnt]));
				if (rc==2)
					s->wpsi=1;
				if (rc)
					s->service_profiles_cnt++;
			}				
	s->lock = lock_alloc();
	if (s->lock==0) {
		LM_ERR("Failed to allocate Lock for IMS Subscription\n");
		shm_free(s);
		return 0;
	}
	if (lock_init(s->lock)==0){
		LM_ERR("Failed to initialize Lock for IMS Subscription\n");
		lock_dealloc(s->lock);
		s->lock=0;
		shm_free(s);
		return 0;
	}
        
        s->sl = -1; //this tells us the subscription is not linked to a list....
#ifdef EXTRA_DEBUG
    	LM_DBG("LOCK CREATED FOR SUBSCRIPTION [%.*s]: %p\n", s->private_identity.len, s->private_identity.s, s->lock);
#endif
	return s;
}


/**
 * Parses the user data XML and copies data into a new ims_subscription structure.
 * @param xml - the input xml
 * @returns the ims_subscription* on success or NULL on error
 */
ims_subscription *parse_user_data(str xml)
{
	xmlDocPtr doc=0;
	xmlNodePtr root=0;
	ims_subscription *s = 0;
	if (!ctxtInit) parser_init(scscf_user_data_dtd,scscf_user_data_xsd);	
	doc=0;
	
	doc = xmlParseMemory(xml.s, xml.len);
	if (!doc){
		LM_ERR("This is not a valid XML <%.*s>\n", xml.len,xml.s);
		goto error;
	}

	if (dtdCtxt){
		if (xmlValidateDtd(dtdCtxt,doc,dtd)!=1){
			LM_ERR("Verification of XML against DTD failed <%.*s>\n", xml.len,xml.s);
			goto error;
		}
	}
	if (xsdCtxt){
		if (xmlSchemaValidateDoc(xsdCtxt,doc)!=0){
			LM_ERR("Verification of XML against XSD failed <%.*s>\n", xml.len,xml.s);
			goto error;
		}
	}

	root = xmlDocGetRootElement(doc);
	if (!root){
		LM_ERR("Empty XML <%.*s>\n",
			xml.len,xml.s);
		goto error;
	}
	s = parse_ims_subscription(doc,root);
	if (!s){
		LM_ERR("Error while loading into  ims subscription structure\n");
		goto error;		
	}
	xmlFreeDoc(doc);
	print_user_data(s);
	return s;
error:	
	if (doc) xmlFreeDoc(doc);
	return 0;	
}

/**
 * Initializes the libxml2 parser.
 * @param dtd_filename - path to the DTD or NULL if none
 * @param xsd_filename - path to the XSD or NULL if none
 * @returns 1 on success or 0 on error
 */
int parser_init(char *dtd_filename, char *xsd_filename)
{
	if (dtd_filename){
		dtd = xmlParseDTD(NULL,(unsigned char*)dtd_filename);
		if (!dtd){
			LM_ERR("unsuccessful DTD parsing from file <%s>\n",
				dtd_filename);
			return 0;
		}
		dtdCtxt = xmlNewValidCtxt();
		dtdCtxt->userData = (void*)stderr;
		dtdCtxt->error = (xmlValidityErrorFunc) fprintf;
		dtdCtxt->warning = (xmlValidityWarningFunc) fprintf;
	}
	if (xsd_filename){
		xmlSchemaParserCtxtPtr ctxt;
		ctxt = xmlSchemaNewParserCtxt(xsd_filename);
		if (!ctxt) {
			LM_ERR("unsuccessful XSD parsing from file <%s>\n",
				xsd_filename);
			return 0;
		}
		xmlSchemaSetParserErrors(ctxt,(xmlValidityErrorFunc) fprintf,(xmlValidityWarningFunc) fprintf,stderr);
		xsd = xmlSchemaParse(ctxt);
		xmlSchemaFreeParserCtxt(ctxt);		
		
		xsdCtxt = xmlSchemaNewValidCtxt(xsd);
		xmlSchemaSetValidErrors(xsdCtxt,(xmlValidityErrorFunc) fprintf,(xmlValidityWarningFunc) fprintf,stderr);
	}
	ctxtInit=1;
	return 1;
}


/**
 * Print the contents of an ims_subscription structure.
 * @param log_level - level to log on
 * @param s - the ims_subscription to be printed
 */
void print_user_data(ims_subscription *s) {
    int i, j, k;

    LM_DBG("IMSSubscription:\n");
    if (!s) return;
    
    LM_DBG("Private Identity: <%.*s>\n", s->private_identity.len, s->private_identity.s);
    for (i = 0; i < s->service_profiles_cnt; i++) {
        LM_DBG("\tService Profile:\n");
        for (j = 0; j < s->service_profiles[i].public_identities_cnt; j++) {
            LM_DBG("\t\tPublic Identity: Barring [%d] <%.*s> \n",
                    s->service_profiles[i].public_identities[j].barring,
                    s->service_profiles[i].public_identities[j].public_identity.len,
                    s->service_profiles[i].public_identities[j].public_identity.s);
        }
        for (j = 0; j < s->service_profiles[i].filter_criteria_cnt; j++) {
            LM_DBG("\t\tFilter Criteria: Priority [%d]ProfilePartInd [%d]\n",
                    s->service_profiles[i].filter_criteria[j].priority,
                    s->service_profiles[i].filter_criteria[j].profile_part_indicator ?
                    *(s->service_profiles[i].filter_criteria[j].profile_part_indicator) : -1);
            if (s->service_profiles[i].filter_criteria[j].trigger_point) {
                LM_DBG("\t\t\tTrigger Point: CNF [%c] %s\n",
                        s->service_profiles[i].filter_criteria[j].trigger_point->condition_type_cnf ? 'X' : ' ',
                        s->service_profiles[i].filter_criteria[j].trigger_point->condition_type_cnf ? "(_|_)&(_|_)" : "(_&_)|(_&_)"
                        );
                for (k = 0; k < s->service_profiles[i].filter_criteria[j].trigger_point->spt_cnt; k++) {
                    LM_DBG("\t\t\t\tSPT: Grp[%d] NOT[%c] RegType[%d]\n",
                            s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].group,
                            s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].condition_negated ? 'X' : ' ',
                            s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].registration_type
                            );
                    switch (s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].type) {
                        case 1:
                            LM_DBG("\t\t\t\t\t Request-URI == <%.*s>\n",
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].request_uri.len,
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].request_uri.s);
                            break;
                        case 2:
                            LM_DBG("\t\t\t\t\t Method == <%.*s>\n",
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].method.len,
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].method.s);
                            break;
                        case 3:
                            LM_DBG("\t\t\t\t\t Hdr(%.*s(%d)) == <%.*s>\n",
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.header.len,
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.header.s,
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.type,
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.content.len,
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.content.s);
                            break;
                        case 4:
                            LM_DBG("\t\t\t\t\t SessionCase [%d]\n",
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_case);
                            break;
                        case 5:
                            LM_DBG("\t\t\t\t\t SDP(%.*s) == <%.*s>\n",
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_desc.line.len,
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_desc.line.s,
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_desc.content.len,
                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_desc.content.s);
                            break;
                    }
                }
            }
            LM_DBG("\t\t\tAS: <%.*s> Handling [%d] SrvInfo: <%.*s>\n",
                    s->service_profiles[i].filter_criteria[j].application_server.server_name.len,
                    s->service_profiles[i].filter_criteria[j].application_server.server_name.s,
                    s->service_profiles[i].filter_criteria[j].application_server.default_handling,
                    s->service_profiles[i].filter_criteria[j].application_server.service_info.len,
                    s->service_profiles[i].filter_criteria[j].application_server.service_info.s);
        }
        if (s->service_profiles[i].cn_service_auth) {
            LM_DBG("\t\tCN Serv Auth: Subs Media Profile ID [%d]\n",
                    s->service_profiles[i].cn_service_auth->subscribed_media_profile_id);
        }
        for (j = 0; j < s->service_profiles[i].shared_ifc_set_cnt; j++) {
            LM_DBG("\t\tShared IFC Set: [%d]\n",
                    s->service_profiles[i].shared_ifc_set[j]);
        }
    }
}

str cscf_get_realm_from_ruri(struct sip_msg *msg) {
	str realm = { 0, 0 };
	if (!msg || msg->first_line.type != SIP_REQUEST) {
		LM_ERR("This is not a request!!!\n");
		return realm;
	}

	if (!msg->parsed_orig_ruri_ok)
		if (parse_orig_ruri(msg) < 0)
			return realm;

	realm = msg->parsed_orig_ruri.host;
	return realm;
}
