/*
 * $Id$
 *
 * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
 * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
 * 
 * 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 "bin_utils.h"
#include "../../core/locking.h"

#include "../../lib/ims/useful_defs.h"
/** 
 * Whether to print debug message while encoding/decoding 
 */
#define BIN_DEBUG 0

/** 
 * Whether to do sanity checks on the available data when decoding
 * If you are crazy about start-up performance you can disable this.
 * However, this is very useful for detecting broken snapshots
 */
#define BIN_DECODE_CHECKS 1

static inline int str_shm_dup(str *dest,str *src)
{
	dest->s = shm_malloc(src->len);
	if (!dest->s){
		LM_ERR("str_shm_dup: Error allocating %d bytes\n",src->len);
		dest->len=0;
		return 0;
	}
	dest->len = src->len;
	memcpy(dest->s,src->s,src->len);
	return 1;
}


int bin_alloc(bin_data *x, int max_len)
{                                
	x->s = (char*)BIN_ALLOC_METHOD(max_len);     
	if (!x->s){
		LM_ERR("Error allocating %d bytes.\n",max_len);
		x->len=0;
		x->max=0;
		return 0;
	}
    x->len=0;
    x->max=max_len;
	return 1;
}

int bin_realloc(bin_data *x, int delta)
{
#if BIN_DEBUG
	LOG(L_INFO,"INFO:"M_NAME":bin_realloc: realloc %p from %d to + %d\n",x->s,x->max,delta);
#endif	
	x->s=BIN_REALLOC_METHOD(x->s,x->max + delta);    
	if (x->s==NULL){                             
		LM_ERR("No more memory to expand %d with %d  \n",x->max,delta);
		return 0;
	}
	x->max += delta;
	return 1;
}

int bin_expand(bin_data *x, int delta)
{
	if (x->max-x->len>=delta) return 1;
#if BIN_DEBUG	
	LOG(L_INFO,"INFO:"M_NAME":bin_realloc: realloc %p from %d to + %d\n",x->s,x->max,delta);
#endif	
	x->s=BIN_REALLOC_METHOD(x->s,x->max + delta);    
	if (x->s==NULL){                             
		LM_ERR("No more memory to expand %d with %d  \n",x->max,delta);
		return 0;
	}
	x->max += delta;
	return 1;
}

void bin_free(bin_data *x)
{
	BIN_FREE_METHOD(x->s);
	x->s=0;x->len=0;x->max=0;
}

/**
 *	simple print function 
 */
void bin_print(bin_data *x)
{
	int i,j,w=16;
	char c;
	fprintf(stderr,"----------------------------------\nBinary form %d (max %d) bytes:\n",x->len,x->max);
	for(i=0;i<x->len;i+=w){
		fprintf(stderr,"%04X> ",i);
		for(j=0;j<w;j++){
			if (i+j<x->len) fprintf(stderr,"%02X ",(unsigned char)x->s[i+j]);
			else fprintf(stderr,"   ");
		}
		printf("\t");
		for(j=0;j<w;j++)if (i+j<x->len){
			if (x->s[i+j]>32) c=x->s[i+j];
			else c = '.';
			fprintf(stderr,"%c",c);
		}else fprintf(stderr," ");
		fprintf(stderr,"\n");
	}
	fprintf(stderr,"\n---------------------------------\n");
}

/* basic data type reprezentation functions */




/**
 *	Append a char of 1 byte 
 */
int bin_encode_char(bin_data *x,char k) 
{ 
	if (!bin_expand(x,1)) return 0;
	x->s[x->len++]= k; 
#if BIN_DEBUG	
	LM_ERR("[%d]:[%.02x] new len %04x\n",k,x->s[x->len-1],x->len);
#endif
	return 1;   
}
/**
 *	Decode of 1 char
 */
int bin_decode_char(bin_data *x,char *c)
{
#if BIN_DECODE_CHECKS
	if (x->max+1 > x->len) return 0;
#endif	
	*c = x->s[x->max];
	x->max += 1;
#if BIN_DEBUG	
	LM_ERR("bin_decode_char: [%d] new pos %04x\n",*c,x->max);
#endif
	return 1;
}




/**
 *	Append an unsigned char of 1 byte 
 */
