modules/xcap_server/xcap_server.c
e6d56890
 /*
  * $Id$
  *
  * xcap_server module - builtin XCAP server
  *
  * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
  *
  * 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 
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
e6d56890
  *
  */
 
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <time.h>
 
 #include "../../lib/srdb1/db.h"
 #include "../../pt.h"
 #include "../../sr_module.h"
 #include "../../dprint.h"
 #include "../../error.h"
 #include "../../ut.h"
 #include "../../mem/mem.h"
 #include "../../data_lump.h"
 #include "../../data_lump_rpl.h"
 #include "../../mod_fix.h"
 #include "../../parser/parse_uri.h"
63a09d85
 #include "../../modules/xcap_client/xcap_callbacks.h"
dc0ba95d
 #include "../../modules/sl/sl.h"
d38e225f
 #include "../../lib/kcore/cmpapi.h"
8a00d03a
 #include "../../ip_addr.h"
e6d56890
 
142f0dbd
 #include "xcap_misc.h"
 
e6d56890
 MODULE_VERSION
 
7545c62a
 #define XCAP_TABLE_VERSION   4
e6d56890
 
 
142f0dbd
 static int xcaps_put_db(str* user, str *domain, xcap_uri_t *xuri, str *etag,
 		str* doc);
d38e225f
 static int xcaps_get_db_doc(str* user, str *domain, xcap_uri_t *xuri,
 		str *doc);
 static int xcaps_get_db_etag(str* user, str *domain, xcap_uri_t *xuri,
 		str *etag);
142f0dbd
 static int xcaps_del_db(str* user, str *domain, xcap_uri_t *xuri);
 
e6d56890
 static int w_xcaps_put(sip_msg_t* msg, char* puri, char* ppath,
 		char* pbody);
 static int w_xcaps_get(sip_msg_t* msg, char* puri, char* ppath);
 static int w_xcaps_del(sip_msg_t* msg, char* puri, char* ppath);
 static int fixup_xcaps_put(void** param, int param_no);
d38e225f
 static int check_preconditions(sip_msg_t *msg, str etag_hdr);
 static int check_match_header(str body, str *etag);
e6d56890
 
 static int mod_init(void);
 static int child_init(int rank);
 static void destroy(void);
 
142f0dbd
 int xcaps_xpath_ns_param(modparam_t type, void *val);
 
d646b4b1
 int xcaps_path_get_auid_type(str *path);
e6d56890
 int xcaps_generate_etag_hdr(str *etag);
 
 static str xcaps_db_table = str_init("xcap");
 static str xcaps_db_url = str_init(DEFAULT_DB_URL);
 static int xcaps_init_time = 0;
 static int xcaps_etag_counter = 1;
1ae80a64
 str xcaps_root = str_init("/xcap-root/");
8a00d03a
 static int xcaps_directory_scheme = -1;
 static str xcaps_directory_hostname = {0, 0};
e6d56890
 
142f0dbd
 static str xcaps_buf = {0, 8192};
4d48c5d4
 #define XCAPS_HDR_SIZE	128
 static char xcaps_hdr_buf[XCAPS_HDR_SIZE];
e6d56890
 
80b8e30b
 static str str_id_col = str_init("id");
e6d56890
 static str str_source_col = str_init("source");
 static str str_doc_col = str_init("doc");
 static str str_etag_col = str_init("etag");
 static str str_username_col = str_init("username");
 static str str_domain_col = str_init("domain");
 static str str_doc_type_col = str_init("doc_type");
 static str str_doc_uri_col = str_init("doc_uri");
 static str str_port_col = str_init("port");
 
 
 /* database connection */
 db1_con_t *xcaps_db = NULL;
 db_func_t xcaps_dbf;
 
dc0ba95d
 /** SL API structure */
 sl_api_t slb;
e6d56890
 
1ae80a64
 static pv_export_t mod_pvs[] = {
 	{ {"xcapuri", sizeof("xcapuri")-1}, PVT_OTHER, pv_get_xcap_uri,
 		pv_set_xcap_uri, pv_parse_xcap_uri_name, 0, 0, 0 },
 	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
 };
 
e6d56890
 static param_export_t params[] = {
139f0566
 	{ "db_url",             PARAM_STR, &xcaps_db_url    },
 	{ "xcap_table",         PARAM_STR, &xcaps_db_table  },
 	{ "xcap_root",          PARAM_STR, &xcaps_root  },
8a00d03a
 	{ "buf_size",           INT_PARAM, &xcaps_buf.len  },
139f0566
 	{ "xml_ns",             PARAM_STRING|USE_FUNC_PARAM, (void*)xcaps_xpath_ns_param },
8a00d03a
 	{ "directory_scheme",   INT_PARAM, &xcaps_directory_scheme },
139f0566
 	{ "directory_hostname", PARAM_STR, &xcaps_directory_hostname },
e6d56890
 	{ 0, 0, 0 }
 };
 
 static cmd_export_t cmds[]={
 	{"xcaps_put",   (cmd_function)w_xcaps_put,   3,
 			fixup_xcaps_put,  0, REQUEST_ROUTE},
 	{"xcaps_get",   (cmd_function)w_xcaps_get,   2,
 			fixup_xcaps_put,  0, REQUEST_ROUTE},
 	{"xcaps_del",   (cmd_function)w_xcaps_del,   2,
 			fixup_xcaps_put,  0, REQUEST_ROUTE},
 	{0,0,0,0,0,0}
 };
 
 
 /** module exports */
 struct module_exports exports= {
 	"xcap_server",				/* module name */
 	DEFAULT_DLFLAGS,			/* dlopen flags */
 	cmds,  						/* exported functions */
 	params,						/* exported parameters */
 	0,      					/* exported statistics */
 	0,							/* exported MI functions */
1ae80a64
 	mod_pvs,					/* exported pseudo-variables */
e6d56890
 	0,							/* extra processes */
 	mod_init,					/* module initialization function */
 	0,							/* response handling function */
 	destroy,                    /* destroy function */
 	child_init					/* per-child init function */
 };
 
 /**
  * init module function
  */
 static int mod_init(void)
 {
 
8a00d03a
 	if (xcaps_directory_scheme < -1 || xcaps_directory_scheme > 1)
 	{
 		LM_ERR("invalid xcaps_directory_scheme\n");
 		return -1;
 	}
e6d56890
 	
 	if(xcaps_buf.len<=0)
 	{
 		LM_ERR("invalid buffer size\n");
 		return -1;
 	}
 
 	xcaps_buf.s = (char*)pkg_malloc(xcaps_buf.len+1);
 	if(xcaps_buf.s==NULL)
 	{
 		LM_ERR("no pkg\n");
 		return -1;
 	}
 
 	/* binding to mysql module  */
 	if (db_bind_mod(&xcaps_db_url, &xcaps_dbf))
 	{
 		LM_ERR("Database module not found\n");
 		return -1;
 	}
 	
 	if (!DB_CAPABILITY(xcaps_dbf, DB_CAP_ALL)) {
 		LM_ERR("Database module does not implement all functions"
 				" needed by the module\n");
 		return -1;
 	}
 
 	xcaps_db = xcaps_dbf.init(&xcaps_db_url);
 	if (xcaps_db==NULL)
 	{
 		LM_ERR("connecting to database\n");
 		return -1;
 	}
 
 	if(db_check_table_version(&xcaps_dbf, xcaps_db, &xcaps_db_table,
 				XCAP_TABLE_VERSION) < 0) {
 		LM_ERR("error during table version check.\n");
 		return -1;
 	}
 	xcaps_dbf.close(xcaps_db);
 	xcaps_db = NULL;
 
dc0ba95d
 	/* bind the SL API */
 	if (sl_load_api(&slb)!=0) {
 		LM_ERR("cannot bind to SL API\n");
e6d56890
 		return -1;
 	}
 
 	xcaps_init_time = (int)time(NULL);
 	return 0;
 }
 
 /**
  *
  */
 static int child_init(int rank)
 {
 	if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
 		return 0; /* do nothing for the main process */
 
 	if((xcaps_db = xcaps_dbf.init(&xcaps_db_url))==NULL)
 	{
 		LM_ERR("cannot connect to db\n");
 		return -1;
 	}
 	return 0;
 }
 
 /**
  *
  */
 static void destroy(void)
 {
 	if(xcaps_db != NULL)
 		xcaps_dbf.close(xcaps_db);
 }
 
 
 /**
  *
  */
 static int xcaps_send_reply(sip_msg_t *msg, int code, str *reason,
 		str *hdrs, str *ctype, str *body)
 {
 	str tbuf;
 
4d48c5d4
 	if(hdrs && hdrs->len>0)
7ac74ab5
 	{
 		if (add_lump_rpl(msg, hdrs->s, hdrs->len, LUMP_RPL_HDR) == 0)
 		{
 			LM_ERR("failed to insert extra-headers lump\n");
 			return -1;
 		}
 	}
 
4d48c5d4
 	if(ctype && ctype->len>0)
e6d56890
 	{
 		/* add content-type */
 		tbuf.len=sizeof("Content-Type: ") - 1 + ctype->len + CRLF_LEN;
 		tbuf.s=pkg_malloc(sizeof(char)*(tbuf.len));
 
 		if (tbuf.len==0)
 		{
 			LM_ERR("out of pkg memory\n");
 			return -1;
 		}
 		memcpy(tbuf.s, "Content-Type: ", sizeof("Content-Type: ") - 1);
 		memcpy(tbuf.s+sizeof("Content-Type: ") - 1, ctype->s, ctype->len);
 		memcpy(tbuf.s+sizeof("Content-Type: ") - 1 + ctype->len,
 				CRLF, CRLF_LEN);
 		if (add_lump_rpl(msg, tbuf.s, tbuf.len, LUMP_RPL_HDR) == 0)
 		{
 			LM_ERR("failed to insert content-type lump\n");
 			pkg_free(tbuf.s);
 			return -1;
 		}
 		pkg_free(tbuf.s);
 	}
4d48c5d4
 	if(body && body->len>0)
e6d56890
 	{
 		if (add_lump_rpl(msg, body->s, body->len, LUMP_RPL_BODY) < 0)
 		{
 			LM_ERR("Error while adding reply lump\n");
 			return -1;
 		}
 	}
dc0ba95d
 	if (slb.freply(msg, code, reason) < 0)
e6d56890
 	{
 		LM_ERR("Error while sending reply\n");
 		return -1;
 	}
 	return 0;
 }
 
