mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1429 lines
34 KiB
1429 lines
34 KiB
/*
|
|
* $Id: notify.c 2230 2007-06-06 07:13:20Z anca_vamanu $
|
|
*
|
|
* rls module - resource list server
|
|
*
|
|
* Copyright (C) 2007 Voice Sistem S.R.L.
|
|
*
|
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* History:
|
|
* --------
|
|
* 2007-09-11 initial version (anca)
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "../../ut.h"
|
|
#include "../../str.h"
|
|
#include "../../dprint.h"
|
|
#include "../../trim.h"
|
|
#include "../../data_lump_rpl.h"
|
|
#include "../../parser/msg_parser.h"
|
|
#include "../../parser/parse_event.h"
|
|
#include "../../parser/parse_expires.h"
|
|
#include "../../parser/parse_cseq.h"
|
|
#include "../../parser/contact/parse_contact.h"
|
|
#include "../../parser/parse_rr.h"
|
|
#include "../../modules/tm/dlg.h"
|
|
#include "../presence/utils_func.h"
|
|
#include "../presence/hash.h"
|
|
#include "../../hashes.h"
|
|
#include "rls.h"
|
|
#include "notify.h"
|
|
#include "utils.h"
|
|
#include <libxml/xpath.h>
|
|
#include <libxml/xpathInternals.h>
|
|
|
|
static str *multipart_body = NULL;
|
|
static int multipart_body_size = 0;
|
|
|
|
typedef struct res_param
|
|
{
|
|
struct uri_link **next;
|
|
}res_param_t;
|
|
|
|
typedef struct uri_link
|
|
{
|
|
char *uri;
|
|
struct uri_link *next;
|
|
} uri_link_t;
|
|
|
|
int resource_uri_col=0, content_type_col, pres_state_col= 0,
|
|
auth_state_col= 0, reason_col= 0;
|
|
|
|
xmlDocPtr constr_rlmi_doc(db1_res_t* result, str* rl_uri, int version,
|
|
xmlNodePtr rl_node, char*** cid_array,
|
|
str username, str domain);
|
|
void constr_multipart_body(const str *const content_type, const str *const body, str *cid, int boundary_len, char *boundary_string);
|
|
|
|
dlg_t* rls_notify_dlg(subs_t* subs);
|
|
|
|
void rls_notify_callback( struct cell *t, int type, struct tmcb_params *ps);
|
|
|
|
int parse_xcap_uri(char *uri, str *host, unsigned short *port, str *path);
|
|
int rls_get_resource_list(str *rl_uri, str *username, str *domain,
|
|
xmlNodePtr *rl_node, xmlDocPtr *xmldoc);
|
|
int add_resource_to_list(char* uri, void* param);
|
|
int add_resource(char* uri, xmlNodePtr list_node, char * boundary_string, db1_res_t *result, int *len_est);
|
|
|
|
char *instance_id = "Scf8UhwQ";
|
|
|
|
int send_full_notify(subs_t* subs, xmlNodePtr rl_node, str* rl_uri,
|
|
unsigned int hash_code)
|
|
{
|
|
xmlDocPtr rlmi_body= NULL;
|
|
xmlNodePtr list_node= NULL;
|
|
db_key_t query_cols[1], update_cols[1], result_cols[5];
|
|
db_val_t query_vals[1], update_vals[1];
|
|
db1_res_t *result= NULL;
|
|
int n_result_cols= 0;
|
|
char* boundary_string;
|
|
str rlsubs_did= {0, 0};
|
|
str* rlmi_cont= NULL;
|
|
uri_link_t *uri_list_head = NULL;
|
|
int len_est;
|
|
res_param_t param;
|
|
int resource_added = 0; /* Flag to indicate that we have added at least one resource */
|
|
multipart_body=NULL;
|
|
db_query_f query_fn = rlpres_dbf.query_lock ? rlpres_dbf.query_lock : rlpres_dbf.query;
|
|
|
|
LM_DBG("start\n");
|
|
|
|
if(CONSTR_RLSUBS_DID(subs, &rlsubs_did)<0)
|
|
{
|
|
LM_ERR("cannot build rls subs did\n");
|
|
goto error;
|
|
}
|
|
|
|
query_cols[0]= &str_rlsubs_did_col;
|
|
query_vals[0].type = DB1_STR;
|
|
query_vals[0].nul = 0;
|
|
query_vals[0].val.str_val= rlsubs_did;
|
|
|
|
result_cols[resource_uri_col= n_result_cols++]= &str_resource_uri_col;
|
|
result_cols[content_type_col= n_result_cols++]= &str_content_type_col;
|
|
result_cols[pres_state_col= n_result_cols++]= &str_presence_state_col;
|
|
result_cols[auth_state_col= n_result_cols++]= &str_auth_state_col;
|
|
result_cols[reason_col= n_result_cols++]= &str_reason_col;
|
|
|
|
update_cols[0]= &str_updated_col;
|
|
update_vals[0].type = DB1_INT;
|
|
update_vals[0].nul = 0;
|
|
update_vals[0].val.int_val= NO_UPDATE_TYPE;
|
|
|
|
if (rlpres_dbf.use_table(rlpres_db, &rlpres_table) < 0)
|
|
{
|
|
LM_ERR("in use_table\n");
|
|
goto error;
|
|
}
|
|
|
|
if (dbmode == RLS_DB_ONLY && rlpres_dbf.start_transaction)
|
|
{
|
|
if (rlpres_dbf.start_transaction(rlpres_db, DB_LOCKING_WRITE) < 0)
|
|
{
|
|
LM_ERR("in start_transaction\n");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if(query_fn(rlpres_db, query_cols, 0, query_vals, result_cols,
|
|
1, n_result_cols, NULL, &result )< 0)
|
|
{
|
|
LM_ERR("in sql query\n");
|
|
goto error;
|
|
}
|
|
if(result == NULL)
|
|
{
|
|
LM_ERR("bad result\n");
|
|
goto error;
|
|
}
|
|
|
|
if (result->n > 0)
|
|
{
|
|
if(rlpres_dbf.update(rlpres_db, query_cols, 0, query_vals,
|
|
update_cols, update_vals, 1, 1) < 0)
|
|
{
|
|
LM_ERR("in sql update\n");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (dbmode == RLS_DB_ONLY && rlpres_dbf.end_transaction)
|
|
{
|
|
if (rlpres_dbf.end_transaction(rlpres_db) < 0)
|
|
{
|
|
LM_ERR("in end_transaction\n");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* Allocate an initial buffer for the multipart body.
|
|
* This buffer will be reallocated if neccessary */
|
|
multipart_body= (str*)pkg_malloc(sizeof(str));
|
|
if(multipart_body== NULL)
|
|
{
|
|
ERR_MEM(PKG_MEM_STR);
|
|
}
|
|
|
|
multipart_body_size = BUF_REALLOC_SIZE;
|
|
multipart_body->s = (char *)pkg_malloc(multipart_body_size);
|
|
|
|
if(multipart_body->s== NULL)
|
|
{
|
|
ERR_MEM(PKG_MEM_STR);
|
|
}
|
|
|
|
multipart_body->len= 0;
|
|
|
|
/* Create an empty rlmi document */
|
|
len_est = create_empty_rlmi_doc(&rlmi_body, &list_node, rl_uri, subs->version, 1);
|
|
xmlDocSetRootElement(rlmi_body, list_node);
|
|
|
|
/* Find all the uri's to which we are subscribed */
|
|
param.next = &uri_list_head;
|
|
if(process_list_and_exec(rl_node, subs->watcher_user, subs->watcher_domain, add_resource_to_list,(void*)(¶m))< 0)
|
|
{
|
|
LM_ERR("in process_list_and_exec function\n");
|
|
goto error;
|
|
}
|
|
|
|
boundary_string= generate_string(BOUNDARY_STRING_LEN);
|
|
|
|
while (uri_list_head)
|
|
{
|
|
uri_link_t *last = uri_list_head;
|
|
if (add_resource(uri_list_head->uri, list_node, boundary_string, result, &len_est) >0)
|
|
{
|
|
if (resource_added == 0)
|
|
{
|
|
/* We have exceeded our length estimate without adding any resource.
|
|
We cannot send this resource, move on. */
|
|
LM_ERR("Failed to add a single resource %d vs %d\n", len_est, rls_max_notify_body_len);
|
|
uri_list_head = uri_list_head->next;
|
|
if (last->uri) pkg_free(last->uri);
|
|
pkg_free(last);
|
|
}
|
|
else
|
|
{
|
|
LM_DBG("send_full_notify estimate exceeded %d vs %d\n", len_est, rls_max_notify_body_len);
|
|
/* If add_resource returns > 0 the resource did not fit in our size limit */
|
|
rlmi_cont= (str*)pkg_malloc(sizeof(str));
|
|
if(rlmi_cont== NULL)
|
|
{
|
|
ERR_MEM(PKG_MEM_STR);
|
|
}
|
|
/* Where we are worried about length we won't use padding */
|
|
xmlDocDumpFormatMemory(rlmi_body,(xmlChar**)(void*)&rlmi_cont->s,
|
|
&rlmi_cont->len, 0);
|
|
xmlFreeDoc(rlmi_body);
|
|
|
|
if(agg_body_sendn_update(rl_uri, boundary_string, rlmi_cont,
|
|
multipart_body, subs, hash_code)< 0)
|
|
{
|
|
LM_ERR("in function agg_body_sendn_update\n");
|
|
goto error;
|
|
}
|
|
|
|
/* Create a new rlmi body, but not a full_state one this time */
|
|
len_est = create_empty_rlmi_doc(&rlmi_body, &list_node, rl_uri, subs->version, 0);
|
|
xmlDocSetRootElement(rlmi_body, list_node);
|
|
multipart_body->len = 0;
|
|
resource_added = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
resource_added = 1;
|
|
uri_list_head = uri_list_head->next;
|
|
if (last->uri) pkg_free(last->uri);
|
|
pkg_free(last);
|
|
}
|
|
}
|
|
|
|
rlmi_cont= (str*)pkg_malloc(sizeof(str));
|
|
if(rlmi_cont== NULL)
|
|
{
|
|
ERR_MEM(PKG_MEM_STR);
|
|
}
|
|
xmlDocDumpFormatMemory(rlmi_body,(xmlChar**)(void*)&rlmi_cont->s,
|
|
&rlmi_cont->len, (rls_max_notify_body_len == 0));
|
|
xmlFreeDoc(rlmi_body);
|
|
|
|
if(agg_body_sendn_update(rl_uri, boundary_string, rlmi_cont,
|
|
multipart_body, subs, hash_code)< 0)
|
|
{
|
|
LM_ERR("in function agg_body_sendn_update\n");
|
|
goto error;
|
|
}
|
|
|
|
xmlFree(rlmi_cont->s);
|
|
pkg_free(rlmi_cont);
|
|
|
|
if(multipart_body)
|
|
{
|
|
if (multipart_body->s)
|
|
pkg_free(multipart_body->s);
|
|
pkg_free(multipart_body);
|
|
}
|
|
multipart_body_size = 0;
|
|
pkg_free(rlsubs_did.s);
|
|
rlpres_dbf.free_result(rlpres_db, result);
|
|
|
|
return 0;
|
|
error:
|
|
if(rlmi_cont)
|
|
{
|
|
if(rlmi_cont->s)
|
|
xmlFree(rlmi_cont->s);
|
|
pkg_free(rlmi_cont);
|
|
}
|
|
if(multipart_body)
|
|
{
|
|
if(multipart_body->s)
|
|
pkg_free(multipart_body->s);
|
|
pkg_free(multipart_body);
|
|
}
|
|
multipart_body_size = 0;
|
|
|
|
if(result)
|
|
rlpres_dbf.free_result(rlpres_db, result);
|
|
if(rlsubs_did.s)
|
|
pkg_free(rlsubs_did.s);
|
|
|
|
if (dbmode == RLS_DB_ONLY && rlpres_dbf.abort_transaction)
|
|
{
|
|
if (rlpres_dbf.abort_transaction(rlpres_db) < 0)
|
|
LM_ERR("in abort_transaction");
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int agg_body_sendn_update(str* rl_uri, char* boundary_string, str* rlmi_body,
|
|
str* multipart_body, subs_t* subs, unsigned int hash_code)
|
|
{
|
|
char* cid;
|
|
int len;
|
|
str body= {0, 0};
|
|
int init_len;
|
|
|
|
cid= generate_cid(rl_uri->s, rl_uri->len);
|
|
|
|
len= 2*strlen(boundary_string)+4+102+strlen(cid)+2+rlmi_body->len+50;
|
|
if(multipart_body)
|
|
len+= multipart_body->len;
|
|
|
|
init_len= len;
|
|
|
|
body.s= (char*)pkg_malloc((len+1)* sizeof(char));
|
|
if(body.s== NULL)
|
|
{
|
|
ERR_MEM(PKG_MEM_STR);
|
|
}
|
|
len= sprintf(body.s, "--%s\r\n", boundary_string);
|
|
len+= sprintf(body.s+ len , "Content-Transfer-Encoding: binary\r\n");
|
|
len+= sprintf(body.s+ len , "Content-ID: <%s>\r\n", cid);
|
|
len+= sprintf(body.s+ len ,
|
|
"Content-Type: application/rlmi+xml;charset=\"UTF-8\"\r\n");
|
|
len+= sprintf(body.s+ len, "\r\n"); /*blank line*/
|
|
memcpy(body.s+ len, rlmi_body->s, rlmi_body->len);
|
|
len+= rlmi_body->len;
|
|
len+= sprintf(body.s+ len, "\r\n"); /*blank line*/
|
|
|
|
if(multipart_body)
|
|
{
|
|
memcpy(body.s+ len, multipart_body->s, multipart_body->len);
|
|
len+= multipart_body->len;
|
|
}
|
|
len+= sprintf(body.s+ len, "--%s--\r\n", boundary_string);
|
|
|
|
if(init_len< len)
|
|
{
|
|
LM_ERR("buffer size overflow init_size= %d\tlen= %d\n",init_len,len);
|
|
goto error;
|
|
}
|
|
body.s[len]= '\0';
|
|
body.len= len;
|
|
|
|
/* send Notify */
|
|
if(rls_send_notify(subs, &body, cid, boundary_string)< 0)
|
|
{
|
|
LM_ERR("when sending Notify\n");
|
|
goto error;
|
|
}
|
|
/* update local_cseq in cache list watchers table */
|
|
pkg_free(body.s);
|
|
body.s= NULL;
|
|
|
|
if (dbmode==RLS_DB_ONLY)
|
|
{
|
|
if (update_dialog_notify_rlsdb(subs) < 0)
|
|
{
|
|
LM_ERR( "updating DB\n" );
|
|
goto error;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(pres_update_shtable(rls_table, hash_code,subs, LOCAL_TYPE)< 0)
|
|
{
|
|
LM_ERR("updating in hash table\n");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
if(body.s)
|
|
pkg_free(body.s);
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
int add_resource_instance(char* uri, xmlNodePtr resource_node,
|
|
db1_res_t* result, char * boundary_string,
|
|
int *len_est)
|
|
{
|
|
xmlNodePtr instance_node= NULL;
|
|
db_row_t *row;
|
|
db_val_t *row_vals;
|
|
int i, cmp_code;
|
|
char* auth_state= NULL;
|
|
int auth_state_flag;
|
|
int boundary_len = strlen(boundary_string);
|
|
str cid;
|
|
str content_type= {0, 0};
|
|
str body= {0, 0};
|
|
|
|
for(i= 0; i< result->n; i++)
|
|
{
|
|
row = &result->rows[i];
|
|
row_vals = ROW_VALUES(row);
|
|
|
|
cmp_code= strncmp(row_vals[resource_uri_col].val.string_val, uri,
|
|
strlen(uri));
|
|
if(cmp_code> 0)
|
|
break;
|
|
|
|
if(cmp_code== 0)
|
|
{
|
|
auth_state_flag= row_vals[auth_state_col].val.int_val;
|
|
auth_state= get_auth_string(auth_state_flag );
|
|
if(auth_state== NULL)
|
|
{
|
|
LM_ERR("bad authorization status flag\n");
|
|
goto error;
|
|
}
|
|
*len_est += strlen(auth_state) + 38; /* <instance id="12345678" state="[auth_state]" />r/n */
|
|
|
|
if(auth_state_flag & ACTIVE_STATE)
|
|
{
|
|
cid.s= generate_cid(uri, strlen(uri));
|
|
cid.len= strlen(cid.s);
|
|
body.s= (char*)row_vals[pres_state_col].val.string_val;
|
|
body.len= strlen(body.s);
|
|
trim(&body);
|
|
|
|
*len_est += cid.len + 8; /* cid="[cid]" */
|
|
content_type.s = (char*)row_vals[content_type_col].val.string_val;
|
|
content_type.len = strlen(content_type.s);
|
|
*len_est += 4 + boundary_len
|
|
+ 35
|
|
+ 16 + cid.len
|
|
+ 18 + content_type.len
|
|
+ 4 + body.len + 8;
|
|
}
|
|
else if(auth_state_flag & TERMINATED_STATE)
|
|
{
|
|
*len_est += strlen(row_vals[resource_uri_col].val.string_val) + 10; /* reason="[resaon]" */
|
|
}
|
|
if (rls_max_notify_body_len > 0 && *len_est > rls_max_notify_body_len)
|
|
{
|
|
/* We have a limit on body length set, and we were about to exceed it */
|
|
return *len_est;
|
|
}
|
|
|
|
instance_node= xmlNewChild(resource_node, NULL,
|
|
BAD_CAST "instance", NULL);
|
|
if(instance_node== NULL)
|
|
{
|
|
LM_ERR("while adding instance child\n");
|
|
goto error;
|
|
}
|
|
|
|
/* OK, we are happy this will fit */
|
|
/* Instance ID should be unique for each instance node
|
|
within a resource node. The same instance ID can be
|
|
used in different resource nodes. Instance ID needs
|
|
to remain the same for each resource instance in
|
|
future updates. We can just use a common string
|
|
here because you will only get multiple instances
|
|
for a resource when the back-end SUBSCRIBE is forked
|
|
and pua does not support this. If/when pua supports
|
|
forking of the SUBSCRIBEs it sends this will need to
|
|
be fixed properly. */
|
|
xmlNewProp(instance_node, BAD_CAST "id",
|
|
BAD_CAST instance_id);
|
|
xmlNewProp(instance_node, BAD_CAST "state", BAD_CAST auth_state);
|
|
|
|
if(auth_state_flag & ACTIVE_STATE)
|
|
{
|
|
constr_multipart_body (&content_type, &body, &cid, boundary_len, boundary_string);
|
|
xmlNewProp(instance_node, BAD_CAST "cid", BAD_CAST cid.s);
|
|
}
|
|
else
|
|
if(auth_state_flag & TERMINATED_STATE)
|
|
{
|
|
xmlNewProp(instance_node, BAD_CAST "reason",
|
|
BAD_CAST row_vals[reason_col].val.string_val);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if record not found should not add a instance node */
|
|
return 0;
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
int add_resource(char* uri, xmlNodePtr list_node, char * boundary_string, db1_res_t *result, int *len_est)
|
|
{
|
|
xmlNodePtr resource_node= NULL;
|
|
int res;
|
|
|
|
if (rls_max_notify_body_len > 0)
|
|
{
|
|
*len_est += strlen (uri) + 35; /* <resource uri="[uri]"></resource>/r/n */
|
|
if (*len_est > rls_max_notify_body_len)
|
|
{
|
|
return *len_est;
|
|
}
|
|
}
|
|
resource_node= xmlNewChild(list_node, NULL, BAD_CAST "resource", NULL);
|
|
if(resource_node== NULL)
|
|
{
|
|
goto error;
|
|
}
|
|
xmlNewProp(resource_node, BAD_CAST "uri", BAD_CAST uri);
|
|
|
|
res = add_resource_instance(uri, resource_node, result, boundary_string, len_est);
|
|
if(res < 0)
|
|
{
|
|
LM_ERR("while adding resource instance node\n");
|
|
goto error;
|
|
}
|
|
|
|
return res;
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
int add_resource_to_list(char* uri, void* param)
|
|
{
|
|
struct uri_link **next = ((res_param_t*)param)->next;
|
|
*next = pkg_malloc(sizeof(uri_link_t));
|
|
if (*next == NULL)
|
|
{
|
|
LM_ERR("while creating linked list element\n");
|
|
goto error;
|
|
}
|
|
|
|
(*next)->next = NULL;
|
|
(*next)->uri = pkg_malloc(strlen(uri) + 1);
|
|
if ((*next)->uri == NULL)
|
|
{
|
|
LM_ERR("while creating uri store\n");
|
|
pkg_free(*next);
|
|
*next = NULL;
|
|
goto error;
|
|
}
|
|
strcpy((*next)->uri, uri);
|
|
((res_param_t*)param)->next = &(*next)->next;
|
|
|
|
return 0;
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
int create_empty_rlmi_doc(xmlDocPtr *rlmi_doc, xmlNodePtr *list_node, str *uri, int version, int full_state)
|
|
{
|
|
/* length is an pessimitic estimate of the size of an empty document
|
|
We calculate it once for performance reasons.
|
|
We add in the uri length each time as this varies, and it is cheap to add */
|
|
static int length = 0;
|
|
char* rl_uri= NULL;
|
|
int len;
|
|
|
|
/* make new rlmi and multipart documents */
|
|
*rlmi_doc= xmlNewDoc(BAD_CAST "1.0");
|
|
if(*rlmi_doc== NULL)
|
|
{
|
|
LM_ERR("when creating new xml doc\n");
|
|
return 0;
|
|
}
|
|
*list_node= xmlNewNode(NULL, BAD_CAST "list");
|
|
if(*list_node== NULL)
|
|
{
|
|
LM_ERR("while creating new xml node\n");
|
|
return 0;
|
|
}
|
|
rl_uri= (char*)pkg_malloc((uri->len+ 1)* sizeof(char));
|
|
if(rl_uri== NULL)
|
|
{
|
|
ERR_MEM(PKG_MEM_STR);
|
|
}
|
|
memcpy(rl_uri, uri->s, uri->len);
|
|
rl_uri[uri->len]= '\0';
|
|
|
|
xmlNewProp(*list_node, BAD_CAST "uri", BAD_CAST rl_uri);
|
|
xmlNewProp(*list_node, BAD_CAST "xmlns",
|
|
BAD_CAST "urn:ietf:params:xml:ns:rlmi");
|
|
xmlNewProp(*list_node, BAD_CAST "version",
|
|
BAD_CAST int2str(version, &len));
|
|
if (full_state)
|
|
xmlNewProp(*list_node, BAD_CAST "fullState", BAD_CAST "true");
|
|
else
|
|
xmlNewProp(*list_node, BAD_CAST "fullState", BAD_CAST "false");
|
|
|
|
xmlDocSetRootElement(*rlmi_doc, *list_node);
|
|
pkg_free(rl_uri); /* xmlNewProp takes a copy, so we can free this now */
|
|
|
|
if (length == 0)
|
|
{
|
|
/* We haven't found out how big an empty doc is
|
|
Let's find out now ! */
|
|
xmlChar* dumped_document;
|
|
/* Where we are worried about length we won't use padding */
|
|
xmlDocDumpFormatMemory( *rlmi_doc,&dumped_document, &length, 0);
|
|
xmlFree(dumped_document);
|
|
length -= uri->len; /* The uri varies, so we will add it each time */
|
|
}
|
|
return length + uri->len;
|
|
error:
|
|
return 0;
|
|
}
|
|
|
|
|
|
void constr_multipart_body(const str *const content_type, const str *const body, str *cid, int boundary_len, char *boundary_string)
|
|
{
|
|
char* buf= multipart_body->s;
|
|
int length= multipart_body->len;
|
|
int chunk_len;
|
|
|
|
LM_DBG("start\n");
|
|
|
|
chunk_len = 4 + boundary_len
|
|
+ 35
|
|
+ 16 + cid->len
|
|
+ 18 + content_type->len
|
|
+ 4 + body->len + 8;
|
|
while(length + chunk_len >= multipart_body_size)
|
|
{
|
|
multipart_body_size += BUF_REALLOC_SIZE;
|
|
multipart_body->s = (char*)pkg_realloc(multipart_body->s, multipart_body_size);
|
|
if(multipart_body->s == NULL)
|
|
{
|
|
ERR_MEM("constr_multipart_body");
|
|
}
|
|
}
|
|
buf = multipart_body->s;
|
|
|
|
length+= sprintf(buf+ length, "--%.*s\r\n",
|
|
boundary_len, boundary_string);
|
|
length+= sprintf(buf+ length, "Content-Transfer-Encoding: binary\r\n");
|
|
length+= sprintf(buf+ length, "Content-ID: <%.*s>\r\n",
|
|
cid->len, cid->s);
|
|
length+= sprintf(buf+ length, "Content-Type: %.*s\r\n\r\n",
|
|
content_type->len, content_type->s);
|
|
length+= sprintf(buf+length,"%.*s\r\n\r\n",
|
|
body->len, body->s);
|
|
|
|
multipart_body->len = length;
|
|
|
|
error:
|
|
|
|
return;
|
|
}
|
|
|
|
str* rls_notify_extra_hdr(subs_t* subs, char* start_cid, char* boundary_string)
|
|
{
|
|
str* str_hdr= NULL;
|
|
int len= 0, expires;
|
|
|
|
str_hdr= (str*)pkg_malloc(sizeof(str));
|
|
if(str_hdr== NULL)
|
|
{
|
|
ERR_MEM(PKG_MEM_STR);
|
|
}
|
|
memset(str_hdr, 0, sizeof(str));
|
|
|
|
str_hdr->s= (char*)pkg_malloc(RLS_HDR_LEN* sizeof(char));
|
|
if(str_hdr->s== NULL)
|
|
{
|
|
ERR_MEM(PKG_MEM_STR);
|
|
}
|
|
memcpy(str_hdr->s ,"Max-Forwards: ", 14);
|
|
str_hdr->len = 14;
|
|
len= sprintf(str_hdr->s+str_hdr->len, "%d", MAX_FORWARD);
|
|
if(len<= 0)
|
|
{
|
|
LM_ERR("while printing in string\n");
|
|
goto error;
|
|
}
|
|
str_hdr->len+= len;
|
|
memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN);
|
|
str_hdr->len += CRLF_LEN;
|
|
|
|
memcpy(str_hdr->s+str_hdr->len ,"Event: ", 7);
|
|
str_hdr->len+= 7;
|
|
memcpy(str_hdr->s+str_hdr->len, subs->event->name.s,
|
|
subs->event->name.len);
|
|
str_hdr->len+= subs->event->name.len;
|
|
memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN);
|
|
str_hdr->len += CRLF_LEN;
|
|
|
|
memcpy(str_hdr->s+str_hdr->len ,"Contact: <", 10);
|
|
str_hdr->len += 10;
|
|
memcpy(str_hdr->s+str_hdr->len,subs->local_contact.s,
|
|
subs->local_contact.len);
|
|
str_hdr->len += subs->local_contact.len;
|
|
memcpy(str_hdr->s+str_hdr->len, ">", 1);
|
|
str_hdr->len += 1;
|
|
memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN);
|
|
str_hdr->len += CRLF_LEN;
|
|
|
|
expires= subs->expires;
|
|
|
|
if( expires> 0 )
|
|
str_hdr->len+= sprintf(str_hdr->s+str_hdr->len,
|
|
"Subscription-State: active;expires=%d\r\n", expires);
|
|
else
|
|
str_hdr->len+= sprintf(str_hdr->s+str_hdr->len,
|
|
"Subscription-State: terminated;reason=timeout\r\n");
|
|
|
|
str_hdr->len+= sprintf(str_hdr->s+str_hdr->len, "Require: eventlist\r\n");
|
|
|
|
if(start_cid && boundary_string)
|
|
{
|
|
str_hdr->len+= sprintf(str_hdr->s+str_hdr->len,
|
|
"Content-Type: multipart/related;type=\"application/rlmi+xml\"");
|
|
str_hdr->len+= sprintf(str_hdr->s+str_hdr->len,
|
|
";start=\"<%s>\";boundary=\"%s\"\r\n",
|
|
start_cid, boundary_string);
|
|
}
|
|
if(str_hdr->len> RLS_HDR_LEN)
|
|
{
|
|
LM_ERR("buffer size overflow\n");
|
|
goto error;
|
|
}
|
|
str_hdr->s[str_hdr->len] = '\0';
|
|
|
|
return str_hdr;
|
|
|
|
error:
|
|
if(str_hdr)
|
|
{
|
|
if(str_hdr->s)
|
|
pkg_free(str_hdr->s);
|
|
pkg_free(str_hdr);
|
|
}
|
|
return NULL;
|
|
|
|
}
|
|
|
|
void rls_free_td(dlg_t* td)
|
|
{
|
|
if(td)
|
|
{
|
|
if(td->loc_uri.s)
|
|
pkg_free(td->loc_uri.s);
|
|
|
|
if(td->rem_uri.s)
|
|
pkg_free(td->rem_uri.s);
|
|
|
|
if(td->route_set)
|
|
free_rr(&td->route_set);
|
|
|
|
pkg_free(td);
|
|
}
|
|
}
|
|
|
|
int rls_send_notify(subs_t* subs, str* body, char* start_cid,
|
|
char* boundary_string)
|
|
{
|
|
dlg_t* td= NULL;
|
|
str met= {"NOTIFY", 6};
|
|
str* str_hdr= NULL;
|
|
dialog_id_t* cb_param= NULL;
|
|
int size;
|
|
int rt;
|
|
uac_req_t uac_r;
|
|
|
|
LM_DBG("start\n");
|
|
td= rls_notify_dlg(subs);
|
|
if(td ==NULL)
|
|
{
|
|
LM_ERR("while building dlg_t structure\n");
|
|
goto error;
|
|
}
|
|
|
|
LM_DBG("constructed dlg_t struct\n");
|
|
size= sizeof(dialog_id_t)+(subs->to_tag.len+ subs->callid.len+
|
|
subs->from_tag.len) *sizeof(char);
|
|
|
|
cb_param = (dialog_id_t*)shm_malloc(size);
|
|
if(cb_param== NULL)
|
|
{
|
|
ERR_MEM(SHARE_MEM);
|
|
}
|
|
size= sizeof(dialog_id_t);
|
|
|
|
cb_param->callid.s= (char*)cb_param + size;
|
|
memcpy(cb_param->callid.s, subs->callid.s, subs->callid.len);
|
|
cb_param->callid.len= subs->callid.len;
|
|
size+= subs->callid.len;
|
|
|
|
cb_param->to_tag.s= (char*)cb_param + size;
|
|
memcpy(cb_param->to_tag.s, subs->to_tag.s, subs->to_tag.len);
|
|
cb_param->to_tag.len= subs->to_tag.len;
|
|
size+= subs->to_tag.len;
|
|
|
|
cb_param->from_tag.s= (char*)cb_param + size;
|
|
memcpy(cb_param->from_tag.s, subs->from_tag.s, subs->from_tag.len);
|
|
cb_param->from_tag.len= subs->from_tag.len;
|
|
|
|
LM_DBG("constructed cb_param\n");
|
|
|
|
str_hdr= rls_notify_extra_hdr(subs, start_cid, boundary_string);
|
|
if(str_hdr== NULL || str_hdr->s== NULL)
|
|
{
|
|
LM_ERR("while building extra headers\n");
|
|
goto error;
|
|
}
|
|
LM_DBG("str_hdr= %.*s\n", str_hdr->len, str_hdr->s);
|
|
|
|
set_uac_req(&uac_r, &met, str_hdr, body, td, TMCB_LOCAL_COMPLETED,
|
|
rls_notify_callback, (void*)cb_param);
|
|
|
|
rt = tmb.t_request_within(&uac_r);
|
|
if(rt < 0)
|
|
{
|
|
LM_ERR("in function tmb.t_request_within\n");
|
|
goto error;
|
|
}
|
|
|
|
pkg_free(str_hdr->s);
|
|
pkg_free(str_hdr);
|
|
rls_free_td(td);
|
|
return 0;
|
|
|
|
error:
|
|
if(td)
|
|
rls_free_td(td);
|
|
if(cb_param)
|
|
shm_free(cb_param);
|
|
if(str_hdr)
|
|
{
|
|
if(str_hdr->s)
|
|
pkg_free(str_hdr->s);
|
|
pkg_free(str_hdr);
|
|
}
|
|
return -1;
|
|
|
|
}
|
|
|
|
dlg_t* rls_notify_dlg(subs_t* subs)
|
|
{
|
|
dlg_t* td=NULL;
|
|
|
|
td= (dlg_t*)pkg_malloc(sizeof(dlg_t));
|
|
if(td== NULL)
|
|
{
|
|
ERR_MEM(PKG_MEM_STR);
|
|
}
|
|
|
|
memset(td, 0, sizeof(dlg_t));
|
|
td->loc_seq.value = subs->local_cseq;
|
|
td->loc_seq.is_set = 1;
|
|
|
|
td->id.call_id = subs->callid;
|
|
td->id.rem_tag = subs->from_tag;
|
|
td->id.loc_tag =subs->to_tag;
|
|
if(uandd_to_uri(subs->to_user, subs->to_domain, &td->loc_uri)< 0)
|
|
{
|
|
LM_ERR("while constructing uri from user and domain\n");
|
|
goto error;
|
|
}
|
|
|
|
if(uandd_to_uri(subs->from_user, subs->from_domain, &td->rem_uri)< 0)
|
|
{
|
|
LM_ERR("while constructing uri from user and domain\n");
|
|
goto error;
|
|
}
|
|
|
|
if(subs->contact.len ==0 || subs->contact.s == NULL )
|
|
{
|
|
LM_DBG("BAD BAD contact NULL\n");
|
|
td->rem_target = td->rem_uri;
|
|
}
|
|
else
|
|
td->rem_target = subs->contact;
|
|
|
|
if(subs->record_route.s && subs->record_route.len)
|
|
{
|
|
if(parse_rr_body(subs->record_route.s, subs->record_route.len,
|
|
&td->route_set)< 0)
|
|
{
|
|
LM_ERR("in function parse_rr_body\n");
|
|
goto error;
|
|
}
|
|
}
|
|
td->state= DLG_CONFIRMED ;
|
|
|
|
if (subs->sockinfo_str.len) {
|
|
int port, proto;
|
|
str host;
|
|
char* tmp;
|
|
if ((tmp = as_asciiz(&subs->sockinfo_str)) == NULL) {
|
|
LM_ERR("no pkg mem left\n");
|
|
goto error;
|
|
}
|
|
if (parse_phostport (tmp,&host.s,
|
|
&host.len,&port, &proto )) {
|
|
LM_ERR("bad sockinfo string\n");
|
|
pkg_free(tmp);
|
|
goto error;
|
|
}
|
|
pkg_free(tmp);
|
|
td->send_sock = grep_sock_info (
|
|
&host, (unsigned short) port, (unsigned short) proto);
|
|
}
|
|
|
|
return td;
|
|
|
|
error:
|
|
if(td) rls_free_td(td);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
void rls_notify_callback( struct cell *t, int type, struct tmcb_params *ps)
|
|
{
|
|
if(ps->param==NULL || *ps->param==NULL ||
|
|
((dialog_id_t*)(*ps->param)) == NULL)
|
|
{
|
|
LM_DBG("message id not received\n");
|
|
return;
|
|
}
|
|
|
|
LM_DBG("completed with status %d [to_tag:"
|
|
"%.*s]\n",ps->code,
|
|
((dialog_id_t*)(*ps->param))->to_tag.len,
|
|
((dialog_id_t*)(*ps->param))->to_tag.s);
|
|
|
|
if(ps->code >= 300)
|
|
{
|
|
db_key_t db_keys[2];
|
|
db_val_t db_vals[2];
|
|
unsigned int hash_code;
|
|
subs_t subs;
|
|
|
|
memset(&subs, 0, sizeof(subs_t));
|
|
|
|
subs.to_tag= ((dialog_id_t*)(*ps->param))->to_tag;
|
|
subs.from_tag= ((dialog_id_t*)(*ps->param))->from_tag;
|
|
subs.callid= ((dialog_id_t*)(*ps->param))->callid;
|
|
|
|
if (dbmode != RLS_DB_ONLY)
|
|
{
|
|
/* delete from cache table */
|
|
hash_code= core_hash(&subs.callid, &subs.to_tag , hash_size);
|
|
|
|
if(pres_delete_shtable(rls_table,hash_code, subs.to_tag)< 0)
|
|
{
|
|
LM_ERR("record not found in hash table\n");
|
|
}
|
|
|
|
/* delete from database table */
|
|
if (rls_dbf.use_table(rls_db, &rlsubs_table) < 0)
|
|
{
|
|
LM_ERR("in use_table\n");
|
|
goto done;
|
|
}
|
|
|
|
db_keys[0] =&str_to_tag_col;
|
|
db_vals[0].type = DB1_STR;
|
|
db_vals[0].nul = 0;
|
|
db_vals[0].val.str_val = subs.to_tag;
|
|
|
|
db_keys[1] =&str_callid_col;
|
|
db_vals[1].type = DB1_STR;
|
|
db_vals[1].nul = 0;
|
|
db_vals[1].val.str_val = subs.callid;
|
|
|
|
|
|
if (rls_dbf.delete(rls_db, db_keys, 0, db_vals, 2) < 0)
|
|
LM_ERR("cleaning expired messages\n");
|
|
}
|
|
else
|
|
{
|
|
if (delete_rlsdb(&subs.callid, &subs.to_tag, NULL) < 0 )
|
|
{
|
|
LM_ERR( "unable to delete record from DB\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
if(*ps->param !=NULL )
|
|
shm_free(*ps->param);
|
|
return ;
|
|
|
|
}
|
|
|
|
int process_list_and_exec(xmlNodePtr list_node, str username, str domain,
|
|
list_func_t function, void* param)
|
|
{
|
|
xmlNodePtr node;
|
|
str uri;
|
|
int res = 0;
|
|
|
|
for(node= list_node->children; node; node= node->next)
|
|
{
|
|
if(xmlStrcasecmp(node->name,(unsigned char*)"resource-list")==0)
|
|
{
|
|
str hostname, rl_uri;
|
|
unsigned short port = 0;
|
|
xmlNodePtr rl_node = NULL;
|
|
xmlDocPtr rl_doc = NULL;
|
|
str unescaped_uri;
|
|
char buf[MAX_URI_SIZE];
|
|
|
|
|
|
uri.s = XMLNodeGetNodeContentByName(node, "resource-list", NULL);
|
|
if (uri.s == NULL)
|
|
{
|
|
LM_ERR("when extracting URI from node\n");
|
|
return -1;
|
|
}
|
|
uri.len = strlen(uri.s);
|
|
if (uri.len > MAX_URI_SIZE-1) {
|
|
LM_ERR("XCAP URI is too long\n");
|
|
return -1;
|
|
}
|
|
LM_DBG("got resource-list uri <%.*s>\n", uri.len, uri.s);
|
|
|
|
unescaped_uri.s = buf;
|
|
unescaped_uri.len = 0;
|
|
if (un_escape(&uri, &unescaped_uri) < 0) {
|
|
LM_ERR("Error un-escaping XCAP URI\n");
|
|
return -1;
|
|
}
|
|
unescaped_uri.s[unescaped_uri.len] = 0;
|
|
LM_DBG("got unescaped uri <%s>\n", unescaped_uri.s);
|
|
|
|
if(parse_xcap_uri(unescaped_uri.s, &hostname, &port, &rl_uri)>0)
|
|
{
|
|
if (rls_integrated_xcap_server == 1
|
|
&& (hostname.len == 0
|
|
|| check_self(&hostname, 0, PROTO_NONE) == 1))
|
|
{
|
|
LM_DBG("fetching local <resource-list/>\n");
|
|
if (rls_get_resource_list(&rl_uri, &username, &domain, &rl_node, &rl_doc)>0)
|
|
{
|
|
LM_DBG("calling myself for rl_node\n");
|
|
res = process_list_and_exec(rl_node, username, domain, function, param);
|
|
xmlFree(uri.s);
|
|
xmlFreeDoc(rl_doc);
|
|
}
|
|
else
|
|
{
|
|
LM_ERR("<resource-list/> not found\n");
|
|
xmlFree(uri.s);
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
LM_ERR("<resource-list/> is not local - unsupported at this time\n");
|
|
xmlFree(uri.s);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LM_ERR("unable to parse URI for <resource-list/>\n");
|
|
xmlFree(uri.s);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
if(xmlStrcasecmp(node->name,(unsigned char*)"entry")== 0)
|
|
{
|
|
uri.s = XMLNodeGetAttrContentByName(node, "uri");
|
|
if(uri.s== NULL)
|
|
{
|
|
LM_ERR("when extracting entry uri attribute\n");
|
|
return -1;
|
|
}
|
|
LM_DBG("uri= %s\n", uri.s);
|
|
if(function(uri.s, param)< 0)
|
|
{
|
|
LM_ERR("in function given as a parameter\n");
|
|
xmlFree(uri.s);
|
|
return -1;
|
|
}
|
|
xmlFree(uri.s);
|
|
}
|
|
else
|
|
if(xmlStrcasecmp(node->name,(unsigned char*)"list")== 0)
|
|
res = process_list_and_exec(node, username, domain, function, param);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
char* generate_string(int length)
|
|
{
|
|
static char buf[128];
|
|
int r,i;
|
|
|
|
if(length>= 128)
|
|
{
|
|
LM_ERR("requested length exceeds buffer size\n");
|
|
return NULL;
|
|
}
|
|
|
|
for(i=0; i<length; i++)
|
|
{
|
|
r= rand() % ('z'- 'A') + 'A';
|
|
if(r>'Z' && r< 'a')
|
|
r= '0'+ (r- 'Z');
|
|
|
|
sprintf(buf+i, "%c", r);
|
|
}
|
|
buf[length]= '\0';
|
|
|
|
return buf;
|
|
}
|
|
|
|
char* generate_cid(char* uri, int uri_len)
|
|
{
|
|
static char cid[512];
|
|
int len;
|
|
|
|
len= sprintf(cid, "%d.%.*s.%d", (int)time(NULL), uri_len, uri, rand());
|
|
cid[len]= '\0';
|
|
|
|
return cid;
|
|
}
|
|
|
|
char* get_auth_string(int flag)
|
|
{
|
|
switch(flag)
|
|
{
|
|
case ACTIVE_STATE: return "active";
|
|
case PENDING_STATE: return "pending";
|
|
case TERMINATED_STATE: return "terminated";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#define HTTP_PREFIX "http://"
|
|
#define HTTP_PREFIX_LEN 7
|
|
#define HTTP_PORT_DEFAULT 80
|
|
#define HTTPS_PREFIX "https://"
|
|
#define HTTPS_PREFIX_LEN 8
|
|
#define HTTPS_PORT_DEFAULT 443
|
|
#define LOCAL_PREFIX "/"
|
|
#define LOCAL_PREFIX_LEN 1
|
|
#define CHAR_PORT_LEN 5
|
|
|
|
int parse_xcap_uri(char *uri, str *host, unsigned short *port, str *path)
|
|
{
|
|
host->s = NULL;
|
|
host->len = 0;
|
|
*port = 0;
|
|
path->s = NULL;
|
|
path->len = 0;
|
|
|
|
if(strncmp(uri, HTTP_PREFIX, HTTP_PREFIX_LEN) == 0)
|
|
{
|
|
host->s = &uri[HTTP_PREFIX_LEN];
|
|
*port = HTTP_PORT_DEFAULT;
|
|
LM_DBG("resource list is on http server\n");
|
|
}
|
|
else
|
|
if(strncmp(uri, HTTPS_PREFIX, HTTPS_PREFIX_LEN) == 0)
|
|
{
|
|
host->s = &uri[HTTPS_PREFIX_LEN];
|
|
*port = HTTPS_PORT_DEFAULT;
|
|
LM_DBG("resource list is on https server\n");
|
|
}
|
|
else
|
|
if(strncmp(uri, LOCAL_PREFIX, LOCAL_PREFIX_LEN) == 0)
|
|
{
|
|
path->s = &uri[0];
|
|
LM_DBG("resource list is local\n");
|
|
}
|
|
else
|
|
{
|
|
LM_ERR("resource list is unidentifiable\n");
|
|
return -1;
|
|
}
|
|
|
|
if (host->s != NULL)
|
|
{
|
|
while(host->s[host->len] != '\0' && host->s[host->len] != ':' && host->s[host->len] != '/') host->len++;
|
|
|
|
if (host->s[host->len] == ':')
|
|
{
|
|
char char_port[CHAR_PORT_LEN + 1];
|
|
unsigned cur_pos = host->len + 1;
|
|
memset(char_port, '\0', CHAR_PORT_LEN + 1);
|
|
|
|
while(host->s[cur_pos] != '\0' && host->s[cur_pos] != '/') cur_pos++;
|
|
strncpy(char_port, &host->s[host->len + 1], MIN(cur_pos - host->len - 1, CHAR_PORT_LEN));
|
|
*port = atoi(char_port);
|
|
|
|
path->s = &host->s[cur_pos];
|
|
}
|
|
else
|
|
{
|
|
path->s = &host->s[host->len];
|
|
}
|
|
}
|
|
|
|
while(path->s[path->len] != '\0') path->len++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
#define MAX_PATH_LEN 127
|
|
int rls_get_resource_list(str *rl_uri, str *username, str *domain,
|
|
xmlNodePtr *rl_node, xmlDocPtr *xmldoc)
|
|
{
|
|
db_key_t query_cols[4];
|
|
db_val_t query_vals[4];
|
|
int n_query_cols = 0;
|
|
db_key_t result_cols[1];
|
|
int n_result_cols = 0;
|
|
db1_res_t *result = 0;
|
|
db_row_t *row;
|
|
db_val_t *row_vals;
|
|
int xcap_col;
|
|
str body;
|
|
int checked = 0;
|
|
str root, path = {0, 0};
|
|
char path_str[MAX_PATH_LEN + 1];
|
|
xmlXPathContextPtr xpathCtx = NULL;
|
|
xmlXPathObjectPtr xpathObj = NULL;
|
|
|
|
|
|
if (rl_uri==NULL || username==NULL || domain==NULL)
|
|
{
|
|
LM_ERR("invalid parameters\n");
|
|
return -1;
|
|
}
|
|
|
|
LM_DBG("rl_uri: %.*s", rl_uri->len, rl_uri->s);
|
|
|
|
root.s = rl_uri->s;
|
|
root.len = rl_uri->len;
|
|
while (checked < rl_uri->len)
|
|
{
|
|
if (checked < rl_uri->len - 3 && strncmp(rl_uri->s + checked, "/~~", 3) == 0)
|
|
{
|
|
root.len = checked;
|
|
checked += 3;
|
|
break;
|
|
}
|
|
checked++;
|
|
}
|
|
LM_DBG("doc: %.*s", root.len, root.s);
|
|
|
|
memset (path_str, '\0', MAX_PATH_LEN + 1);
|
|
path.s = path_str;
|
|
path.len = 0;
|
|
while (checked < rl_uri->len && path.len <= MAX_PATH_LEN)
|
|
{
|
|
if (rl_uri->s[checked] == '/')
|
|
{
|
|
strcat(path.s, "/xmlns:");
|
|
path.len += 7;
|
|
checked++;
|
|
}
|
|
else
|
|
{
|
|
path.s[path.len++] = rl_uri->s[checked];
|
|
checked++;
|
|
}
|
|
}
|
|
LM_DBG("path: %.*s", path.len, path.s);
|
|
|
|
query_cols[n_query_cols] = &str_username_col;
|
|
query_vals[n_query_cols].type = DB1_STR;
|
|
query_vals[n_query_cols].nul = 0;
|
|
query_vals[n_query_cols].val.str_val = *username;
|
|
n_query_cols++;
|
|
|
|
query_cols[n_query_cols] = &str_domain_col;
|
|
query_vals[n_query_cols].type = DB1_STR;
|
|
query_vals[n_query_cols].nul = 0;
|
|
query_vals[n_query_cols].val.str_val = *domain;
|
|
n_query_cols++;
|
|
|
|
query_cols[n_query_cols] = &str_doc_type_col;
|
|
query_vals[n_query_cols].type = DB1_INT;
|
|
query_vals[n_query_cols].nul = 0;
|
|
query_vals[n_query_cols].val.int_val = RESOURCE_LIST;
|
|
n_query_cols++;
|
|
|
|
query_cols[n_query_cols] = &str_doc_uri_col;
|
|
query_vals[n_query_cols].type = DB1_STR;
|
|
query_vals[n_query_cols].nul = 0;
|
|
query_vals[n_query_cols].val.str_val = root;
|
|
n_query_cols++;
|
|
|
|
if(rls_xcap_dbf.use_table(rls_xcap_db, &rls_xcap_table) < 0)
|
|
{
|
|
LM_ERR("in use_table-[table]=%.*s\n",
|
|
rls_xcap_table.len, rls_xcap_table.s);
|
|
return -1;
|
|
}
|
|
|
|
result_cols[xcap_col= n_result_cols++] = &str_doc_col;
|
|
|
|
if(rls_xcap_dbf.query(rls_xcap_db, query_cols, 0, query_vals, result_cols,
|
|
n_query_cols, n_result_cols, 0, &result)<0)
|
|
{
|
|
LM_ERR("failed querying table xcap for document: %.*s\n",
|
|
root.len, root.s);
|
|
if(result)
|
|
rls_xcap_dbf.free_result(rls_xcap_db, result);
|
|
return -1;
|
|
}
|
|
|
|
if(result == NULL)
|
|
{
|
|
LM_ERR("bad result\n");
|
|
return -1;
|
|
}
|
|
|
|
if(result->n<=0)
|
|
{
|
|
LM_DBG("No rl document found\n");
|
|
rls_xcap_dbf.free_result(rls_xcap_db, result);
|
|
return -1;
|
|
}
|
|
|
|
row = &result->rows[0];
|
|
row_vals = ROW_VALUES(row);
|
|
|
|
body.s = (char*)row_vals[xcap_col].val.string_val;
|
|
if(body.s==NULL)
|
|
{
|
|
LM_ERR("xcap doc is null\n");
|
|
goto error;
|
|
}
|
|
body.len = strlen(body.s);
|
|
if(body.len==0)
|
|
{
|
|
LM_ERR("xcap doc is empty\n");
|
|
goto error;
|
|
}
|
|
|
|
LM_DBG("rl document:\n%.*s", body.len, body.s);
|
|
*xmldoc = xmlParseMemory(body.s, body.len);
|
|
if(*xmldoc==NULL)
|
|
{
|
|
LM_ERR("while parsing XML memory. len = %d\n", body.len);
|
|
goto error;
|
|
}
|
|
|
|
if (path.len == 0)
|
|
{
|
|
/* No path specified - use all resource-lists. */
|
|
*rl_node = XMLDocGetNodeByName(*xmldoc,"resource-lists", NULL);
|
|
if(rl_node==NULL)
|
|
{
|
|
LM_ERR("no resource-lists node in XML document\n");
|
|
goto error;
|
|
}
|
|
}
|
|
else if (path.s != NULL)
|
|
{
|
|
xpathCtx = xmlXPathNewContext(*xmldoc);
|
|
if (xpathCtx == NULL)
|
|
{
|
|
LM_ERR("unable to create new XPath context");
|
|
goto error;
|
|
}
|
|
|
|
if (xmlXPathRegisterNs(xpathCtx, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:resource-lists") != 0)
|
|
{
|
|
LM_ERR("unable to register xmlns\n");
|
|
goto error;
|
|
}
|
|
|
|
xpathObj = xmlXPathEvalExpression(BAD_CAST path.s, xpathCtx);
|
|
if (xpathObj == NULL)
|
|
{
|
|
LM_ERR("unable to evaluate path\n");
|
|
goto error;
|
|
}
|
|
|
|
if (xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr <= 0)
|
|
{
|
|
LM_ERR("no nodes found\n");
|
|
goto error;
|
|
}
|
|
if (xpathObj->nodesetval->nodeTab[0] != NULL && xpathObj->nodesetval->nodeTab[0]->type != XML_ELEMENT_NODE)
|
|
{
|
|
LM_ERR("no nodes of the correct type found\n");
|
|
goto error;
|
|
|
|
}
|
|
|
|
*rl_node = xpathObj->nodesetval->nodeTab[0];
|
|
|
|
xmlXPathFreeObject(xpathObj);
|
|
xmlXPathFreeContext(xpathCtx);
|
|
}
|
|
|
|
rls_xcap_dbf.free_result(rls_xcap_db, result);
|
|
return 1;
|
|
|
|
error:
|
|
if(result!=NULL)
|
|
rls_xcap_dbf.free_result(rls_xcap_db, result);
|
|
if(xpathObj!=NULL)
|
|
xmlXPathFreeObject(xpathObj);
|
|
|
|
if(xpathCtx!=NULL)
|
|
xmlXPathFreeContext(xpathCtx);
|
|
if(xmldoc!=NULL)
|
|
xmlFreeDoc(*xmldoc);
|
|
|
|
return -1;
|
|
}
|