int bin_encode_uchar(bin_data *x,unsigned char k) 
{ 
	if (!bin_expand(x,1)) return 0;
	x->s[x->len++]= k; 
#if BIN_DEBUG	
	LM_ERR("bin_encode_uchar: [%u]:[%.02x] new len %04x\n",k,x->s[x->len-1],x->len);
#endif
	return 1;   
}
/**
 *	Decode of 1 unsigned char
 */
int bin_decode_uchar(bin_data *x,unsigned char *c)
{
#if BIN_DECODE_CHECKS
	if (x->max+1 > x->len) return 0;
#endif	
	*c = x->s[x->max];
	x->max += 1;
#if BIN_DEBUG	
	LM_ERR("bin_decode_uchar: [%u] new pos %04x\n",*c,x->max);
#endif
	return 1;
}







/**
 *	Append the a short  
 */
int bin_encode_short(bin_data *x,short k) 
{ 
	if (!bin_expand(x,2)) return 0;
	x->s[x->len++]=k & 0x00FF;    
	x->s[x->len++]=(k & 0xFF00) >> 8;   
#if BIN_DEBUG	
	LM_ERR("bin_encode_short: [%d]:[%.02x %.02x] new len %04x\n",k,x->s[x->len-2],x->s[x->len-1],x->len);
#endif
	return 1;   
}
/**
 *	Decode of a short
 */
int bin_decode_short(bin_data *x,short *v)
{
#if BIN_DECODE_CHECKS
	if (x->max+2 > x->len) return 0;
#endif
	*v =	(unsigned char)x->s[x->max  ]    |
	 		(unsigned char)x->s[x->max+1]<<8;
	x->max += 2;
#if BIN_DEBUG	
	LM_ERR("bin_decode_short: [%d] new pos %04x\n",*v,x->max);
#endif
	return 1;
}


/**
 *	Append the an unsigned short  
 */
int bin_encode_ushort(bin_data *x,unsigned short k) 
{ 
	if (!bin_expand(x,2)) return 0;
	x->s[x->len++]=k & 0x00FF;    
	x->s[x->len++]=(k & 0xFF00) >> 8;   
#if BIN_DEBUG	
	LM_ERR("bin_encode_ushort: [%u]:[%.02x %.02x] new len %04x\n",k,x->s[x->len-2],x->s[x->len-1],x->len);
#endif
	return 1;   
}
/**
 *	Decode of a short
 */
int bin_decode_ushort(bin_data *x,unsigned short *v)
{
#if BIN_DECODE_CHECKS
	if (x->max+2 > x->len) return 0;
#endif
	*v =	(unsigned char)x->s[x->max  ]    |
	 		(unsigned char)x->s[x->max+1]<<8;
	x->max += 2;
#if BIN_DEBUG	
	LM_ERR("bin_decode_ushort: [%u] new pos %04x\n",*v,x->max);
#endif
	return 1;
}


/**
 *	Append an integer
 */
int bin_encode_int(bin_data *x,int k) 
{ 
	int len = sizeof(int),i;
	if (!bin_expand(x,len)) return 0;
	for(i=0;i<len;i++){
		x->s[x->len++]= k & 0xFF;
		k = k>>8;          
	}
#if BIN_DEBUG		    
	switch(len){
		case 4:
			LM_ERR(":bin_encode_int: [%d]:[%.02x %.02x %.02x %.02x] new len %04x\n",k,
				x->s[x->len-4],x->s[x->len-3],x->s[x->len-2],x->s[x->len-1],x->len);
			break;
		case 8:
			LM_ERR("bin_encode_int: [%d]:[%.02x %.02x %.02x %.02x%.02x %.02x %.02x %.02x] new len %04x\n",k,
				x->s[x->len-8],x->s[x->len-7],x->s[x->len-6],x->s[x->len-5],
				x->s[x->len-4],x->s[x->len-3],x->s[x->len-2],x->s[x->len-1],
				x->len);
			break;
	}
#endif		
	return 1;   
}
/**
 *	Decode an integer
 */
int bin_decode_int(bin_data *x,int *v)
{
	int len = sizeof(int),i;
#if BIN_DECODE_CHECKS
	if (x->max+len > x->len) return 0;
#endif
	*v = 0;
	for(i=0;i<len;i++)
		*v =  *v | ((unsigned char)x->s[x->max++] <<(8*i));
#if BIN_DEBUG	
	LM_ERR("bin_decode_int: [%d] new pos %04x\n",*v,x->max);
#endif
	return 1;
}



/**
 *	Append an unsigned integer
 */