142f0dbd
 /**
  *
  */
 int xcaps_xpath_hack(str *buf, int type)
 {
 	char *match;
 	char *repl;
 	char c;
 	char *p;
 	char *start;
 
 	if(buf==NULL || buf->len <=10)
 		return 0;
 
 	if(type==0)
 	{
 		match = " xmlns=";
 		repl  = " x____=";
 	} else {
 		match = " x____=";
 		repl  = " xmlns=";
 	}
 
 	start = buf->s;
 	c = buf->s[buf->len-1];
 	buf->s[buf->len-1] = '\0';
 	while((p = strstr(start, match))!=NULL)
 	{
 		memcpy(p, repl, 7);
 		start = p + 7;
 	}
 	buf->s[buf->len-1] = c;
 	return 0;
 }
e6d56890
 
 /**
  *
  */
142f0dbd
 static int xcaps_put_db(str* user, str *domain, xcap_uri_t *xuri, str *etag,
 		str* doc)
e6d56890
 {
80b8e30b
 	db_key_t qcols[9], rcols[2], ucols[5];
 	db_val_t qvals[9], uvals[5];
 	db1_res_t *res = NULL;
 	int ncols = 0, num_ucols = 0, nrows = 0;
e6d56890
 
b111f84d
 	if(xcaps_check_doc_validity(doc)<0)
 	{
 		LM_ERR("invalid xml doc to insert in database\n");
 		goto error;
 	}
142f0dbd
 
e6d56890
 	qcols[ncols] = &str_username_col;
 	qvals[ncols].type = DB1_STR;
 	qvals[ncols].nul = 0;
 	qvals[ncols].val.str_val = *user;
 	ncols++;
 	
 	qcols[ncols] = &str_domain_col;
 	qvals[ncols].type = DB1_STR;
 	qvals[ncols].nul = 0;
 	qvals[ncols].val.str_val = *domain;
 	ncols++;
 	
 	qcols[ncols] = &str_doc_type_col;
 	qvals[ncols].type = DB1_INT;
 	qvals[ncols].nul = 0;
142f0dbd
 	qvals[ncols].val.int_val= xuri->type;
e6d56890
 	ncols++;
 
 	qcols[ncols] = &str_doc_uri_col;
 	qvals[ncols].type = DB1_STR;
 	qvals[ncols].nul = 0;
142f0dbd
 	qvals[ncols].val.str_val= xuri->adoc;
e6d56890
 	ncols++;
 
80b8e30b
 	rcols[0] = &str_id_col;
e6d56890
 
 	if (xcaps_dbf.use_table(xcaps_db, &xcaps_db_table) < 0) 
 	{
 		LM_ERR("in use_table-[table]= %.*s\n", xcaps_db_table.len,
 				xcaps_db_table.s);
 		goto error;
 	}
80b8e30b
 
 	if (xcaps_dbf.query(xcaps_db, qcols, 0, qvals, rcols, ncols, 1, 0, &res) < 0)
e6d56890
 	{
80b8e30b
 		LM_ERR("in sql query\n");
 		goto error;
 	}
 
 	nrows = RES_ROW_N(res);
 	xcaps_dbf.free_result(xcaps_db, res);
 
 	if (nrows == 0)
 	{
 		qcols[ncols] = &str_doc_col;
 		qvals[ncols].type = DB1_BLOB;
 		qvals[ncols].nul = 0;
 		qvals[ncols].val.str_val= *doc;
 		ncols++;
 
 		qcols[ncols] = &str_etag_col;
 		qvals[ncols].type = DB1_STR;
 		qvals[ncols].nul = 0;
 		qvals[ncols].val.str_val= *etag;
 		ncols++;
 
 		qcols[ncols] = &str_source_col;
 		qvals[ncols].type = DB1_INT;
 		qvals[ncols].nul = 0;
 		qvals[ncols].val.int_val = 0;
 		ncols++;
 
 		qcols[ncols] = &str_port_col;
 		qvals[ncols].type = DB1_INT;
 		qvals[ncols].nul = 0;
 		qvals[ncols].val.int_val = 0;
 		ncols++;
 
 		if(xcaps_dbf.insert(xcaps_db, qcols, qvals, ncols)< 0)
 		{
 			LM_ERR("in sql insert\n");
 			goto error;
 		}
 	}
 	else if (nrows == 1)
 	{
 		ucols[num_ucols] = &str_doc_col;
 		uvals[num_ucols].type = DB1_BLOB;
 		uvals[num_ucols].nul = 0;
 		uvals[num_ucols].val.str_val= *doc;
 		num_ucols++;
 
 		ucols[num_ucols] = &str_etag_col;
 		uvals[num_ucols].type = DB1_STR;
 		uvals[num_ucols].nul = 0;
 		uvals[num_ucols].val.str_val= *etag;
 		num_ucols++;
 
 		ucols[num_ucols] = &str_source_col;
 		uvals[num_ucols].type = DB1_INT;
 		uvals[num_ucols].nul = 0;
 		uvals[num_ucols].val.int_val = 0;
 		num_ucols++;
 
 		ucols[num_ucols] = &str_port_col;
 		uvals[num_ucols].type = DB1_INT;
 		uvals[num_ucols].nul = 0;
 		uvals[num_ucols].val.int_val = 0;
 		num_ucols++;
 
 		if (xcaps_dbf.update(xcaps_db, qcols, 0, qvals, ucols, uvals, ncols, num_ucols) < 0)
 		{
 			LM_ERR("in sql update\n");
 			goto error;
 		}
 	}
 	else
 	{
 		LM_ERR("found %d copies of the same document in XCAP Server\n", nrows);
e6d56890
 		goto error;
 	}
 
 	return 0;
 error:
 	return -1;
 }
 
