/* * $Id: xcap_functions.c 2230 2007-06-06 07:13:20Z anca_vamanu $ * * xcap_client module - XCAP client for Kamailio * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * History: * -------- * 2007-08-20 initial version (anca) */ #include #include #include #include #include #include #include #include #include #include "../../mem/mem.h" #include "../../lib/srdb1/db.h" #include "xcap_functions.h" #include "xcap_client.h" #include "../presence/hash.h" #define ETAG_HDR "Etag: " #define ETAG_HDR_LEN strlen("Etag: ") size_t write_function( void *ptr, size_t size, size_t nmemb, void *stream); char* get_xcap_path(xcap_get_req_t req); int bind_xcap(xcap_api_t* api) { if (!api) { LM_ERR("Invalid parameter value\n"); return -1; } api->get_elem= xcapGetElem; api->int_node_sel= xcapInitNodeSel; api->add_step= xcapNodeSelAddStep; api->add_terminal= xcapNodeSelAddTerminal; api->free_node_sel= xcapFreeNodeSel; api->register_xcb= register_xcapcb; api->getNewDoc= xcapGetNewDoc; return 0; } void xcapFreeNodeSel(xcap_node_sel_t* node) { step_t* s, *p; ns_list_t* n, *m; s= node->steps; while(s) { p= s; s= s->next; pkg_free(p->val.s); pkg_free(p); } n= node->ns_list; while(n) { m= n; n= n->next; pkg_free(m->value.s); pkg_free(m); } pkg_free(node); } xcap_node_sel_t* xcapInitNodeSel(void) { xcap_node_sel_t* nsel= NULL; nsel= (xcap_node_sel_t*)pkg_malloc(sizeof(xcap_node_sel_t)); if(nsel== NULL) { ERR_MEM(PKG_MEM_STR); } memset(nsel, 0, sizeof(xcap_node_sel_t)); nsel->steps= (step_t*)pkg_malloc(sizeof(step_t)); if(nsel->steps== NULL) { ERR_MEM(PKG_MEM_STR); } memset(nsel->steps, 0, sizeof(step_t)); nsel->last_step= nsel->steps; nsel->ns_list= (ns_list_t*)pkg_malloc(sizeof(ns_list_t)); if(nsel->ns_list== NULL) { ERR_MEM(PKG_MEM_STR); } memset(nsel->ns_list, 0, sizeof(ns_list_t)); nsel->last_ns= nsel->ns_list; return nsel; error: if(nsel) { if(nsel->steps) pkg_free(nsel->steps); if(nsel->ns_list) pkg_free(nsel->ns_list); pkg_free(nsel); } return NULL; } xcap_node_sel_t* xcapNodeSelAddStep(xcap_node_sel_t* curr_sel, str* name, str* namespace, int pos, attr_test_t* attr_test, str* extra_sel) { int size= 0; str new_step= {NULL, 0}; step_t* s= NULL; char ns_card= 'a'; ns_list_t* ns= NULL; if(name) size+= name->len; else size+= 1; if(namespace) size+= 2; if(pos> 0) size+= 7; if(attr_test) size+= 2+ attr_test->name.len+ attr_test->value.len; if(extra_sel) size+= 2+ extra_sel->len; new_step.s= (char*)pkg_malloc(size* sizeof(char)); if(new_step.s== NULL) { ERR_MEM(PKG_MEM_STR); } if(name) { if(namespace) { ns_card= curr_sel->ns_no+ 'a'; curr_sel->ns_no++; if(ns_card> 'z') { LM_ERR("Insuficient name cards for namespaces\n"); goto error; } new_step.len= sprintf(new_step.s, "%c:", ns_card); } memcpy(new_step.s+new_step.len, name->s, name->len); new_step.len+= name->len; } else memcpy(new_step.s+new_step.len, "*", 1); if(attr_test) { new_step.len+= sprintf(new_step.s+ new_step.len, "[%.*s=%.*s]", attr_test->name.len, attr_test->name.s, attr_test->value.len, attr_test->value.s); } if(pos> 0) new_step.len+= sprintf(new_step.s+ new_step.len, "[%d]", pos); if(extra_sel) { memcpy(new_step.s+ new_step.len, extra_sel->s, extra_sel->len); new_step.len= extra_sel->len; } s= (step_t*)pkg_malloc(sizeof(step_t)); if(s== NULL) { ERR_MEM(PKG_MEM_STR); } s->val= new_step; s->next= NULL; curr_sel->last_step->next= s; curr_sel->last_step= s; /* add the namespace binding if present */ if(namespace) { ns= (ns_list_t*)pkg_malloc(sizeof(ns_list_t)); if(ns== NULL) { ERR_MEM(PKG_MEM_STR); } ns->name= ns_card; ns->value.s= (char*)pkg_malloc(namespace->len* sizeof(char)); if(ns->value.s== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(ns->value.s, namespace->s, namespace->len); ns->value.len= namespace->len; curr_sel->last_ns->next= ns; curr_sel->last_ns= ns; } curr_sel->size+= 1+ new_step.len; if(namespace->len) { curr_sel->size+= namespace->len+ 3; } return curr_sel; error: if(new_step.s) pkg_free(new_step.s); if(s) pkg_free(s); if(ns) { if(ns->value.s) pkg_free(ns->value.s); pkg_free(ns); } return NULL; } xcap_node_sel_t* xcapNodeSelAddTerminal(xcap_node_sel_t* curr_sel, char* attr_sel, char* namespace_sel, char* extra_sel ) { return NULL; } char* get_node_selector(xcap_node_sel_t* node_sel) { char* buf= NULL; step_t* s; int len= 0; ns_list_t* ns_elem; buf= (char*)pkg_malloc((node_sel->size+ 10)* sizeof(char)); if(buf== NULL) { ERR_MEM(PKG_MEM_STR); } s= node_sel->steps->next; while(1) { memcpy(buf+ len, s->val.s, s->val.len); len+= s->val.len; s= s->next; if(s) buf[len++]= '/'; else break; } ns_elem= node_sel->ns_list; if(ns_elem) buf[len++]= '?'; while(ns_elem) { len+= sprintf(buf+ len, "xmlns(%c=%.*s)", ns_elem->name, ns_elem->value.len, ns_elem->value.s); ns_elem= ns_elem->next; } buf[len]= '\0'; return buf; error: return NULL; } char* xcapGetNewDoc(xcap_get_req_t req, str user, str domain) { char* etag= NULL; char* doc= NULL; db_key_t query_cols[9]; db_val_t query_vals[9]; int n_query_cols = 0; char* path= NULL; path= get_xcap_path(req); if(path== NULL) { LM_ERR("while constructing xcap path\n"); return NULL; } /* send HTTP request */ doc= send_http_get(path, req.port, NULL, 0, &etag); if(doc== NULL) { LM_DBG("the searched document was not found\n"); goto done; } if(etag== NULL) { LM_ERR("no etag found\n"); pkg_free(doc); doc= NULL; goto done; } /* insert in xcap table*/ 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 = user; 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= req.doc_sel.doc_type; n_query_cols++; query_cols[n_query_cols] = &str_doc_col; query_vals[n_query_cols].type = DB1_STRING; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.string_val= doc; n_query_cols++; query_cols[n_query_cols] = &str_etag_col; query_vals[n_query_cols].type = DB1_STRING; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.string_val= etag; n_query_cols++; query_cols[n_query_cols] = &str_source_col; query_vals[n_query_cols].type = DB1_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val= XCAP_CL_MOD; n_query_cols++; query_cols[n_query_cols] = &str_doc_uri_col; query_vals[n_query_cols].type = DB1_STRING; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.string_val= path; n_query_cols++; query_cols[n_query_cols] = &str_port_col; query_vals[n_query_cols].type = DB1_INT; query_vals[n_query_cols].nul = 0; query_vals[n_query_cols].val.int_val= req.port; n_query_cols++; if (xcap_dbf.use_table(xcap_db, &xcap_db_table) < 0) { LM_ERR("in use_table-[table]= %.*s\n", xcap_db_table.len, xcap_db_table.s); goto done; } if(xcap_dbf.insert(xcap_db, query_cols, query_vals, n_query_cols)< 0) { LM_ERR("in sql insert\n"); goto done; } done: pkg_free(path); return doc; } char* get_xcap_path(xcap_get_req_t req) { int len= 0, size; char* path= NULL; char* node_selector= NULL; len= (strlen(req.xcap_root)+ 1+ req.doc_sel.auid.len+ 5+ req.doc_sel.xid.len+ req.doc_sel.filename.len+ 50)* sizeof(char); if(req.node_sel) len+= req.node_sel->size; path= (char*)pkg_malloc(len); if(path== NULL) { ERR_MEM(PKG_MEM_STR); } if(req.node_sel) { node_selector= get_node_selector(req.node_sel); if(node_selector== NULL) { LM_ERR("while constructing node selector\n"); goto error; } } size= sprintf(path, "%s/%.*s/", req.xcap_root, req.doc_sel.auid.len, req.doc_sel.auid.s); if(req.doc_sel.type==USERS_TYPE) size+= sprintf(path+ size, "%s/%.*s/", "users", req.doc_sel.xid.len, req.doc_sel.xid.s); else size+= sprintf(path+ size, "%s/", "global"); size+= sprintf(path+ size, "%.*s", req.doc_sel.filename.len, req.doc_sel.filename.s); if(node_selector) { size+= sprintf(path+ size, "/~~%s", node_selector); } if(size> len) { LM_ERR("buffer size overflow\n"); goto error; } pkg_free(node_selector); return path; error: if(path) pkg_free(path); if(node_selector) pkg_free(node_selector); return NULL; } /* xcap_root must be a NULL terminated string */ char* xcapGetElem(xcap_get_req_t req, char** etag) { char* path= NULL; char* stream= NULL; path= get_xcap_path(req); if(path== NULL) { LM_ERR("while constructing xcap path\n"); return NULL; } stream= send_http_get(path, req.port, req.etag, req.match_type, etag); if(stream== NULL) { LM_DBG("the serched element was not found\n"); } if(etag== NULL) { LM_ERR("no etag found\n"); pkg_free(stream); stream= NULL; } if(path) pkg_free(path); return stream; } size_t get_xcap_etag( void *ptr, size_t size, size_t nmemb, void *stream) { int len= 0; char* etag= NULL; if(strncasecmp(ptr, ETAG_HDR, ETAG_HDR_LEN)== 0) { len= size* nmemb- ETAG_HDR_LEN; etag= (char*)pkg_malloc((len+ 1)* sizeof(char)); if(etag== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(etag, ptr+ETAG_HDR_LEN, len); etag[len]= '\0'; *((char**)stream)= etag; } return len; error: return -1; } char* send_http_get(char* path, unsigned int xcap_port, char* match_etag, int match_type, char** etag) { int len; char* stream= NULL; CURLcode ret_code; CURL* curl_handle= NULL; static char buf[128]; char* match_header= NULL; *etag= NULL; if(match_etag) { char* hdr_name= NULL; memset(buf, 0, 128* sizeof(char)); match_header= buf; hdr_name= (match_type==IF_MATCH)?"If-Match":"If-None-Match"; len=sprintf(match_header, "%s: %s\n", hdr_name, match_etag); match_header[len]= '\0'; } curl_handle = curl_easy_init(); curl_easy_setopt(curl_handle, CURLOPT_URL, path); curl_easy_setopt(curl_handle, CURLOPT_PORT, xcap_port); curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1); curl_easy_setopt(curl_handle, CURLOPT_STDERR, stdout); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_function); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &stream); curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, get_xcap_etag); curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, &etag); if(match_header) curl_easy_setopt(curl_handle, CURLOPT_HEADER, (long)match_header); /* non-2xx => error */ curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1); ret_code= curl_easy_perform(curl_handle ); if( ret_code== CURLE_WRITE_ERROR) { LM_ERR("while performing curl option\n"); if(stream) pkg_free(stream); stream= NULL; return NULL; } curl_global_cleanup(); return stream; } size_t write_function( void *ptr, size_t size, size_t nmemb, void *stream) { /* allocate memory and copy */ char* data; data= (char*)pkg_malloc(size* nmemb); if(data== NULL) { ERR_MEM(PKG_MEM_STR); } memcpy(data, (char*)ptr, size* nmemb); *((char**) stream)= data; return size* nmemb; error: return CURLE_WRITE_ERROR; }