int bin_encode_uint(bin_data *x,unsigned int k) 
{ 
	int len = sizeof(unsigned int),i;
	if (!bin_expand(x,len)) return 0;
	for(i=0;i<len;i++){
		x->s[x->len++]= k & 0xFF;
		k = k>>8;          
	}
#if BIN_DEBUG		    
	switch(len){
		case 4:
			LM_ERR("bin_encode_uint: [%u]:[%.02x %.02x %.02x %.02x] new len %04x\n",k,
				x->s[x->len-4],x->s[x->len-3],x->s[x->len-2],x->s[x->len-1],x->len);
			break;
		case 8:
			LM_ERR("bin_encode_uint: [%u]:[%.02x %.02x %.02x %.02x%.02x %.02x %.02x %.02x] new len %04x\n",k,
				x->s[x->len-8],x->s[x->len-7],x->s[x->len-6],x->s[x->len-5],
				x->s[x->len-4],x->s[x->len-3],x->s[x->len-2],x->s[x->len-1],
				x->len);
			break;
	}
#endif		
	return 1;   
}
/**
 *	Decode an unsigned integer
 */
int bin_decode_uint(bin_data *x,unsigned int *v)
{
	int len = sizeof(unsigned int),i;
#if BIN_DECODE_CHECKS
	if (x->max+len > x->len) return 0;
#endif
	*v = 0;
	for(i=0;i<len;i++)
		*v =  *v | ((unsigned char)x->s[x->max++] <<(8*i));
#if BIN_DEBUG	
	LM_ERR("in_decode_uint: [%u] new pos %04x\n",*v,x->max);
#endif
	return 1;
}

/**
 *	Append a time_t structure
 */
int bin_encode_time_t(bin_data *x,time_t k) 
{ 
	int len = sizeof(time_t),i;
	if (!bin_expand(x,len)) return 0;
	for(i=0;i<len;i++){
		x->s[x->len++]= k & 0xFF;
		k = k>>8;          
	}
#if BIN_DEBUG		    
	switch(len){
		case 4:
			LM_ERR("bin_encode_time_t: [%u]:[%.02x %.02x %.02x %.02x] new len %04x\n",(unsigned int)k,
				x->s[x->len-4],x->s[x->len-3],x->s[x->len-2],x->s[x->len-1],x->len);
			break;
		case 8:
			LM_ERR("bin_encode_time_t: [%u]:[%.02x %.02x %.02x %.02x%.02x %.02x %.02x %.02x] new len %04x\n",(unsigned int)k,
				x->s[x->len-8],x->s[x->len-7],x->s[x->len-6],x->s[x->len-5],
				x->s[x->len-4],x->s[x->len-3],x->s[x->len-2],x->s[x->len-1],
				x->len);
			break;
	}
#endif		
	return 1;   
}
/**
 *	Decode an unsigned integer
 */
int bin_decode_time_t(bin_data *x,time_t *v)
{
	int len = sizeof(time_t),i;
#if BIN_DECODE_CHECKS
	if (x->max+len > x->len) return 0;
#endif
	*v = 0;
	for(i=0;i<len;i++)
		*v =  *v | ((unsigned char)x->s[x->max++] <<(8*i));
#if BIN_DEBUG	
	LM_ERR("bin_decode_time_t: [%u] new pos %04x\n",(unsigned int) *v,x->max);
#endif
	return 1;
}


/**
 *	Append a string 
 */
inline int bin_encode_str(bin_data *x,str *s) 
{ 
	if (!bin_expand(x,2+s->len)) return 0;
	if (s->len>65535) 
		LM_ERR("bin_encode_str: Possible loss of characters in encoding (string > 65535bytes) %d bytes \n",s->len);
	x->s[x->len++]=s->len & 0x000000FF;
	x->s[x->len++]=(s->len & 0x0000FF00)>>8;
	memcpy(x->s+x->len,s->s,s->len);
	x->len+=s->len;
#if BIN_DEBUG		
	LM_ERR(":bin_encode_str : [%d]:[%.02x %.02x]:[%.*s] new len %04x\n",s->len,
		x->s[x->len-s->len-2],x->s[x->len-s->len-1],s->len,s->s,x->len);
#endif		
	return 1;   
}
/**
 *	Decode of a str string
 */