8475fa8c
 static str xcaps_str_ok		= {"OK", 2};
 static str xcaps_str_srverr	= {"Server error", 12};
 static str xcaps_str_notfound	= {"Not found", 9};
 static str xcaps_str_precon	= {"Precondition Failed", 19};
 static str xcaps_str_notmod	= {"Not Modified", 12};
4d48c5d4
 static str xcaps_str_notallowed = {"Method Not Allowed", 18};
 static str xcaps_str_notimplemented = {"Not Implemented", 15};
8475fa8c
 static str xcaps_str_appxml	= {"application/xml", 15};
 static str xcaps_str_apprlxml	= {"application/resource-lists+xml", 30};
 static str xcaps_str_apprsxml	= {"application/rls-services+xml", 28};
406f43b4
 #if 0
8475fa8c
 static str xcaps_str_nocontent	= {"No content", 10};
 static str xcaps_str_appxcxml	= {"application/xcap-caps+xml", 25};
8929a642
 static str xcaps_str_appsexml	= {"application/vnd.oma.search+xml", 30};
406f43b4
 #endif
8475fa8c
 static str xcaps_str_appapxml	= {"application/auth-policy+xml", 27};
1265533a
 static str xcaps_str_appupxml	= {"application/vnd.oma.user-profile+xml", 36}; 
7264b67c
 static str xcaps_str_apppcxml	= {"application/vnd.oma.pres-content+xml", 36};
8475fa8c
 static str xcaps_str_apppdxml	= {"application/pidf+xml", 20};
4d48c5d4
 static str xcaps_str_appdrxml	= {"application/vnd.oma.xcap-directory+xml", 38};
406f43b4
 
e6d56890
 
 /**
  *
  */
 static int w_xcaps_put(sip_msg_t* msg, char* puri, char* ppath,
 		char* pbody)
 {
 	struct sip_uri turi;
 	str uri;
 	str path;
142f0dbd
 	str body = {0, 0};
2d27dd10
 	str etag = {0, 0};
 	str etag_hdr = {0, 0};
142f0dbd
 	str tbuf;
 	str nbuf = {0, 0};
4d48c5d4
 	str allow = {0, 0};
e6d56890
 	pv_elem_t *xm;
142f0dbd
 	xcap_uri_t xuri;
e6d56890
 
 	if(puri==0 || ppath==0 || pbody==0)
 	{
 		LM_ERR("invalid parameters\n");
 		goto error;
 	}
 
 	if(fixup_get_svalue(msg, (gparam_p)puri, &uri)!=0)
 	{
 		LM_ERR("unable to get uri\n");
 		goto error;
 	}
 	if(uri.s==NULL || uri.len == 0)
 	{
 		LM_ERR("invalid uri parameter\n");
 		goto error;
 	}
 
 	if(fixup_get_svalue(msg, (gparam_p)ppath, &path)!=0)
 	{
 		LM_ERR("unable to get path\n");
 		goto error;
 	}
 	if(path.s==NULL || path.len == 0)
 	{
 		LM_ERR("invalid path parameter\n");
4d48c5d4
 		return -1;
142f0dbd
 	}
 
e6d56890
 	if(parse_uri(uri.s, uri.len, &turi)!=0)
 	{
 		LM_ERR("parsing uri parameter\n");
 		goto error;
 	}
 
142f0dbd
 	if(xcap_parse_uri(&path, &xcaps_root, &xuri)<0)
e6d56890
 	{
142f0dbd
 		LM_ERR("cannot parse xcap uri [%.*s]\n",
e6d56890
 				path.len, path.s);
 		goto error;
 	}
d38e225f
 
4d48c5d4
 	switch(xuri.type)
d38e225f
 	{
4d48c5d4
 	case DIRECTORY:
 	case XCAP_CAPS:
 		allow.s = xcaps_hdr_buf;
 		allow.len = snprintf(allow.s, XCAPS_HDR_SIZE, "Allow: GET\r\n");
 		xcaps_send_reply(msg, 405, &xcaps_str_notallowed, &allow, NULL, NULL);
 		break;
 	case SEARCH:
 		allow.s = xcaps_hdr_buf;
 		allow.len = snprintf(allow.s, XCAPS_HDR_SIZE, "Allow: POST\r\n");
 		xcaps_send_reply(msg, 405, &xcaps_str_notallowed, &allow, NULL, NULL);
 		break;
 	default:
 		xm = (pv_elem_t*)pbody;
 		body.len = xcaps_buf.len - 1;
 		if(pv_printf(msg, xm, xcaps_buf.s, &body.len)<0)
142f0dbd
 		{
4d48c5d4
 			LM_ERR("unable to get body\n");
142f0dbd
 			goto error;
 		}
4d48c5d4
 		if(body.len <= 0)
142f0dbd
 		{
4d48c5d4
 			LM_ERR("invalid body parameter\n");
142f0dbd
 			goto error;
 		}
4d48c5d4
 		body.s = (char*)pkg_malloc(body.len+1);
 		if(body.s==NULL)
142f0dbd
 		{
4d48c5d4
 			LM_ERR("no more pkg\n");
142f0dbd
 			goto error;
 		}
4d48c5d4
 		memcpy(body.s, xcaps_buf.s, body.len);
 		body.s[body.len] = '\0';
 
 		xcaps_get_db_etag(&turi.user, &turi.host, &xuri, &etag);
 		if(check_preconditions(msg, etag)!=1)
 		{
 			xcaps_send_reply(msg, 412, &xcaps_str_precon, NULL, NULL, NULL);
 
 			pkg_free(body.s);
 			return -2;
 		}
 
 		if(xuri.nss!=NULL && xuri.node.len>0)
142f0dbd
 		{
4d48c5d4
 			/* partial document upload
 			 *   - fetch, update, delete and store
 			 */
 			if(xcaps_get_db_doc(&turi.user, &turi.host, &xuri, &tbuf) != 0)
 			{
 				LM_ERR("could not fetch xcap document\n");
 				goto error;
 			}
 			if(xcaps_xpath_hack(&tbuf, 0)<0)
 			{
 				LM_ERR("could not hack xcap document\n");
 				goto error;
 			}
 			if(xcaps_xpath_set(&tbuf, &xuri.node, &body, &nbuf)<0)
 			{
 				LM_ERR("could not update xcap document\n");
 				goto error;
 			}
 			if(nbuf.len<=0)
 			{
 				LM_ERR("no new content\n");
 				goto error;
 			}
 			pkg_free(body.s);
 			body = nbuf;
 			if(xcaps_xpath_hack(&body, 1)<0)
 			{
 				LM_ERR("could not hack xcap document\n");
 				goto error;
 			}
 		}
 
 		if(xcaps_generate_etag_hdr(&etag_hdr)<0)
 		{
 			LM_ERR("could not generate etag\n");
142f0dbd
 			goto error;
 		}
4d48c5d4
 		etag.s = etag_hdr.s + 7; /* 'ETag: "' */
 		etag.len = etag_hdr.len - 10; /* 'ETag: "  "\r\n' */
 		if(xcaps_put_db(&turi.user, &turi.host,
 					&xuri, &etag, &body)<0)
142f0dbd
 		{
4d48c5d4
 			LM_ERR("could not store document\n");
142f0dbd
 			goto error;
 		}
4d48c5d4
 		xcaps_send_reply(msg, 200, &xcaps_str_ok, &etag_hdr,
 					NULL, NULL);
e6d56890
 
4d48c5d4
 		if(body.s!=NULL)
 			pkg_free(body.s);
 
 		break;
e6d56890
 	}
4d48c5d4
 
e6d56890
 	return 1;
 
 error:
4d48c5d4
 	xcaps_send_reply(msg, 500, &xcaps_str_srverr, NULL, NULL, NULL);
142f0dbd
 	if(body.s!=NULL)
 		pkg_free(body.s);
e6d56890
 	return -1;
 }
 
 /**
  *
  */
d38e225f
 static int xcaps_get_db_doc(str* user, str *domain, xcap_uri_t *xuri, str *doc)
e6d56890
 {
d38e225f
 	db_key_t qcols[3];
 	db_val_t qvals[3];
e6d56890
 	int ncols = 0;
d38e225f
 	db_key_t rcols[3];
e6d56890
 	int nrcols = 0;
 	db1_res_t* db_res = NULL;
 	str s;
 
d38e225f
 	/* returned cols from table xcap*/
e6d56890
 	rcols[nrcols] = &str_doc_col;
 	nrcols++;
142f0dbd
 
e6d56890
 	/* query cols in xcap table*/
 	qcols[ncols] = &str_username_col;
 	qvals[ncols].type = DB1_STR;
 	qvals[ncols].nul = 0;
 	qvals[ncols].val.str_val = *user;
 	ncols++;
142f0dbd
 
e6d56890
 	qcols[ncols] = &str_domain_col;
 	qvals[ncols].type = DB1_STR;
 	qvals[ncols].nul = 0;
 	qvals[ncols].val.str_val = *domain;
 	ncols++;
142f0dbd
 
e6d56890
 	qcols[ncols] = &str_doc_uri_col;
 	qvals[ncols].type = DB1_STR;
 	qvals[ncols].nul = 0;
142f0dbd
 	qvals[ncols].val.str_val= xuri->adoc;
e6d56890
 	ncols++;
 
 	if (xcaps_dbf.use_table(xcaps_db, &xcaps_db_table) < 0) 
 	{
 		LM_ERR("in use_table-[table]= %.*s\n", xcaps_db_table.len,
 				xcaps_db_table.s);
 		goto error;
 	}
142f0dbd
 
e6d56890
 	if(xcaps_dbf.query(xcaps_db, qcols, NULL, qvals, rcols,
 				ncols, nrcols, NULL, &db_res)< 0)
 	{
 		LM_ERR("in sql query\n");
 		goto error;
 	}
 	if (RES_ROW_N(db_res) <= 0)
 	{
 		LM_DBG("no document\n");
 		goto notfound;
 	}
d38e225f
 
 	/* doc */
e6d56890
 	switch(RES_ROWS(db_res)[0].values[0].type)
 	{
 		case DB1_STRING:
 			s.s=(char*)RES_ROWS(db_res)[0].values[0].val.string_val;
 			s.len=strlen(s.s);
 		break;
 		case DB1_STR:
 			s.len=RES_ROWS(db_res)[0].values[0].val.str_val.len;
 			s.s=(char*)RES_ROWS(db_res)[0].values[0].val.str_val.s;
 		break;
 		case DB1_BLOB:
 			s.len=RES_ROWS(db_res)[0].values[0].val.blob_val.len;
 			s.s=(char*)RES_ROWS(db_res)[0].values[0].val.blob_val.s;
 		break;
 		default:
 			s.len=0;
 			s.s=NULL;
 	}
 	if(s.len==0)
 	{
d38e225f
 		LM_ERR("no xcap doc in db record\n");
e6d56890
 		goto error;
 	}
d38e225f
 	if(s.len>xcaps_buf.len-1)
e6d56890
 	{
d38e225f
 		LM_ERR("xcap doc buffer overflow\n");
e6d56890
 		goto error;
 	}
d38e225f
 	doc->len = s.len;
 	doc->s = xcaps_buf.s;
 	memcpy(doc->s, s.s, s.len);
 	doc->s[doc->len] = '\0';
 
b111f84d
 	if(xcaps_check_doc_validity(doc)<0)
 	{
 		LM_ERR("invalid xml doc retrieved from database\n");
 		goto error;
 	}
 
d38e225f
 	xcaps_dbf.free_result(xcaps_db, db_res);
 	return 0;
 
 notfound:
 	xcaps_dbf.free_result(xcaps_db, db_res);
 	return 1;
 
 error:
 	if(db_res!=NULL)
 		xcaps_dbf.free_result(xcaps_db, db_res);
 	return -1;
 }
 
 /**
b111f84d
  * get the etag from database record for (user@domain, xuri)
  * - return: -1 error; 0 - found; 1 - not found
d38e225f
  *
  */
 static int xcaps_get_db_etag(str* user, str *domain, xcap_uri_t *xuri, str *etag)
 {
 	db_key_t qcols[3];
 	db_val_t qvals[3];
 	int ncols = 0;
 	db_key_t rcols[3];
 	int nrcols = 0;
 	db1_res_t* db_res = NULL;
 	str s;
 
 	/* returned cols from xcap table*/
 	rcols[nrcols] = &str_etag_col;
 	nrcols++;
 
 	/* query cols in xcap table*/
 	qcols[ncols] = &str_username_col;
 	qvals[ncols].type = DB1_STR;
 	qvals[ncols].nul = 0;
 	qvals[ncols].val.str_val = *user;
 	ncols++;
 
 	qcols[ncols] = &str_domain_col;
 	qvals[ncols].type = DB1_STR;
 	qvals[ncols].nul = 0;
 	qvals[ncols].val.str_val = *domain;
 	ncols++;
 
 	qcols[ncols] = &str_doc_uri_col;
 	qvals[ncols].type = DB1_STR;
 	qvals[ncols].nul = 0;
 	qvals[ncols].val.str_val= xuri->adoc;
 	ncols++;
 
 	if (xcaps_dbf.use_table(xcaps_db, &xcaps_db_table) < 0) 
e6d56890
 	{
d38e225f
 		LM_ERR("in use_table-[table]= %.*s\n", xcaps_db_table.len,
 				xcaps_db_table.s);
e6d56890
 		goto error;
 	}
 
d38e225f
 	if(xcaps_dbf.query(xcaps_db, qcols, NULL, qvals, rcols,
 				ncols, nrcols, NULL, &db_res)< 0)
 	{
 		LM_ERR("in sql query\n");
 		goto error;
 	}
 	if (RES_ROW_N(db_res) <= 0)
 	{
 		LM_DBG("no document\n");
 		goto notfound;
 	}
 	/* etag */
 	switch(RES_ROWS(db_res)[0].values[0].type)
e6d56890
 	{
 		case DB1_STRING:
d38e225f
 			s.s=(char*)RES_ROWS(db_res)[0].values[0].val.string_val;
e6d56890
 			s.len=strlen(s.s);
 		break;
 		case DB1_STR:
d38e225f
 			s.len=RES_ROWS(db_res)[0].values[0].val.str_val.len;
 			s.s=(char*)RES_ROWS(db_res)[0].values[0].val.str_val.s;
e6d56890
 		break;
 		case DB1_BLOB:
d38e225f
 			s.len=RES_ROWS(db_res)[0].values[0].val.blob_val.len;
 			s.s=(char*)RES_ROWS(db_res)[0].values[0].val.blob_val.s;
e6d56890
 		break;
 		default:
 			s.len=0;
 			s.s=NULL;
 	}
 	if(s.len==0)
 	{
d38e225f
 		LM_ERR("no etag in db record\n");
e6d56890
 		goto error;
 	}
4d48c5d4
 	etag->len = snprintf(xcaps_hdr_buf, XCAPS_HDR_SIZE,
d38e225f
 			"ETag: \"%.*s\"\r\n", s.len, s.s);
 	if(etag->len < 0)
e6d56890
 	{
d38e225f
 		LM_ERR("error printing etag hdr\n ");
 		goto error;
 	}
4d48c5d4
 	if(etag->len >= XCAPS_HDR_SIZE)
d38e225f
 	{
 		LM_ERR("etag buffer overflow\n");
e6d56890
 		goto error;
 	}
d38e225f
 
4d48c5d4
 	etag->s = xcaps_hdr_buf;
d38e225f
 	etag->s[etag->len] = '\0';
e6d56890
 
 	xcaps_dbf.free_result(xcaps_db, db_res);
 	return 0;
 
 notfound:
 	xcaps_dbf.free_result(xcaps_db, db_res);
 	return 1;
 
 error:
 	if(db_res!=NULL)
 		xcaps_dbf.free_result(xcaps_db, db_res);
 	return -1;
 }
 