int bin_decode_str(bin_data *x,str *s)
{
#if BIN_DECODE_CHECKS
	if (x->max+2 > x->len) return 0;
#endif
	s->len = (unsigned char)x->s[x->max  ]    |
	 		(unsigned char)x->s[x->max+1]<<8;
	x->max +=2;
	if (x->max+s->len>x->len) return 0;
	s->s = x->s + x->max;
	x->max += s->len;
#if BIN_DEBUG	
	LM_ERR("bin_decode_str : [%d]:[%.*s] new pos %04x\n",s->len,s->len,s->s,x->max);
#endif
	return 1;
}




/**
 *	Encode and append a Public Indentity
 * @param x - binary data to append to
 * @param pi - the public identity to encode
 * @returns 1 on succcess or 0 on error
 */
static int bin_encode_public_identity(bin_data *x,ims_public_identity *pi)
{
	if (!bin_encode_char(x,pi->barring)) goto error;
	if (!bin_encode_str(x,&(pi->public_identity))) goto error;	
	return 1;
error:
	LM_ERR("bin_encode_public_identity: Error while encoding.\n");
	return 0;		
}

/**
 *	Decode a binary string from a binary data structure
 * @param x - binary data to decode from
 * @param pi - the public identity to decode into
 * @returns 1 on succcess or 0 on error
 */
static int bin_decode_public_identity(bin_data *x,ims_public_identity *pi)
{
	str s;
	if (!bin_decode_char(x,	&(pi->barring))) goto error;
	if (!bin_decode_str(x,&s)||!str_shm_dup(&(pi->public_identity),&s))	goto error;
	
	return 1;
error:
	LM_ERR("bin_decode_public_identity: Error while decoding (at %d (%04x)).\n",x->max,x->max);
	if (pi) {
		if (pi->public_identity.s) shm_free(pi->public_identity.s);
	}
	return 0;
}

/**
 *	Encode and append a SPT
 * @param x - binary data to append to
 * @param spt - the service point trigger to encode
 * @returns 1 on succcess or 0 on error
 */
static int bin_encode_spt(bin_data *x, ims_spt *spt)
{
	unsigned char c = spt->condition_negated<<7 | spt->registration_type<<4 | spt->type;
	// cond negated, reg type, spt type
	if (!bin_encode_uchar(x,c)) goto error;

	//group
	if (!bin_encode_int(x,spt->group)) goto error;

	//spt
	switch(spt->type){
		case 1:
			if (!bin_encode_str(x,&(spt->request_uri))) goto error; 
			break;
		case 2:
			if (!bin_encode_str(x,&(spt->method))) goto error; 
			break;
		case 3:
			if (!bin_encode_short(x,spt->sip_header.type)) goto error;
			if (!bin_encode_str(x,&(spt->sip_header.header))) goto error; 
			if (!bin_encode_str(x,&(spt->sip_header.content))) goto error; 
			break;
		case 4:
			if (!bin_encode_char(x,spt->session_case)) goto error;
			break;
		case 5:
			if (!bin_encode_str(x,&(spt->session_desc.line))) goto error; 
			if (!bin_encode_str(x,&(spt->session_desc.content))) goto error; 
			break;
	}
	return 1;
error:
	LM_ERR("bin_encode_spt: Error while encoding.\n");
	return 0;		
}


/**
 *	Decode an SPT
 * @param x - binary data to decode from
 * @param spt - the service point trigger to decode into
 * @returns 1 on succcess or 0 on error
 */
static int bin_decode_spt(bin_data *x, ims_spt *spt)
{
	unsigned char k;
	str s;
	
	if (!bin_decode_uchar(x,&k)) goto error;
	
	spt->type = k & 0x0F;
	spt->condition_negated = ((k & 0x80)!=0);
	spt->registration_type = ((k & 0x70)>>4);
	
	if (!bin_decode_int(x,&(spt->group))) goto error;

	switch (spt->type){
		case 1:
			if (!bin_decode_str(x,&s)||!str_shm_dup(&(spt->request_uri),&s)) goto error;
			break;
		case 2:
			if (!bin_decode_str(x,&s)||!str_shm_dup(&(spt->method),&s)) goto error;
			break;
		case 3:
			if (!bin_decode_short(x,&(spt->sip_header.type))) goto error;
			if (!bin_decode_str(x,&s)||!str_shm_dup(&(spt->sip_header.header),&s)) goto error;
			if (!bin_decode_str(x,&s)||!str_shm_dup(&(spt->sip_header.content),&s)) goto error;
			break;
		case 4:
			if (!bin_decode_char(x,&(spt->session_case))) goto error;
			break;
		case 5:
			if (!bin_decode_str(x,&s)||!str_shm_dup(&(spt->session_desc.line),&s)) goto error;
			if (!bin_decode_str(x,&s)||!str_shm_dup(&(spt->session_desc.content),&s)) goto error;
			break;

	}
	return 1;
	
error:
	LM_ERR("bin_decode_spt: Error while decoding (at %d (%04x)).\n",x->max,x->max);
	if (spt){
		switch (spt->type){
			case 1:
				if (spt->request_uri.s) shm_free(spt->request_uri.s);
				break;
			case 2:
				if (spt->method.s) shm_free(spt->method.s);
				break;
			case 3:
				if (spt->sip_header.header.s) shm_free(spt->sip_header.header.s);
				if (spt->sip_header.header.s) shm_free(spt->sip_header.content.s);
				break;
			case 4:
				break;
			case 5:
				if (spt->sip_header.header.s) shm_free(spt->session_desc.line.s);
				if (spt->sip_header.header.s) shm_free(spt->session_desc.content.s);
				break;
		}
	}
	return 0;
}	