4d48c5d4
 static int xcaps_get_directory(struct sip_msg *msg, str *user, str *domain, str *directory)
 {
 	db_key_t qcols[2];
 	db_val_t qvals[2], *values;
 	db_key_t rcols[3];
 	db_row_t *rows;
 	db1_res_t* db_res = NULL;
 	int n_qcols = 0, n_rcols = 0;
 	int i, cur_type = 0, cur_pos = 0;
 	int doc_type_col, doc_uri_col, etag_col;
 	str auid_string = {0, 0};
 	struct hdr_field *hdr = msg->headers;
 	str server_name = {0, 0};
 
 	qcols[n_qcols] = &str_username_col;
 	qvals[n_qcols].type = DB1_STR;
 	qvals[n_qcols].nul = 0;
 	qvals[n_qcols].val.str_val = *user;
 	n_qcols++;
 	
 	qcols[n_qcols] = &str_domain_col;
 	qvals[n_qcols].type = DB1_STR;
 	qvals[n_qcols].nul = 0;
 	qvals[n_qcols].val.str_val = *domain;
 	n_qcols++;
 
 	rcols[doc_type_col = n_rcols++] = &str_doc_type_col;
 	rcols[doc_uri_col = n_rcols++] = &str_doc_uri_col;
 	rcols[etag_col = n_rcols++] = &str_etag_col;
 
 	if (xcaps_dbf.use_table(xcaps_db, &xcaps_db_table) < 0) 
 	{
 		LM_ERR("in use_table-[table]= %.*s\n", xcaps_db_table.len,
 				xcaps_db_table.s);
 		goto error;
 	}
 
 	if (xcaps_dbf.query(xcaps_db, qcols, 0, qvals, rcols, n_qcols,
 				n_rcols, &str_doc_type_col, &db_res) < 0)
 	{
 		LM_ERR("in sql query\n");
 		goto error;
 
 	}
 
 	if (db_res == NULL)
 		goto error;
 
 	directory->s = xcaps_buf.s;
 	directory->len = 0;
 
8a00d03a
 	directory->len += snprintf(directory->s + directory->len,
 					xcaps_buf.len - directory->len,
 			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
 			"<xcap-directory xmlns=\"urn:oma:xml:xdm:xcap-directory\">\r\n");
4d48c5d4
 
 	rows = RES_ROWS(db_res);
 	for (i = 0; i < RES_ROW_N(db_res); i++)
 	{
 		values = ROW_VALUES(&rows[i]);
 
 		if (cur_type != VAL_INT(&values[doc_type_col]))
 		{
 			if (cur_type != 0)
 			{
8a00d03a
 				directory->len += snprintf(directory->s + directory->len,
 								xcaps_buf.len - directory->len,
 			"</folder>\r\n");
4d48c5d4
 			}
 			cur_type = VAL_INT(&values[doc_type_col]);
 
 			memset(&auid_string, 0, sizeof(str));
 			while(xcaps_auid_list[cur_pos].auid.s != NULL)
 			{
 				if (xcaps_auid_list[cur_pos].type == cur_type)
 				{
 					auid_string.s = xcaps_auid_list[cur_pos].auid.s;
 					auid_string.len = xcaps_auid_list[cur_pos].auid.len;
 					break;
 				}
 				cur_pos++;
 			}
 
 			if (auid_string.s == NULL)
 			{
 				goto error;
 			}
 
8a00d03a
 			directory->len += snprintf(directory->s + directory->len,
 							xcaps_buf.len - directory->len,
 			"<folder auid=\"%.*s\">\r\n",
 							auid_string.len, auid_string.s);
4d48c5d4
 		}
 
8a00d03a
 		switch(xcaps_directory_scheme)
 		{
 		case -1:
 			directory->len += snprintf(directory->s + directory->len,
 							xcaps_buf.len - directory->len,
 			"<entry uri=\"%s://", msg->rcv.proto == PROTO_TLS ? "https" : "http");
 			break;
 		case 0:
 			directory->len += snprintf(directory->s + directory->len,
 							xcaps_buf.len - directory->len,
 			"<entry uri=\"http://");
 			break;
 		case 1:
 			directory->len += snprintf(directory->s + directory->len,
 							xcaps_buf.len - directory->len,
 			"<entry uri=\"https://");
 			break;
 		}
 
 		if (xcaps_directory_hostname.len > 0)
 		{
 			directory->len += snprintf(directory->s + directory->len,
 						xcaps_buf.len - directory->len,
 			"%.*s", xcaps_directory_hostname.len, xcaps_directory_hostname.s);
 		}
 		else
 		{
 			if (parse_headers(msg, HDR_EOH_F, 0) < 0)
 			{
 				LM_ERR("error parsing headers\n");
 				goto error;
 			}
 
 			while (hdr != NULL)
 			{
 				if (cmp_hdrname_strzn(&hdr->name, "Host", 4) == 0)
 				{
 					server_name = hdr->body;
 					break;
 				}
 				hdr = hdr->next;
 			}
 
 			if (server_name.len > 0)
 			{
 				directory->len += snprintf(directory->s + directory->len,
 							xcaps_buf.len - directory->len,
 			"%.*s", server_name.len, server_name.s);
 			}
 			else
 			{
 				server_name.s = pkg_malloc(IP6_MAX_STR_SIZE + 6);
 				server_name.len = ip_addr2sbuf(&msg->rcv.dst_ip, server_name.s, IP6_MAX_STR_SIZE);
 				directory->len += snprintf(directory->s + directory->len,
 							xcaps_buf.len - directory->len,
 			"%.*s:%d", server_name.len, server_name.s, msg->rcv.dst_port);
 				pkg_free(server_name.s);
 			}
 		}
 
 		directory->len += snprintf(directory->s + directory->len,
 						xcaps_buf.len - directory->len,
 		"%s\" etag=\"%s\"/>\r\n",
4d48c5d4
 					VAL_STRING(&values[doc_uri_col]),
 					VAL_STRING(&values[etag_col]));
 	}
 
 	if (cur_type != 0)
 	{
 		directory->len += snprintf(directory->s + directory->len, xcaps_buf.len - directory->len,
 		"</folder>\r\n");
 	}
 	directory->len += snprintf(directory->s + directory->len, xcaps_buf.len - directory->len,
 		"</xcap-directory>");
 
 	if (db_res != NULL)
 		xcaps_dbf.free_result(xcaps_db, db_res);
 
 	return 0;
 
 error:
 	if (db_res != NULL)
 		xcaps_dbf.free_result(xcaps_db, db_res);
 	return -1;
 }
 
e6d56890
 /**
  *
  */
 static int w_xcaps_get(sip_msg_t* msg, char* puri, char* ppath)
 {
 	struct sip_uri turi;
 	str uri;
 	str path;
2d27dd10
 	str etag = {0, 0};
6b8b8a05
 	str body = {0, 0};
 	str new_body = {0, 0};
e6d56890
 	int ret = 0;
142f0dbd
 	xcap_uri_t xuri;
406f43b4
 	str *ctype;
4d48c5d4
 	str allow;
e6d56890
 
 	if(puri==0 || ppath==0)
 	{
 		LM_ERR("invalid parameters\n");
 		return -1;
 	}
 
 	if(fixup_get_svalue(msg, (gparam_p)puri, &uri)!=0)
 	{
 		LM_ERR("unable to get uri\n");
 		return -1;
 	}
 	if(uri.s==NULL || uri.len == 0)
 	{
 		LM_ERR("invalid uri parameter\n");
 		return -1;
 	}
 
 	if(fixup_get_svalue(msg, (gparam_p)ppath, &path)!=0)
 	{
 		LM_ERR("unable to get path\n");
 		return -1;
 	}
 	if(path.s==NULL || path.len == 0)
 	{
 		LM_ERR("invalid path parameter\n");
 		return -1;
 	}
 
 	if(parse_uri(uri.s, uri.len, &turi)!=0)
 	{
 		LM_ERR("parsing uri parameter\n");
 		goto error;
 	}
 
142f0dbd
 	if(xcap_parse_uri(&path, &xcaps_root, &xuri)<0)
 	{
 		LM_ERR("cannot parse xcap uri [%.*s]\n",
 				path.len, path.s);
 		goto error;
 	}
 
4d48c5d4
 	switch(xuri.type)
d38e225f
 	{
4d48c5d4
 	case DIRECTORY:
 		if (strncmp(xuri.file.s, "directory.xml", xuri.file.len) == 0)
 		{
 			if (xcaps_get_directory(msg, &turi.user, &turi.host, &body) < 0)
 				goto error;
 
 			xcaps_send_reply(msg, 200, &xcaps_str_ok, NULL, &xcaps_str_appdrxml, &body);
 		}
 		else
 		{
 			xcaps_send_reply(msg, 404, &xcaps_str_notfound, NULL, NULL, NULL);
 		}
 		break;
 	case XCAP_CAPS:
 		xcaps_send_reply(msg, 501, &xcaps_str_notimplemented, NULL, NULL, NULL);
 		break;
 	case SEARCH:
 		allow.s = xcaps_hdr_buf;
 		allow.len = snprintf(allow.s, XCAPS_HDR_SIZE, "Allow: POST\r\n");
 		xcaps_send_reply(msg, 405, &xcaps_str_notallowed, &allow, NULL, NULL);
 		break;
 	default:
 		if((ret=xcaps_get_db_etag(&turi.user, &turi.host, &xuri, &etag))<0)
 		{ 
 			LM_ERR("could not fetch etag for xcap document\n");
 			goto error;
 		}
 		if (ret==1)
 		{
 			/* doc not found */
 			xcaps_send_reply(msg, 404, &xcaps_str_notfound, NULL,
 					NULL, NULL);
 			return 1;
 		}
d38e225f
 	
4d48c5d4
 		if((ret=check_preconditions(msg, etag))==-1)
 		{
 			xcaps_send_reply(msg, 412, &xcaps_str_precon, NULL,
 					NULL, NULL);
 			return -2;
 		} else if (ret==-2) {
 			xcaps_send_reply(msg, 304, &xcaps_str_notmod, NULL,
 					NULL, NULL);
 			return -2;
 		}
d38e225f
 
4d48c5d4
 		if((ret=xcaps_get_db_doc(&turi.user, &turi.host, &xuri, &body))<0)
 		{
 			LM_ERR("could not fetch xcap document\n");
 			goto error;
 		}
6b8b8a05
 		if(ret!=0)
4d48c5d4
 		{
 			/* doc not found */
 			xcaps_send_reply(msg, 404, &xcaps_str_notfound, NULL,
 					NULL, NULL);
6b8b8a05
 			break;
4d48c5d4
 		}
 
6b8b8a05
 		if(xuri.nss!=NULL && xuri.node.len>0)
 		{
 			if((new_body.s = pkg_malloc(body.len))==NULL)
 			{
 				LM_ERR("allocating package memory\n");
 				goto error;
 			}
 			new_body.len = body.len;
 			
 			if(xcaps_xpath_hack(&body, 0)<0)
 			{
 				LM_ERR("could not hack xcap document\n");
 				goto error;
 			}
 			if(xcaps_xpath_get(&body, &xuri.node, &new_body)<0)
 			{
 				LM_ERR("could not retrieve element from xcap document\n");
 				goto error;
 			}
 			if(new_body.len<=0)
 			{
 				/* element not found */
 				xcaps_send_reply(msg, 404, &xcaps_str_notfound, NULL,
 					NULL, NULL);
 				pkg_free(new_body.s);
 				new_body.s = NULL;
 				break;
 			}
 			if(xcaps_xpath_hack(&new_body, 1)<0)
 			{
 				LM_ERR("could not hack xcap document\n");
 				goto error;
 			}
 			memcpy(body.s, new_body.s, new_body.len);
 			body.len = new_body.len;
 			pkg_free(new_body.s);
 			new_body.s = NULL;
 		}
 
 		/* doc or element found */
 		ctype = &xcaps_str_appxml;
 		if(xuri.type==RESOURCE_LIST)
 			ctype = &xcaps_str_apprlxml;
 		else if(xuri.type==PRES_RULES)
 			ctype = &xcaps_str_appapxml;
 		else if(xuri.type==RLS_SERVICE)
 			ctype = &xcaps_str_apprsxml;
 		else if(xuri.type==USER_PROFILE)
 			ctype = &xcaps_str_appupxml;
 		else if(xuri.type==PRES_CONTENT)
 			ctype = &xcaps_str_apppcxml;
 		else if(xuri.type==PIDF_MANIPULATION)
 			ctype = &xcaps_str_apppdxml;
 		xcaps_send_reply(msg, 200, &xcaps_str_ok, &etag,
 				ctype, &body);
 
4d48c5d4
 		break;
e6d56890
 	}
4d48c5d4
 
e6d56890
 	return 1;
 
 error:
6b8b8a05
 	if (new_body.s) pkg_free(new_body.s);
4d48c5d4
 	xcaps_send_reply(msg, 500, &xcaps_str_srverr, NULL,
 				NULL, NULL);
e6d56890
 	return -1;
 }
 
 
 /**
  *
  */