/**
 *	Encode and Append a Filter Criteria
 * @param x - binary data to append to
 * @param spt - the service point trigger to encode
 * @returns 1 on succcess or 0 on error
 */
static int bin_encode_filter_criteria(bin_data *x, ims_filter_criteria *fc)
{
	int i;
	char ppindicator;

	//priority
	if (!bin_encode_int(x,fc->priority)) goto error;
	
	//profile part indicator
	if (fc->profile_part_indicator) ppindicator = (*fc->profile_part_indicator)+1;
	else ppindicator = 0;
	if (!bin_encode_char(x,ppindicator)) goto error;
			
	// trigger point 
	if (fc->trigger_point) {
		if (!bin_encode_char(x,fc->trigger_point->condition_type_cnf)) goto error;
		
		if (!bin_encode_ushort(x,fc->trigger_point->spt_cnt)) goto error;
		
		for(i=0;i<fc->trigger_point->spt_cnt;i++)
			if (!bin_encode_spt(x,fc->trigger_point->spt+i)) goto error;
	} else {
		if (!bin_encode_char(x,100)) goto error;
	}
	
	//app server
	if (!bin_encode_str(x,&(fc->application_server.server_name))) goto error;
	if (!bin_encode_char(x,fc->application_server.default_handling)) goto error;
	if (!bin_encode_str(x,&(fc->application_server.service_info))) goto error;
	
	return 1;
error:
	LM_ERR("bin_encode_filter_criteria: Error while encoding.\n");
	return 0;		
}


/**
 *	Decode a Filter Criteria
 * @param x - binary data to decode from
 * @param fc - filter criteria to decode into
 * @returns 1 on succcess or 0 on error
 */
static int bin_decode_filter_criteria(bin_data *x, ims_filter_criteria *fc)
{
	int i,len;
	str s;
	char ppindicator,cnf;
	
	//priority
	if (!bin_decode_int(x,&(fc->priority))) goto error;
	
	// profile part indicator
	if (!bin_decode_char(x,&ppindicator)) goto error;
	if (!ppindicator){
		fc->profile_part_indicator = 0;
	}
	else {
		fc->profile_part_indicator = (char*)shm_malloc(sizeof(char));
		if (!fc->profile_part_indicator) {
			LM_ERR("bin_decode_filter_criteria: Error allocating %lx bytes.\n",sizeof(int));
			goto error;
		}
		*(fc->profile_part_indicator) = ppindicator-1;
	}
	
	//cnf 
	if (!bin_decode_char(x,&cnf)) goto error;

	if (cnf==100)
		fc->trigger_point=NULL;
	else {
		ims_trigger_point *tp=0;
		//trigger point
		len = sizeof(ims_trigger_point);
		tp = (ims_trigger_point*)shm_malloc(len);
		fc->trigger_point = tp;
		if (!tp) {
			LM_ERR("bin_decode_filter_criteria: Error allocating %d bytes.\n",len);
			goto error;
		}
		memset(tp,0,len);
		tp->condition_type_cnf=cnf;
		
		if (!bin_decode_ushort(x,&tp->spt_cnt)) goto error;
		len = sizeof(ims_spt)*tp->spt_cnt;
		tp->spt = (ims_spt*)shm_malloc(len);
		if (!tp->spt) {
			LM_ERR("bin_decode_filter_criteria: Error allocating %d bytes.\n",len);
			goto error;
		}
		memset(tp->spt,0,len);
		for(i=0;i<tp->spt_cnt;i++)
			if (!bin_decode_spt(x,tp->spt+i)) goto error;
	}
	//app server uri
	if (!bin_decode_str(x,&s)||!str_shm_dup(&(fc->application_server.server_name),&s)) goto error;
	// app server default handling
	if (!bin_decode_char(x,&(fc->application_server.default_handling)))goto error;
	// app server service info
	if (!bin_decode_str(x,&s)||!str_shm_dup(&(fc->application_server.service_info),&s)) goto error;

	return 1;
error:
	LM_ERR("bin_decode_filter_criteria: Error while decoding (at %d (%04x)).\n",x->max,x->max);
	if (fc){
		if (fc->trigger_point){
			if (fc->trigger_point){
				if (fc->trigger_point->spt) shm_free(fc->trigger_point->spt);
			}
			shm_free(fc->trigger_point);
		}
		if (fc->application_server.server_name.s) shm_free(fc->application_server.server_name.s);
		if (fc->application_server.service_info.s) shm_free(fc->application_server.service_info.s);
	}
	return 0;		
}