142f0dbd
 static int xcaps_del_db(str* user, str *domain, xcap_uri_t *xuri)
e6d56890
 {
 	db_key_t qcols[4];
 	db_val_t qvals[4];
 	int ncols = 0;
 
 	/* delete in xcap table*/
 	qcols[ncols] = &str_username_col;
 	qvals[ncols].type = DB1_STR;
 	qvals[ncols].nul = 0;
 	qvals[ncols].val.str_val = *user;
 	ncols++;
 	
 	qcols[ncols] = &str_domain_col;
 	qvals[ncols].type = DB1_STR;
 	qvals[ncols].nul = 0;
 	qvals[ncols].val.str_val = *domain;
 	ncols++;
 	
 	qcols[ncols] = &str_doc_uri_col;
 	qvals[ncols].type = DB1_STR;
 	qvals[ncols].nul = 0;
142f0dbd
 	qvals[ncols].val.str_val= xuri->adoc;
e6d56890
 	ncols++;
 
 	if (xcaps_dbf.use_table(xcaps_db, &xcaps_db_table) < 0) 
 	{
 		LM_ERR("in use_table-[table]= %.*s\n", xcaps_db_table.len,
 				xcaps_db_table.s);
 		goto error;
 	}
 	
 	if(xcaps_dbf.delete(xcaps_db, qcols, NULL, qvals, ncols)< 0)
 	{
 		LM_ERR("in sql delete\n");
 		goto error;
 	}
 
 	return 0;
 error:
 	return -1;
 }
 
 
 /**
  *
  */
 static int w_xcaps_del(sip_msg_t* msg, char* puri, char* ppath)
 {
 	struct sip_uri turi;
 	str uri;
 	str path;
142f0dbd
 	xcap_uri_t xuri;
b111f84d
 	str body = {0, 0};
 	str etag_hdr = {0, 0};
 	str etag = {0, 0};
142f0dbd
 	str tbuf;
4d48c5d4
 	str allow = {0, 0};
e6d56890
 
 	if(puri==0 || ppath==0)
 	{
 		LM_ERR("invalid parameters\n");
 		return -1;
 	}
 
 	if(fixup_get_svalue(msg, (gparam_p)puri, &uri)!=0)
 	{
 		LM_ERR("unable to get uri\n");
 		return -1;
 	}
 	if(uri.s==NULL || uri.len == 0)
 	{
 		LM_ERR("invalid uri parameter\n");
 		return -1;
 	}
 
 	if(fixup_get_svalue(msg, (gparam_p)ppath, &path)!=0)
 	{
 		LM_ERR("unable to get path\n");
 		return -1;
 	}
 	if(path.s==NULL || path.len == 0)
 	{
 		LM_ERR("invalid path parameter\n");
 		return -1;
 	}
 
 	if(parse_uri(uri.s, uri.len, &turi)!=0)
 	{
 		LM_ERR("parsing uri parameter\n");
 		goto error;
 	}
 
142f0dbd
 	if(xcap_parse_uri(&path, &xcaps_root, &xuri)<0)
e6d56890
 	{
142f0dbd
 		LM_ERR("cannot parse xcap uri [%.*s]\n",
 				path.len, path.s);
e6d56890
 		goto error;
 	}
142f0dbd
 
4d48c5d4
 	switch(xuri.type)
d38e225f
 	{
4d48c5d4
 	case DIRECTORY:
 	case XCAP_CAPS:
 		allow.s = xcaps_hdr_buf;
 		allow.len = snprintf(allow.s, XCAPS_HDR_SIZE, "Allow: GET\r\n");
 		xcaps_send_reply(msg, 405, &xcaps_str_notallowed, &allow, NULL, NULL);
 		break;
 	case SEARCH:
 		allow.s = xcaps_hdr_buf;
 		allow.len = snprintf(allow.s, XCAPS_HDR_SIZE, "Allow: POST\r\n");
 		xcaps_send_reply(msg, 405, &xcaps_str_notallowed, &allow, NULL, NULL);
 		break;
 	default:
 		if(xcaps_get_db_etag(&turi.user, &turi.host, &xuri, &etag)!=0)
 		{ 
 			LM_ERR("could not fetch etag for xcap document\n");
142f0dbd
 			goto error;
 		}
4d48c5d4
 
 		if(check_preconditions(msg, etag)!=1)
142f0dbd
 		{
4d48c5d4
 			xcaps_send_reply(msg, 412, &xcaps_str_precon, NULL,
 					NULL, NULL);
 			return -2;
142f0dbd
 		}
4d48c5d4
 
 		if(xuri.nss==NULL)
142f0dbd
 		{
4d48c5d4
 			/* delete document */
 			if(xcaps_del_db(&turi.user, &turi.host, &xuri)<0)
 			{
 				LM_ERR("could not delete document\n");
 				goto error;
 			}
 			xcaps_send_reply(msg, 200, &xcaps_str_ok, NULL,
 					NULL, NULL);
 		} else {
 			/* delete element */
 			if(xcaps_get_db_doc(&turi.user, &turi.host, &xuri, &tbuf) != 0)
 			{
 				LM_ERR("could not fetch xcap document\n");
 				goto error;
 			}
 			if(xcaps_xpath_hack(&tbuf, 0)<0)
 			{
 				LM_ERR("could not hack xcap document\n");
 				goto error;
 			}
 			if(xcaps_xpath_set(&tbuf, &xuri.node, NULL, &body)<0)
 			{
 				LM_ERR("could not update xcap document\n");
 				goto error;
 			}
 			if(body.len<=0)
 			{
 				LM_ERR("no new content\n");
 				goto error;
 			}
 			if(xcaps_xpath_hack(&body, 1)<0)
 			{
 				LM_ERR("could not hack xcap document\n");
 				goto error;
 			}
 			if(xcaps_generate_etag_hdr(&etag_hdr)<0)
 			{
 				LM_ERR("could not generate etag\n");
 				goto error;
 			}
 			etag.s = etag_hdr.s + 7; /* 'ETag: "' */
 			etag.len = etag_hdr.len - 10; /* 'ETag: "  "\r\n' */
 			if(xcaps_put_db(&turi.user, &turi.host,
 					&xuri, &etag, &body)<0)
 			{
 				LM_ERR("could not store document\n");
 				goto error;
 			}
 			xcaps_send_reply(msg, 200, &xcaps_str_ok, &etag_hdr,
 					NULL, NULL);
 			if(body.s!=NULL)
 				pkg_free(body.s);
142f0dbd
 		}
4d48c5d4
 
 		break;
142f0dbd
 	}
4d48c5d4
 
e6d56890
 	return 1;
 
 error:
4d48c5d4
 	xcaps_send_reply(msg, 500, &xcaps_str_srverr, NULL,
 				NULL, NULL);
142f0dbd
 	if(body.s!=NULL)
 		pkg_free(body.s);
e6d56890
 	return -1;
 }
 
 /**
  *
  */
d646b4b1
 int xcaps_path_get_auid_type(str *path)