/**
 *	Encode and append a Service Profile
 * @param x - binary data to append to
 * @param sp - the service profile to encode
 * @returns 1 on succcess or 0 on error
 */
static int bin_encode_service_profile(bin_data *x,ims_service_profile *sp)
{
	int i;
		
	//public identity
	if (!bin_encode_ushort(x,sp->public_identities_cnt)) return 0;
	for(i=0;i<sp->public_identities_cnt;i++)
		if (!bin_encode_public_identity(x,sp->public_identities+i)) goto error;
	
	//filter criteria
	if (!bin_encode_ushort(x,sp->filter_criteria_cnt)) return 0;
	for(i=0;i<sp->filter_criteria_cnt;i++)
		if (!bin_encode_filter_criteria(x,sp->filter_criteria+i)) goto error;
		
	//cn service_auth
	if (sp->cn_service_auth)
		i = sp->cn_service_auth->subscribed_media_profile_id;
	else i = 0xFFFFFFFF;
	if (!bin_encode_int(x,i)) goto error;

	//shared_ifc
	if (!bin_encode_ushort(x,sp->shared_ifc_set_cnt)) return 0;
	for(i=0;i<sp->shared_ifc_set_cnt;i++)
		if (!bin_encode_int(x,sp->shared_ifc_set[i])) goto error;

	return 1;
error:
	LM_ERR("bin_encode_service_profile: Error while encoding.\n");
	return 0;		
}


/**
 *	Decode a service profile
 * @param x - binary data to decode from
 * @param sp - service profile to decode into
 * @returns 1 on succcess or 0 on error
 */
static int bin_decode_service_profile(bin_data *x, ims_service_profile *sp)
{
	int i,len;

	//public identities
	if (!bin_decode_ushort(x,&(sp->public_identities_cnt))) goto error;
	len = sizeof(ims_public_identity)*sp->public_identities_cnt;
	sp->public_identities = (ims_public_identity*)shm_malloc(len);
	if (!sp->public_identities) {
		LM_ERR("bin_decode_service_profile: Error allocating %d bytes.\n",len);
		goto error;
	}
	memset(sp->public_identities,0,len);
	for(i=0;i<sp->public_identities_cnt;i++)
		if (!bin_decode_public_identity(x,sp->public_identities+i)) goto error;
	
	// filter criteria
	if (!bin_decode_ushort(x,&(sp->filter_criteria_cnt))) goto error;	
	len = sizeof(ims_filter_criteria)*sp->filter_criteria_cnt;
	sp->filter_criteria = (ims_filter_criteria*)shm_malloc(len);
	if (!sp->filter_criteria) {
		LM_ERR("bin_decode_service_profile: Error allocating %d bytes.\n",len);
		goto error;
	}
	memset(sp->filter_criteria,0,len);
	for(i=0;i<sp->filter_criteria_cnt;i++)
		if (!bin_decode_filter_criteria(x,sp->filter_criteria+i)) goto error;

	// cn service auth
	if (!bin_decode_int(x,&i)) goto error;
	if (i==0xFFFFFFFF)
		sp->cn_service_auth = 0;
	else {
		len = sizeof(ims_cn_service_auth);
		sp->cn_service_auth = (ims_cn_service_auth*)shm_malloc(len);
		if (!sp->cn_service_auth) {
			LM_ERR("bin_decode_service_profile: Error allocating %d bytes.\n",len);
			goto error;
		}
		sp->cn_service_auth->subscribed_media_profile_id=i;
	}
	
	//shared ifc
	if (!bin_decode_ushort(x,&(sp->shared_ifc_set_cnt))) goto error;	
	len = sizeof(int)*sp->shared_ifc_set_cnt;
	sp->shared_ifc_set = (int*)shm_malloc(len);
	if (!sp->shared_ifc_set) {
		LM_ERR("bin_decode_service_profile: Error allocating %d bytes.\n",len);
		goto error;
	}
	memset(sp->shared_ifc_set,0,len);
	for(i=0;i<sp->shared_ifc_set_cnt;i++)
		if (!bin_decode_int(x,sp->shared_ifc_set+i)) goto error;

	return 1;
error:
	LM_ERR("bin_decode_service_profile: Error while decoding (at %d (%04x)).\n",x->max,x->max);
	if (sp) {
		if (sp->public_identities) shm_free(sp->public_identities);
		if (sp->filter_criteria) shm_free(sp->filter_criteria);
		if (sp->cn_service_auth) shm_free(sp->cn_service_auth);
		if (sp->shared_ifc_set) shm_free(sp->shared_ifc_set);
	}
	return 0;
}

/**
 *	Encode the entire user profile and append it to the binary data
 * @param x - binary data to append to
 * @param s - the ims subscription to encode
 * @returns 1 on succcess or 0 on error
 */
int bin_encode_ims_subscription(bin_data *x, ims_subscription *s)
{
	int i;
	if (!bin_encode_str(x,&(s->private_identity))) goto error;
	if (!bin_encode_ushort(x,s->service_profiles_cnt)) goto error;

	for(i=0;i<s->service_profiles_cnt;i++)
		if (!bin_encode_service_profile(x,s->service_profiles+i)) goto error;
	
	return 1;
error:
	LM_ERR("bin_encode_ims_subscription: Error while encoding.\n");
	return 0;	
}


/**
 *	Decode a binary string from a binary data structure
 * @param x - binary data to decode from
 * @returns the ims_subscription* where the data has been decoded
 */
ims_subscription *bin_decode_ims_subscription(bin_data *x)
{
	ims_subscription *imss=0;
	int i,len;
	str s;
	
	imss = (ims_subscription*) shm_malloc(sizeof(ims_subscription));
	if (!imss) {
		LM_ERR("bin_decode_ims_subscription: Error allocating %lx bytes.\n",sizeof(ims_subscription));
		goto error;
	}
	memset(imss,0,sizeof(ims_subscription));
	imss->sl = -1;	/* we set this to -1 as it's not currently in a slot at creation stage */
	
	if (!bin_decode_str(x,&s)||!str_shm_dup(&(imss->private_identity),&s)) goto error;
	if (!bin_decode_ushort(x,	&(imss->service_profiles_cnt))) goto error;
	
	len = sizeof(ims_service_profile)*imss->service_profiles_cnt;
	imss->service_profiles = (ims_service_profile*)shm_malloc(len);
	if (!imss->service_profiles) {
		LM_ERR("bin_decode_ims_subscription: Error allocating %d bytes.\n",len);
		goto error;
	}
	memset(imss->service_profiles,0,len);

	for(i=0;i<imss->service_profiles_cnt;i++)
		if (!bin_decode_service_profile(x,imss->service_profiles+i)) goto error;

	imss->lock = lock_alloc();
	if (imss->lock==0){
		goto error;
	}
	if (lock_init(imss->lock)==0){
		lock_dealloc(imss->lock);
		imss->lock=0;
		goto error;
	}
	imss->ref_count = 1;
#ifdef EXTRA_DEBUG
    	LM_DBG("LOCK CREATED FOR SUBSCRIPTION [%.*s]: %p\n", imss->private_identity.len, imss->private_identity.s, imss->lock);
#endif

	return imss;
error:
	LM_ERR("bin_decode_ims_subscription: Error while decoding (at %d (%04x)).\n",x->max,x->max);
	if (imss) {
		if (imss->private_identity.s) shm_free(imss->private_identity.s);
		if (imss->service_profiles) shm_free(imss->service_profiles);
		shm_free(imss);
	}
	return 0;
}