e6d56890
 {
 	str s;
d646b4b1
 	char c;
 	int ret;
 
 	ret = -1;
e6d56890
 	if(path==NULL)
 		return -1;
 	if(path->len<xcaps_root.len)
 		return -1;
 
 	if(strncmp(path->s, xcaps_root.s, xcaps_root.len)!=0)
 	{
 		LM_ERR("missing xcap-root in [%.*s]\n", path->len, path->s);
 		return -1;
 	}
 
d646b4b1
 	s.s = path->s + xcaps_root.len - 1;
 	s.len = path->len - xcaps_root.len + 1;
 
 	c = s.s[s.len];
 	s.s[s.len] = '\0';
e6d56890
 
7264b67c
 	if(s.len>12 && strstr(s.s, "/pres-rules/")!=NULL)
e6d56890
 	{
 		LM_DBG("matched pres-rules\n");
d646b4b1
 		ret = PRES_RULES;
 		goto done;
e6d56890
 	}
 
7264b67c
 	if(s.len>35 && strstr(s.s, "/org.openmobilealliance.pres-rules/")!=NULL)
 	{
 		LM_DBG("matched oma pres-rules\n");
 		ret = PRES_RULES;
 		goto done;
 	}
 
 	if(s.len>14 && strstr(s.s, "/rls-services/")!=NULL)
e6d56890
 	{
 		LM_DBG("matched rls-services\n");
d646b4b1
 		ret = RLS_SERVICE;
 		goto done;
e6d56890
 	}
 
7264b67c
 	if(s.len>19 && strstr(s.s, "pidf-manipulation")!=NULL)
e6d56890
 	{
 		LM_DBG("matched pidf-manipulation\n");
d646b4b1
 		ret = PIDF_MANIPULATION;
 		goto done;
e6d56890
 	}
 
7264b67c
 	if(s.len>16 && strstr(s.s, "/resource-lists/")!=NULL)
e6d56890
 	{
 		LM_DBG("matched resource-lists\n");
d646b4b1
 		ret = RESOURCE_LIST;
 		goto done;
e6d56890
 	}
 
1265533a
         if(s.len>11 && strstr(s.s, "/xcap-caps/")!=NULL)
7264b67c
 	{
1265533a
                 LM_DBG("matched xcap-caps\n");
                 ret = XCAP_CAPS;
7264b67c
 		goto done;
 	}
 
1265533a
         if(s.len> 37 && strstr(s.s, "/org.openmobilealliance.user-profile/")!=NULL)
7264b67c
 	{
1265533a
                 LM_DBG("matched oma user-profile\n");
                 ret = USER_PROFILE;
7264b67c
 		goto done;
 	}
 
1265533a
         if(s.len> 37 && strstr(s.s, "/org.openmobilealliance.pres-content/")!=NULL)
7264b67c
 	{
1265533a
                 LM_DBG("matched oma pres-content\n");
                 ret = PRES_CONTENT;
 		goto done;
 	}
 
 	if(s.len>31 && strstr(s.s, "/org.openmobilealliance.search?")!=NULL)
 	{
                 LM_DBG("matched oma search\n");
                 ret = SEARCH;
7264b67c
 		goto done;
 	}
 
d646b4b1
 done:
 	s.s[s.len] = c;
 	return ret;
e6d56890
 }
 
 /**
  *
  */
 int xcaps_generate_etag_hdr(str *etag)
 {
4d48c5d4
 	etag->len = snprintf(xcaps_hdr_buf, XCAPS_HDR_SIZE,
d38e225f
 			"ETag: \"sr-%d-%d-%d\"\r\n", xcaps_init_time, my_pid(),
e6d56890
 			xcaps_etag_counter++);
 	if(etag->len <0)
 	{
 		LM_ERR("error printing etag\n ");
 		return -1;
 	}
4d48c5d4
 	if(etag->len >= XCAPS_HDR_SIZE)
e6d56890
 	{
 		LM_ERR("etag buffer overflow\n");
 		return -1;
 	}
 
4d48c5d4
 	etag->s = xcaps_hdr_buf;
e6d56890
 	etag->s[etag->len] = '\0';
 	return 0;
 }
 
 /** 
  * 
  */
 static int fixup_xcaps_put(void** param, int param_no)
 {
 	str s;
 	pv_elem_t *xm;
 	if (param_no == 1) {
 	    return fixup_spve_null(param, 1);
 	} else if (param_no == 2) {
 	    return fixup_spve_null(param, 1);
 	} else if (param_no == 3) {
 		s.s = (char*)(*param); s.len = strlen(s.s);
 		if(pv_parse_format(&s, &xm)<0)
 		{
 			LM_ERR("wrong format[%s]\n", (char*)(*param));
 			return E_UNSPEC;
 		}
 		*param = (void*)xm;
 	    return 0;
 	}
 	return 0;
 }
 
d38e225f
 static int check_preconditions(sip_msg_t *msg, str etag_hdr)
 {
 	struct hdr_field* hdr = msg->headers;
 	int ifmatch_found=0;
 	int matched_matched=0;
 	int matched_nonematched=0;
 
a4f28b97
 	if (parse_headers(msg, HDR_EOH_F, 0) < 0)
 	{
 		LM_ERR("error parsing headers\n");
 		return 1;
 	}
 
d38e225f
 	if (etag_hdr.len > 0)
 	{
 		str etag;
 
 		/* Keep the surrounding "s in the ETag */
 		etag.s = etag_hdr.s + 6; /* 'ETag: ' */
 		etag.len = etag_hdr.len - 8; /* 'ETag: "  "\r\n' */
e6d56890
 
d38e225f
 		while (hdr!=NULL)
 		{
 			if(cmp_hdrname_strzn(&hdr->name, "If-Match", 8)==0)
 			{
 				ifmatch_found = 1;
 				if (check_match_header(hdr->body, &etag)>0)
 					matched_matched = 1;
 			}
 			else if (cmp_hdrname_strzn(&hdr->name, "If-None-Match", 13)==0)
 			{
 				if (check_match_header(hdr->body, &etag)>0)
 					matched_nonematched = 1;
 			}
 			hdr = hdr->next;
 		}
 	} else {
 		while (hdr!=NULL)
 		{
 			if(cmp_hdrname_strzn(&hdr->name, "If-Match", 8)==0)
 				ifmatch_found = 1;
 
 			hdr = hdr->next;
 		}
 	}
 
 	if (ifmatch_found == 1 && matched_matched == 0)
 		return -1;
 	else if (matched_nonematched == 1)
 		return -2;
 	else
 		return 1;
 }
 
 static int check_match_header(str body, str *etag)
 {
d29cfab1
 	if (etag == NULL)
 		return -1;
 
10dafd75
 	if (etag->s == NULL || etag->len == 0)
d29cfab1
 		return -1;
 
d38e225f
 	do
 	{
 		char *start_pos, *end_pos, *old_body_pos;
 		int cur_etag_len;
 
 		if ((start_pos = strchr(body.s, '"')) == NULL)
 			return -1;
 		if ((end_pos = strchr(start_pos + 1, '"')) == NULL)
 			return -1;
 		cur_etag_len = end_pos - start_pos + 1;
 	
 		if (strncmp(start_pos, etag->s, cur_etag_len)==0)
 			return 1;
 		else if (strncmp(start_pos, "\"*\"", cur_etag_len)==0)
 			return 1;
 
 		old_body_pos = body.s;
 		if ((body.s = strchr(end_pos, ',')) == NULL)
 			return -1;
 		body.len -= body.s - old_body_pos;
 	} while (body.len > 0);
 
 	return -1;
 }