xcap_server.c

00001 /*
00002  * $Id$
00003  *
00004  * xcap_server module - builtin XCAP server
00005  *
00006  * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
00007  *
00008  * This file is part of Kamailio, a free SIP server.
00009  *
00010  * Kamailio is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version
00014  *
00015  * Kamailio is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License 
00021  * along with this program; if not, write to the Free Software 
00022  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00023  *
00024  */
00025 
00026 #include <stdio.h>
00027 #include <string.h>
00028 #include <stdlib.h>
00029 #include <sys/types.h>
00030 #include <unistd.h>
00031 #include <fcntl.h>
00032 #include <time.h>
00033 
00034 #include "../../lib/srdb1/db.h"
00035 #include "../../pt.h"
00036 #include "../../sr_module.h"
00037 #include "../../dprint.h"
00038 #include "../../error.h"
00039 #include "../../ut.h"
00040 #include "../../mem/mem.h"
00041 #include "../../data_lump.h"
00042 #include "../../data_lump_rpl.h"
00043 #include "../../mod_fix.h"
00044 #include "../../parser/parse_uri.h"
00045 #include "../../modules_k/xcap_client/xcap_callbacks.h"
00046 #include "../../modules/sl/sl.h"
00047 #include "../../lib/kcore/cmpapi.h"
00048 
00049 #include "xcap_misc.h"
00050 
00051 MODULE_VERSION
00052 
00053 #define XCAP_TABLE_VERSION   4
00054 
00055 
00056 static int xcaps_put_db(str* user, str *domain, xcap_uri_t *xuri, str *etag,
00057                 str* doc);
00058 static int xcaps_get_db_doc(str* user, str *domain, xcap_uri_t *xuri,
00059                 str *doc);
00060 static int xcaps_get_db_etag(str* user, str *domain, xcap_uri_t *xuri,
00061                 str *etag);
00062 static int xcaps_del_db(str* user, str *domain, xcap_uri_t *xuri);
00063 
00064 static int w_xcaps_put(sip_msg_t* msg, char* puri, char* ppath,
00065                 char* pbody);
00066 static int w_xcaps_get(sip_msg_t* msg, char* puri, char* ppath);
00067 static int w_xcaps_del(sip_msg_t* msg, char* puri, char* ppath);
00068 static int fixup_xcaps_put(void** param, int param_no);
00069 static int check_preconditions(sip_msg_t *msg, str etag_hdr);
00070 static int check_match_header(str body, str *etag);
00071 
00072 static int mod_init(void);
00073 static int child_init(int rank);
00074 static void destroy(void);
00075 
00076 int xcaps_xpath_ns_param(modparam_t type, void *val);
00077 
00078 int xcaps_path_get_auid_type(str *path);
00079 int xcaps_generate_etag_hdr(str *etag);
00080 
00081 static str xcaps_db_table = str_init("xcap");
00082 static str xcaps_db_url = str_init(DEFAULT_DB_URL);
00083 static int xcaps_init_time = 0;
00084 static int xcaps_etag_counter = 1;
00085 str xcaps_root = str_init("/xcap-root/");
00086 
00087 static str xcaps_buf = {0, 8192};
00088 #define XCAPS_ETAG_SIZE 128
00089 static char xcaps_etag_buf[XCAPS_ETAG_SIZE];
00090 
00091 static str str_id_col = str_init("id");
00092 static str str_source_col = str_init("source");
00093 static str str_doc_col = str_init("doc");
00094 static str str_etag_col = str_init("etag");
00095 static str str_username_col = str_init("username");
00096 static str str_domain_col = str_init("domain");
00097 static str str_doc_type_col = str_init("doc_type");
00098 static str str_doc_uri_col = str_init("doc_uri");
00099 static str str_port_col = str_init("port");
00100 
00101 
00102 /* database connection */
00103 db1_con_t *xcaps_db = NULL;
00104 db_func_t xcaps_dbf;
00105 
00107 sl_api_t slb;
00108 
00109 static pv_export_t mod_pvs[] = {
00110         { {"xcapuri", sizeof("xcapuri")-1}, PVT_OTHER, pv_get_xcap_uri,
00111                 pv_set_xcap_uri, pv_parse_xcap_uri_name, 0, 0, 0 },
00112         { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
00113 };
00114 
00115 static param_export_t params[] = {
00116         { "db_url",             STR_PARAM, &xcaps_db_url.s    },
00117         { "xcap_table", STR_PARAM, &xcaps_db_table.s  },
00118         { "xcap_root",  STR_PARAM, &xcaps_root.s  },
00119         { "buf_size",   INT_PARAM, &xcaps_buf.len  },
00120         { "xml_ns",     STR_PARAM|USE_FUNC_PARAM, (void*)xcaps_xpath_ns_param },
00121         { 0, 0, 0 }
00122 };
00123 
00124 static cmd_export_t cmds[]={
00125         {"xcaps_put",   (cmd_function)w_xcaps_put,   3,
00126                         fixup_xcaps_put,  0, REQUEST_ROUTE},
00127         {"xcaps_get",   (cmd_function)w_xcaps_get,   2,
00128                         fixup_xcaps_put,  0, REQUEST_ROUTE},
00129         {"xcaps_del",   (cmd_function)w_xcaps_del,   2,
00130                         fixup_xcaps_put,  0, REQUEST_ROUTE},
00131         {0,0,0,0,0,0}
00132 };
00133 
00134 
00136 struct module_exports exports= {
00137         "xcap_server",                          /* module name */
00138         DEFAULT_DLFLAGS,                        /* dlopen flags */
00139         cmds,                                           /* exported functions */
00140         params,                                         /* exported parameters */
00141         0,                                              /* exported statistics */
00142         0,                                                      /* exported MI functions */
00143         mod_pvs,                                        /* exported pseudo-variables */
00144         0,                                                      /* extra processes */
00145         mod_init,                                       /* module initialization function */
00146         0,                                                      /* response handling function */
00147         destroy,                    /* destroy function */
00148         child_init                                      /* per-child init function */
00149 };
00150 
00154 static int mod_init(void)
00155 {
00156 
00157         xcaps_db_url.len   = (xcaps_db_url.s) ? strlen(xcaps_db_url.s) : 0;
00158         xcaps_db_table.len = (xcaps_db_table.s) ? strlen(xcaps_db_table.s) : 0;
00159         xcaps_root.len     = (xcaps_root.s) ? strlen(xcaps_root.s) : 0;
00160         
00161         if(xcaps_buf.len<=0)
00162         {
00163                 LM_ERR("invalid buffer size\n");
00164                 return -1;
00165         }
00166 
00167         xcaps_buf.s = (char*)pkg_malloc(xcaps_buf.len+1);
00168         if(xcaps_buf.s==NULL)
00169         {
00170                 LM_ERR("no pkg\n");
00171                 return -1;
00172         }
00173 
00174         /* binding to mysql module  */
00175         if (db_bind_mod(&xcaps_db_url, &xcaps_dbf))
00176         {
00177                 LM_ERR("Database module not found\n");
00178                 return -1;
00179         }
00180         
00181         if (!DB_CAPABILITY(xcaps_dbf, DB_CAP_ALL)) {
00182                 LM_ERR("Database module does not implement all functions"
00183                                 " needed by the module\n");
00184                 return -1;
00185         }
00186 
00187         xcaps_db = xcaps_dbf.init(&xcaps_db_url);
00188         if (xcaps_db==NULL)
00189         {
00190                 LM_ERR("connecting to database\n");
00191                 return -1;
00192         }
00193 
00194         if(db_check_table_version(&xcaps_dbf, xcaps_db, &xcaps_db_table,
00195                                 XCAP_TABLE_VERSION) < 0) {
00196                 LM_ERR("error during table version check.\n");
00197                 return -1;
00198         }
00199         xcaps_dbf.close(xcaps_db);
00200         xcaps_db = NULL;
00201 
00202         /* bind the SL API */
00203         if (sl_load_api(&slb)!=0) {
00204                 LM_ERR("cannot bind to SL API\n");
00205                 return -1;
00206         }
00207 
00208         xcaps_init_time = (int)time(NULL);
00209         return 0;
00210 }
00211 
00215 static int child_init(int rank)
00216 {
00217         if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
00218                 return 0; /* do nothing for the main process */
00219 
00220         if((xcaps_db = xcaps_dbf.init(&xcaps_db_url))==NULL)
00221         {
00222                 LM_ERR("cannot connect to db\n");
00223                 return -1;
00224         }
00225         return 0;
00226 }
00227 
00231 static void destroy(void)
00232 {
00233         if(xcaps_db != NULL)
00234                 xcaps_dbf.close(xcaps_db);
00235 }
00236 
00237 
00241 static int xcaps_send_reply(sip_msg_t *msg, int code, str *reason,
00242                 str *hdrs, str *ctype, str *body)
00243 {
00244         str tbuf;
00245 
00246         if(hdrs->len>0)
00247         {
00248                 if (add_lump_rpl(msg, hdrs->s, hdrs->len, LUMP_RPL_HDR) == 0)
00249                 {
00250                         LM_ERR("failed to insert extra-headers lump\n");
00251                         return -1;
00252                 }
00253         }
00254 
00255         if(ctype->len>0)
00256         {
00257                 /* add content-type */
00258                 tbuf.len=sizeof("Content-Type: ") - 1 + ctype->len + CRLF_LEN;
00259                 tbuf.s=pkg_malloc(sizeof(char)*(tbuf.len));
00260 
00261                 if (tbuf.len==0)
00262                 {
00263                         LM_ERR("out of pkg memory\n");
00264                         return -1;
00265                 }
00266                 memcpy(tbuf.s, "Content-Type: ", sizeof("Content-Type: ") - 1);
00267                 memcpy(tbuf.s+sizeof("Content-Type: ") - 1, ctype->s, ctype->len);
00268                 memcpy(tbuf.s+sizeof("Content-Type: ") - 1 + ctype->len,
00269                                 CRLF, CRLF_LEN);
00270                 if (add_lump_rpl(msg, tbuf.s, tbuf.len, LUMP_RPL_HDR) == 0)
00271                 {
00272                         LM_ERR("failed to insert content-type lump\n");
00273                         pkg_free(tbuf.s);
00274                         return -1;
00275                 }
00276                 pkg_free(tbuf.s);
00277         }
00278         if(body->len>0)
00279         {
00280                 if (add_lump_rpl(msg, body->s, body->len, LUMP_RPL_BODY) < 0)
00281                 {
00282                         LM_ERR("Error while adding reply lump\n");
00283                         return -1;
00284                 }
00285         }
00286         if (slb.freply(msg, code, reason) < 0)
00287         {
00288                 LM_ERR("Error while sending reply\n");
00289                 return -1;
00290         }
00291         return 0;
00292 }
00293 
00297 int xcaps_xpath_hack(str *buf, int type)
00298 {
00299         char *match;
00300         char *repl;
00301         char c;
00302         char *p;
00303         char *start;
00304 
00305         if(buf==NULL || buf->len <=10)
00306                 return 0;
00307 
00308         if(type==0)
00309         {
00310                 match = " xmlns=";
00311                 repl  = " x____=";
00312         } else {
00313                 match = " x____=";
00314                 repl  = " xmlns=";
00315         }
00316 
00317         start = buf->s;
00318         c = buf->s[buf->len-1];
00319         buf->s[buf->len-1] = '\0';
00320         while((p = strstr(start, match))!=NULL)
00321         {
00322                 memcpy(p, repl, 7);
00323                 start = p + 7;
00324         }
00325         buf->s[buf->len-1] = c;
00326         return 0;
00327 }
00328 
00332 static int xcaps_put_db(str* user, str *domain, xcap_uri_t *xuri, str *etag,
00333                 str* doc)
00334 {
00335         db_key_t qcols[9], rcols[2], ucols[5];
00336         db_val_t qvals[9], uvals[5];
00337         db1_res_t *res = NULL;
00338         int ncols = 0, num_ucols = 0, nrows = 0;
00339 
00340         if(xcaps_check_doc_validity(doc)<0)
00341         {
00342                 LM_ERR("invalid xml doc to insert in database\n");
00343                 goto error;
00344         }
00345 
00346         qcols[ncols] = &str_username_col;
00347         qvals[ncols].type = DB1_STR;
00348         qvals[ncols].nul = 0;
00349         qvals[ncols].val.str_val = *user;
00350         ncols++;
00351         
00352         qcols[ncols] = &str_domain_col;
00353         qvals[ncols].type = DB1_STR;
00354         qvals[ncols].nul = 0;
00355         qvals[ncols].val.str_val = *domain;
00356         ncols++;
00357         
00358         qcols[ncols] = &str_doc_type_col;
00359         qvals[ncols].type = DB1_INT;
00360         qvals[ncols].nul = 0;
00361         qvals[ncols].val.int_val= xuri->type;
00362         ncols++;
00363 
00364         qcols[ncols] = &str_doc_uri_col;
00365         qvals[ncols].type = DB1_STR;
00366         qvals[ncols].nul = 0;
00367         qvals[ncols].val.str_val= xuri->adoc;
00368         ncols++;
00369 
00370         rcols[0] = &str_id_col;
00371 
00372         if (xcaps_dbf.use_table(xcaps_db, &xcaps_db_table) < 0) 
00373         {
00374                 LM_ERR("in use_table-[table]= %.*s\n", xcaps_db_table.len,
00375                                 xcaps_db_table.s);
00376                 goto error;
00377         }
00378 
00379         if (xcaps_dbf.query(xcaps_db, qcols, 0, qvals, rcols, ncols, 1, 0, &res) < 0)
00380         {
00381                 LM_ERR("in sql query\n");
00382                 goto error;
00383         }
00384 
00385         nrows = RES_ROW_N(res);
00386         xcaps_dbf.free_result(xcaps_db, res);
00387 
00388         if (nrows == 0)
00389         {
00390                 qcols[ncols] = &str_doc_col;
00391                 qvals[ncols].type = DB1_BLOB;
00392                 qvals[ncols].nul = 0;
00393                 qvals[ncols].val.str_val= *doc;
00394                 ncols++;
00395 
00396                 qcols[ncols] = &str_etag_col;
00397                 qvals[ncols].type = DB1_STR;
00398                 qvals[ncols].nul = 0;
00399                 qvals[ncols].val.str_val= *etag;
00400                 ncols++;
00401 
00402                 qcols[ncols] = &str_source_col;
00403                 qvals[ncols].type = DB1_INT;
00404                 qvals[ncols].nul = 0;
00405                 qvals[ncols].val.int_val = 0;
00406                 ncols++;
00407 
00408                 qcols[ncols] = &str_port_col;
00409                 qvals[ncols].type = DB1_INT;
00410                 qvals[ncols].nul = 0;
00411                 qvals[ncols].val.int_val = 0;
00412                 ncols++;
00413 
00414                 if(xcaps_dbf.insert(xcaps_db, qcols, qvals, ncols)< 0)
00415                 {
00416                         LM_ERR("in sql insert\n");
00417                         goto error;
00418                 }
00419         }
00420         else if (nrows == 1)
00421         {
00422                 ucols[num_ucols] = &str_doc_col;
00423                 uvals[num_ucols].type = DB1_BLOB;
00424                 uvals[num_ucols].nul = 0;
00425                 uvals[num_ucols].val.str_val= *doc;
00426                 num_ucols++;
00427 
00428                 ucols[num_ucols] = &str_etag_col;
00429                 uvals[num_ucols].type = DB1_STR;
00430                 uvals[num_ucols].nul = 0;
00431                 uvals[num_ucols].val.str_val= *etag;
00432                 num_ucols++;
00433 
00434                 ucols[num_ucols] = &str_source_col;
00435                 uvals[num_ucols].type = DB1_INT;
00436                 uvals[num_ucols].nul = 0;
00437                 uvals[num_ucols].val.int_val = 0;
00438                 num_ucols++;
00439 
00440                 ucols[num_ucols] = &str_port_col;
00441                 uvals[num_ucols].type = DB1_INT;
00442                 uvals[num_ucols].nul = 0;
00443                 uvals[num_ucols].val.int_val = 0;
00444                 num_ucols++;
00445 
00446                 if (xcaps_dbf.update(xcaps_db, qcols, 0, qvals, ucols, uvals, ncols, num_ucols) < 0)
00447                 {
00448                         LM_ERR("in sql update\n");
00449                         goto error;
00450                 }
00451         }
00452         else
00453         {
00454                 LM_ERR("found %d copies of the same document in XCAP Server\n", nrows);
00455                 goto error;
00456         }
00457 
00458         return 0;
00459 error:
00460         return -1;
00461 }
00462 
00463 static str xcaps_str_empty      = {"", 0};
00464 static str xcaps_str_ok         = {"OK", 2};
00465 static str xcaps_str_srverr     = {"Server error", 12};
00466 static str xcaps_str_notfound   = {"Not found", 9};
00467 static str xcaps_str_precon     = {"Precondition Failed", 19};
00468 static str xcaps_str_notmod     = {"Not Modified", 12};
00469 static str xcaps_str_appxml     = {"application/xml", 15};
00470 static str xcaps_str_apprlxml   = {"application/resource-lists+xml", 30};
00471 static str xcaps_str_apprsxml   = {"application/rls-services+xml", 28};
00472 #if 0
00473 static str xcaps_str_nocontent  = {"No content", 10};
00474 static str xcaps_str_appxcxml   = {"application/xcap-caps+xml", 25};
00475 static str xcaps_str_appsexml   = {"application/vnd.oma.search+xml", 30};
00476 #endif
00477 static str xcaps_str_appapxml   = {"application/auth-policy+xml", 27};
00478 static str xcaps_str_appupxml   = {"application/vnd.oma.user-profile+xml", 36}; 
00479 static str xcaps_str_apppcxml   = {"application/vnd.oma.pres-content+xml", 36};
00480 static str xcaps_str_apppdxml   = {"application/pidf+xml", 20};
00481 
00482 
00486 static int w_xcaps_put(sip_msg_t* msg, char* puri, char* ppath,
00487                 char* pbody)
00488 {
00489         struct sip_uri turi;
00490         str uri;
00491         str path;
00492         str body = {0, 0};
00493         str etag;
00494         str etag_hdr;
00495         str tbuf;
00496         str nbuf = {0, 0};
00497         pv_elem_t *xm;
00498         xcap_uri_t xuri;
00499 
00500         if(puri==0 || ppath==0 || pbody==0)
00501         {
00502                 LM_ERR("invalid parameters\n");
00503                 goto error;
00504         }
00505 
00506         if(fixup_get_svalue(msg, (gparam_p)puri, &uri)!=0)
00507         {
00508                 LM_ERR("unable to get uri\n");
00509                 goto error;
00510         }
00511         if(uri.s==NULL || uri.len == 0)
00512         {
00513                 LM_ERR("invalid uri parameter\n");
00514                 goto error;
00515         }
00516 
00517         if(fixup_get_svalue(msg, (gparam_p)ppath, &path)!=0)
00518         {
00519                 LM_ERR("unable to get path\n");
00520                 goto error;
00521         }
00522         if(path.s==NULL || path.len == 0)
00523         {
00524                 LM_ERR("invalid path parameter\n");
00525                 goto error;
00526         }
00527 
00528         xm = (pv_elem_t*)pbody;
00529         body.len = xcaps_buf.len - 1;
00530         if(pv_printf(msg, xm, xcaps_buf.s, &body.len)<0)
00531         {
00532                 LM_ERR("unable to get body\n");
00533                 goto error;
00534         }
00535         if(body.len <= 0)
00536         {
00537                 LM_ERR("invalid body parameter\n");
00538                 goto error;
00539         }
00540         body.s = (char*)pkg_malloc(body.len+1);
00541         if(body.s==NULL)
00542         {
00543                 LM_ERR("no more pkg\n");
00544                 goto error;
00545         }
00546 
00547         memcpy(body.s, xcaps_buf.s, body.len);
00548         body.s[body.len] = '\0';
00549 
00550         if(parse_uri(uri.s, uri.len, &turi)!=0)
00551         {
00552                 LM_ERR("parsing uri parameter\n");
00553                 goto error;
00554         }
00555         /* TODO: do xml parsing for validation */
00556 
00557         if(xcap_parse_uri(&path, &xcaps_root, &xuri)<0)
00558         {
00559                 LM_ERR("cannot parse xcap uri [%.*s]\n",
00560                                 path.len, path.s);
00561                 goto error;
00562         }
00563 
00564         xcaps_get_db_etag(&turi.user, &turi.host, &xuri, &etag);
00565         if(check_preconditions(msg, etag)!=1)
00566         {
00567                 xcaps_send_reply(msg, 412, &xcaps_str_precon, &xcaps_str_empty,
00568                                 &xcaps_str_empty, &xcaps_str_empty);
00569 
00570                 pkg_free(body.s);
00571                 return -2;
00572         }
00573 
00574         if(xuri.nss!=NULL && xuri.node.len>0)
00575         {
00576                 /* partial document upload
00577                  *   - fetch, update, delete and store
00578                  */
00579                 if(xcaps_get_db_doc(&turi.user, &turi.host, &xuri, &tbuf) != 0)
00580                 {
00581                         LM_ERR("could not fetch xcap document\n");
00582                         goto error;
00583                 }
00584                 if(xcaps_xpath_hack(&tbuf, 0)<0)
00585                 {
00586                         LM_ERR("could not hack xcap document\n");
00587                         goto error;
00588                 }
00589                 if(xcaps_xpath_set(&tbuf, &xuri.node, &body, &nbuf)<0)
00590                 {
00591                         LM_ERR("could not update xcap document\n");
00592                         goto error;
00593                 }
00594                 if(nbuf.len<=0)
00595                 {
00596                         LM_ERR("no new content\n");
00597                         goto error;
00598                 }
00599                 pkg_free(body.s);
00600                 body = nbuf;
00601                 if(xcaps_xpath_hack(&body, 1)<0)
00602                 {
00603                         LM_ERR("could not hack xcap document\n");
00604                         goto error;
00605                 }
00606         }
00607 
00608         if(xcaps_generate_etag_hdr(&etag_hdr)<0)
00609         {
00610                 LM_ERR("could not generate etag\n");
00611                 goto error;
00612         }
00613         etag.s = etag_hdr.s + 7; /* 'ETag: "' */
00614         etag.len = etag_hdr.len - 10; /* 'ETag: "  "\r\n' */
00615         if(xcaps_put_db(&turi.user, &turi.host,
00616                                 &xuri, &etag, &body)<0)
00617         {
00618                 LM_ERR("could not store document\n");
00619                 goto error;
00620         }
00621         xcaps_send_reply(msg, 200, &xcaps_str_ok, &etag_hdr,
00622                                 &xcaps_str_empty, &xcaps_str_empty);
00623         if(body.s!=NULL)
00624                 pkg_free(body.s);
00625         return 1;
00626 
00627 error:
00628         xcaps_send_reply(msg, 500, &xcaps_str_srverr, &xcaps_str_empty,
00629                                 &xcaps_str_empty, &xcaps_str_empty);
00630         if(body.s!=NULL)
00631                 pkg_free(body.s);
00632         return -1;
00633 }
00634 
00638 static int xcaps_get_db_doc(str* user, str *domain, xcap_uri_t *xuri, str *doc)
00639 {
00640         db_key_t qcols[3];
00641         db_val_t qvals[3];
00642         int ncols = 0;
00643         db_key_t rcols[3];
00644         int nrcols = 0;
00645         db1_res_t* db_res = NULL;
00646         str s;
00647 
00648         /* returned cols from table xcap*/
00649         rcols[nrcols] = &str_doc_col;
00650         nrcols++;
00651 
00652         /* query cols in xcap table*/
00653         qcols[ncols] = &str_username_col;
00654         qvals[ncols].type = DB1_STR;
00655         qvals[ncols].nul = 0;
00656         qvals[ncols].val.str_val = *user;
00657         ncols++;
00658 
00659         qcols[ncols] = &str_domain_col;
00660         qvals[ncols].type = DB1_STR;
00661         qvals[ncols].nul = 0;
00662         qvals[ncols].val.str_val = *domain;
00663         ncols++;
00664 
00665         qcols[ncols] = &str_doc_uri_col;
00666         qvals[ncols].type = DB1_STR;
00667         qvals[ncols].nul = 0;
00668         qvals[ncols].val.str_val= xuri->adoc;
00669         ncols++;
00670 
00671         if (xcaps_dbf.use_table(xcaps_db, &xcaps_db_table) < 0) 
00672         {
00673                 LM_ERR("in use_table-[table]= %.*s\n", xcaps_db_table.len,
00674                                 xcaps_db_table.s);
00675                 goto error;
00676         }
00677 
00678         if(xcaps_dbf.query(xcaps_db, qcols, NULL, qvals, rcols,
00679                                 ncols, nrcols, NULL, &db_res)< 0)
00680         {
00681                 LM_ERR("in sql query\n");
00682                 goto error;
00683         }
00684         if (RES_ROW_N(db_res) <= 0)
00685         {
00686                 LM_DBG("no document\n");
00687                 goto notfound;
00688         }
00689 
00690         /* doc */
00691         switch(RES_ROWS(db_res)[0].values[0].type)
00692         {
00693                 case DB1_STRING:
00694                         s.s=(char*)RES_ROWS(db_res)[0].values[0].val.string_val;
00695                         s.len=strlen(s.s);
00696                 break;
00697                 case DB1_STR:
00698                         s.len=RES_ROWS(db_res)[0].values[0].val.str_val.len;
00699                         s.s=(char*)RES_ROWS(db_res)[0].values[0].val.str_val.s;
00700                 break;
00701                 case DB1_BLOB:
00702                         s.len=RES_ROWS(db_res)[0].values[0].val.blob_val.len;
00703                         s.s=(char*)RES_ROWS(db_res)[0].values[0].val.blob_val.s;
00704                 break;
00705                 default:
00706                         s.len=0;
00707                         s.s=NULL;
00708         }
00709         if(s.len==0)
00710         {
00711                 LM_ERR("no xcap doc in db record\n");
00712                 goto error;
00713         }
00714         if(s.len>xcaps_buf.len-1)
00715         {
00716                 LM_ERR("xcap doc buffer overflow\n");
00717                 goto error;
00718         }
00719         doc->len = s.len;
00720         doc->s = xcaps_buf.s;
00721         memcpy(doc->s, s.s, s.len);
00722         doc->s[doc->len] = '\0';
00723 
00724         if(xcaps_check_doc_validity(doc)<0)
00725         {
00726                 LM_ERR("invalid xml doc retrieved from database\n");
00727                 goto error;
00728         }
00729 
00730         xcaps_dbf.free_result(xcaps_db, db_res);
00731         return 0;
00732 
00733 notfound:
00734         xcaps_dbf.free_result(xcaps_db, db_res);
00735         return 1;
00736 
00737 error:
00738         if(db_res!=NULL)
00739                 xcaps_dbf.free_result(xcaps_db, db_res);
00740         return -1;
00741 }
00742 
00748 static int xcaps_get_db_etag(str* user, str *domain, xcap_uri_t *xuri, str *etag)
00749 {
00750         db_key_t qcols[3];
00751         db_val_t qvals[3];
00752         int ncols = 0;
00753         db_key_t rcols[3];
00754         int nrcols = 0;
00755         db1_res_t* db_res = NULL;
00756         str s;
00757 
00758         /* returned cols from xcap table*/
00759         rcols[nrcols] = &str_etag_col;
00760         nrcols++;
00761 
00762         /* query cols in xcap table*/
00763         qcols[ncols] = &str_username_col;
00764         qvals[ncols].type = DB1_STR;
00765         qvals[ncols].nul = 0;
00766         qvals[ncols].val.str_val = *user;
00767         ncols++;
00768 
00769         qcols[ncols] = &str_domain_col;
00770         qvals[ncols].type = DB1_STR;
00771         qvals[ncols].nul = 0;
00772         qvals[ncols].val.str_val = *domain;
00773         ncols++;
00774 
00775         qcols[ncols] = &str_doc_uri_col;
00776         qvals[ncols].type = DB1_STR;
00777         qvals[ncols].nul = 0;
00778         qvals[ncols].val.str_val= xuri->adoc;
00779         ncols++;
00780 
00781         if (xcaps_dbf.use_table(xcaps_db, &xcaps_db_table) < 0) 
00782         {
00783                 LM_ERR("in use_table-[table]= %.*s\n", xcaps_db_table.len,
00784                                 xcaps_db_table.s);
00785                 goto error;
00786         }
00787 
00788         if(xcaps_dbf.query(xcaps_db, qcols, NULL, qvals, rcols,
00789                                 ncols, nrcols, NULL, &db_res)< 0)
00790         {
00791                 LM_ERR("in sql query\n");
00792                 goto error;
00793         }
00794         if (RES_ROW_N(db_res) <= 0)
00795         {
00796                 LM_DBG("no document\n");
00797                 goto notfound;
00798         }
00799         /* etag */
00800         switch(RES_ROWS(db_res)[0].values[0].type)
00801         {
00802                 case DB1_STRING:
00803                         s.s=(char*)RES_ROWS(db_res)[0].values[0].val.string_val;
00804                         s.len=strlen(s.s);
00805                 break;
00806                 case DB1_STR:
00807                         s.len=RES_ROWS(db_res)[0].values[0].val.str_val.len;
00808                         s.s=(char*)RES_ROWS(db_res)[0].values[0].val.str_val.s;
00809                 break;
00810                 case DB1_BLOB:
00811                         s.len=RES_ROWS(db_res)[0].values[0].val.blob_val.len;
00812                         s.s=(char*)RES_ROWS(db_res)[0].values[0].val.blob_val.s;
00813                 break;
00814                 default:
00815                         s.len=0;
00816                         s.s=NULL;
00817         }
00818         if(s.len==0)
00819         {
00820                 LM_ERR("no etag in db record\n");
00821                 goto error;
00822         }
00823         etag->len = snprintf(xcaps_etag_buf, XCAPS_ETAG_SIZE,
00824                         "ETag: \"%.*s\"\r\n", s.len, s.s);
00825         if(etag->len < 0)
00826         {
00827                 LM_ERR("error printing etag hdr\n ");
00828                 goto error;
00829         }
00830         if(etag->len >= XCAPS_ETAG_SIZE)
00831         {
00832                 LM_ERR("etag buffer overflow\n");
00833                 goto error;
00834         }
00835 
00836         etag->s = xcaps_etag_buf;
00837         etag->s[etag->len] = '\0';
00838 
00839         xcaps_dbf.free_result(xcaps_db, db_res);
00840         return 0;
00841 
00842 notfound:
00843         xcaps_dbf.free_result(xcaps_db, db_res);
00844         return 1;
00845 
00846 error:
00847         if(db_res!=NULL)
00848                 xcaps_dbf.free_result(xcaps_db, db_res);
00849         return -1;
00850 }
00851 
00855 static int w_xcaps_get(sip_msg_t* msg, char* puri, char* ppath)
00856 {
00857         struct sip_uri turi;
00858         str uri;
00859         str path;
00860         str etag;
00861         str body;
00862         int ret = 0;
00863         xcap_uri_t xuri;
00864         str *ctype;
00865 
00866         if(puri==0 || ppath==0)
00867         {
00868                 LM_ERR("invalid parameters\n");
00869                 return -1;
00870         }
00871 
00872         if(fixup_get_svalue(msg, (gparam_p)puri, &uri)!=0)
00873         {
00874                 LM_ERR("unable to get uri\n");
00875                 return -1;
00876         }
00877         if(uri.s==NULL || uri.len == 0)
00878         {
00879                 LM_ERR("invalid uri parameter\n");
00880                 return -1;
00881         }
00882 
00883         if(fixup_get_svalue(msg, (gparam_p)ppath, &path)!=0)
00884         {
00885                 LM_ERR("unable to get path\n");
00886                 return -1;
00887         }
00888         if(path.s==NULL || path.len == 0)
00889         {
00890                 LM_ERR("invalid path parameter\n");
00891                 return -1;
00892         }
00893 
00894         if(parse_uri(uri.s, uri.len, &turi)!=0)
00895         {
00896                 LM_ERR("parsing uri parameter\n");
00897                 goto error;
00898         }
00899 
00900         if(xcap_parse_uri(&path, &xcaps_root, &xuri)<0)
00901         {
00902                 LM_ERR("cannot parse xcap uri [%.*s]\n",
00903                                 path.len, path.s);
00904                 goto error;
00905         }
00906 
00907         if((ret=xcaps_get_db_etag(&turi.user, &turi.host, &xuri, &etag))<0)
00908         { 
00909                 LM_ERR("could not fetch etag for xcap document\n");
00910                 goto error;
00911         }
00912         if (ret==1)
00913         {
00914                 /* doc not found */
00915                 xcaps_send_reply(msg, 404, &xcaps_str_notfound, &xcaps_str_empty,
00916                                 &xcaps_str_empty, &xcaps_str_empty);
00917                 return 1;
00918         }
00919         
00920         if((ret=check_preconditions(msg, etag))==-1)
00921         {
00922                 xcaps_send_reply(msg, 412, &xcaps_str_precon, &xcaps_str_empty,
00923                                 &xcaps_str_empty, &xcaps_str_empty);
00924                 return -2;
00925         } else if (ret==-2) {
00926                 xcaps_send_reply(msg, 304, &xcaps_str_notmod, &xcaps_str_empty,
00927                                 &xcaps_str_empty, &xcaps_str_empty);
00928                 return -2;
00929         }
00930 
00931         if((ret=xcaps_get_db_doc(&turi.user, &turi.host, &xuri, &body))<0)
00932         {
00933                 LM_ERR("could not fetch xcap document\n");
00934                 goto error;
00935         }
00936         if(ret==0)
00937         {
00938                 /* doc found */
00939                 ctype = &xcaps_str_appxml;
00940                 if(xuri.type==RESOURCE_LIST)
00941                         ctype = &xcaps_str_apprlxml;
00942                 else if(xuri.type==PRES_RULES)
00943                         ctype = &xcaps_str_appapxml;
00944                 else if(xuri.type==RLS_SERVICE)
00945                         ctype = &xcaps_str_apprsxml;
00946                 else if(xuri.type==USER_PROFILE)
00947                         ctype = &xcaps_str_appupxml;
00948                 else if(xuri.type==PRES_CONTENT)
00949                         ctype = &xcaps_str_apppcxml;
00950                 else if(xuri.type==PIDF_MANIPULATION)
00951                         ctype = &xcaps_str_apppdxml;
00952                 xcaps_send_reply(msg, 200, &xcaps_str_ok, &etag,
00953                                 ctype, &body);
00954         } else {
00955                 /* doc not found */
00956                 xcaps_send_reply(msg, 404, &xcaps_str_notfound, &xcaps_str_empty,
00957                                 &xcaps_str_empty, &xcaps_str_empty);
00958         }
00959         return 1;
00960 
00961 error:
00962         xcaps_send_reply(msg, 500, &xcaps_str_srverr, &xcaps_str_empty,
00963                                 &xcaps_str_empty, &xcaps_str_empty);
00964         return -1;
00965 }
00966 
00967 
00971 static int xcaps_del_db(str* user, str *domain, xcap_uri_t *xuri)
00972 {
00973         db_key_t qcols[4];
00974         db_val_t qvals[4];
00975         int ncols = 0;
00976 
00977         /* delete in xcap table*/
00978         qcols[ncols] = &str_username_col;
00979         qvals[ncols].type = DB1_STR;
00980         qvals[ncols].nul = 0;
00981         qvals[ncols].val.str_val = *user;
00982         ncols++;
00983         
00984         qcols[ncols] = &str_domain_col;
00985         qvals[ncols].type = DB1_STR;
00986         qvals[ncols].nul = 0;
00987         qvals[ncols].val.str_val = *domain;
00988         ncols++;
00989         
00990         qcols[ncols] = &str_doc_uri_col;
00991         qvals[ncols].type = DB1_STR;
00992         qvals[ncols].nul = 0;
00993         qvals[ncols].val.str_val= xuri->adoc;
00994         ncols++;
00995 
00996         if (xcaps_dbf.use_table(xcaps_db, &xcaps_db_table) < 0) 
00997         {
00998                 LM_ERR("in use_table-[table]= %.*s\n", xcaps_db_table.len,
00999                                 xcaps_db_table.s);
01000                 goto error;
01001         }
01002         
01003         if(xcaps_dbf.delete(xcaps_db, qcols, NULL, qvals, ncols)< 0)
01004         {
01005                 LM_ERR("in sql delete\n");
01006                 goto error;
01007         }
01008 
01009         return 0;
01010 error:
01011         return -1;
01012 }
01013 
01014 
01018 static int w_xcaps_del(sip_msg_t* msg, char* puri, char* ppath)
01019 {
01020         struct sip_uri turi;
01021         str uri;
01022         str path;
01023         xcap_uri_t xuri;
01024         str body = {0, 0};
01025         str etag_hdr = {0, 0};
01026         str etag = {0, 0};
01027         str tbuf;
01028 
01029         if(puri==0 || ppath==0)
01030         {
01031                 LM_ERR("invalid parameters\n");
01032                 return -1;
01033         }
01034 
01035         if(fixup_get_svalue(msg, (gparam_p)puri, &uri)!=0)
01036         {
01037                 LM_ERR("unable to get uri\n");
01038                 return -1;
01039         }
01040         if(uri.s==NULL || uri.len == 0)
01041         {
01042                 LM_ERR("invalid uri parameter\n");
01043                 return -1;
01044         }
01045 
01046         if(fixup_get_svalue(msg, (gparam_p)ppath, &path)!=0)
01047         {
01048                 LM_ERR("unable to get path\n");
01049                 return -1;
01050         }
01051         if(path.s==NULL || path.len == 0)
01052         {
01053                 LM_ERR("invalid path parameter\n");
01054                 return -1;
01055         }
01056 
01057         if(parse_uri(uri.s, uri.len, &turi)!=0)
01058         {
01059                 LM_ERR("parsing uri parameter\n");
01060                 goto error;
01061         }
01062 
01063         if(xcap_parse_uri(&path, &xcaps_root, &xuri)<0)
01064         {
01065                 LM_ERR("cannot parse xcap uri [%.*s]\n",
01066                                 path.len, path.s);
01067                 goto error;
01068         }
01069 
01070         if(xcaps_get_db_etag(&turi.user, &turi.host, &xuri, &etag)!=0)
01071         { 
01072                 LM_ERR("could not fetch etag for xcap document\n");
01073                 goto error;
01074         }
01075 
01076         if(check_preconditions(msg, etag)!=1)
01077         {
01078                 xcaps_send_reply(msg, 412, &xcaps_str_precon, &xcaps_str_empty,
01079                                 &xcaps_str_empty, &xcaps_str_empty);
01080                 return -2;
01081         }
01082 
01083         if(xuri.nss==NULL)
01084         {
01085                 /* delete document */
01086                 if(xcaps_del_db(&turi.user, &turi.host, &xuri)<0)
01087                 {
01088                         LM_ERR("could not delete document\n");
01089                         goto error;
01090                 }
01091                 xcaps_send_reply(msg, 200, &xcaps_str_ok, &xcaps_str_empty,
01092                                 &xcaps_str_empty, &xcaps_str_empty);
01093         } else {
01094                 /* delete element */
01095                 if(xcaps_get_db_doc(&turi.user, &turi.host, &xuri, &tbuf) != 0)
01096                 {
01097                         LM_ERR("could not fetch xcap document\n");
01098                         goto error;
01099                 }
01100                 if(xcaps_xpath_hack(&tbuf, 0)<0)
01101                 {
01102                         LM_ERR("could not hack xcap document\n");
01103                         goto error;
01104                 }
01105                 if(xcaps_xpath_set(&tbuf, &xuri.node, NULL, &body)<0)
01106                 {
01107                         LM_ERR("could not update xcap document\n");
01108                         goto error;
01109                 }
01110                 if(body.len<=0)
01111                 {
01112                         LM_ERR("no new content\n");
01113                         goto error;
01114                 }
01115                 if(xcaps_xpath_hack(&body, 1)<0)
01116                 {
01117                         LM_ERR("could not hack xcap document\n");
01118                         goto error;
01119                 }
01120                 if(xcaps_generate_etag_hdr(&etag_hdr)<0)
01121                 {
01122                         LM_ERR("could not generate etag\n");
01123                         goto error;
01124                 }
01125                 etag.s = etag_hdr.s + 7; /* 'ETag: "' */
01126                 etag.len = etag_hdr.len - 10; /* 'ETag: "  "\r\n' */
01127                 if(xcaps_put_db(&turi.user, &turi.host,
01128                                 &xuri, &etag, &body)<0)
01129                 {
01130                         LM_ERR("could not store document\n");
01131                         goto error;
01132                 }
01133                 xcaps_send_reply(msg, 200, &xcaps_str_ok, &etag_hdr,
01134                                 &xcaps_str_empty, &xcaps_str_empty);
01135                 if(body.s!=NULL)
01136                         pkg_free(body.s);
01137                 return 1;
01138         }
01139         return 1;
01140 
01141 error:
01142         xcaps_send_reply(msg, 500, &xcaps_str_srverr, &xcaps_str_empty,
01143                                 &xcaps_str_empty, &xcaps_str_empty);
01144         if(body.s!=NULL)
01145                 pkg_free(body.s);
01146         return -1;
01147 }
01148 
01152 int xcaps_path_get_auid_type(str *path)
01153 {
01154         str s;
01155         char c;
01156         int ret;
01157 
01158         ret = -1;
01159         if(path==NULL)
01160                 return -1;
01161         if(path->len<xcaps_root.len)
01162                 return -1;
01163 
01164         if(strncmp(path->s, xcaps_root.s, xcaps_root.len)!=0)
01165         {
01166                 LM_ERR("missing xcap-root in [%.*s]\n", path->len, path->s);
01167                 return -1;
01168         }
01169 
01170         s.s = path->s + xcaps_root.len - 1;
01171         s.len = path->len - xcaps_root.len + 1;
01172 
01173         c = s.s[s.len];
01174         s.s[s.len] = '\0';
01175 
01176         if(s.len>12 && strstr(s.s, "/pres-rules/")!=NULL)
01177         {
01178                 LM_DBG("matched pres-rules\n");
01179                 ret = PRES_RULES;
01180                 goto done;
01181         }
01182 
01183         if(s.len>35 && strstr(s.s, "/org.openmobilealliance.pres-rules/")!=NULL)
01184         {
01185                 LM_DBG("matched oma pres-rules\n");
01186                 ret = PRES_RULES;
01187                 goto done;
01188         }
01189 
01190         if(s.len>14 && strstr(s.s, "/rls-services/")!=NULL)
01191         {
01192                 LM_DBG("matched rls-services\n");
01193                 ret = RLS_SERVICE;
01194                 goto done;
01195         }
01196 
01197         if(s.len>19 && strstr(s.s, "pidf-manipulation")!=NULL)
01198         {
01199                 LM_DBG("matched pidf-manipulation\n");
01200                 ret = PIDF_MANIPULATION;
01201                 goto done;
01202         }
01203 
01204         if(s.len>16 && strstr(s.s, "/resource-lists/")!=NULL)
01205         {
01206                 LM_DBG("matched resource-lists\n");
01207                 ret = RESOURCE_LIST;
01208                 goto done;
01209         }
01210 
01211         if(s.len>11 && strstr(s.s, "/xcap-caps/")!=NULL)
01212         {
01213                 LM_DBG("matched xcap-caps\n");
01214                 ret = XCAP_CAPS;
01215                 goto done;
01216         }
01217 
01218         if(s.len> 37 && strstr(s.s, "/org.openmobilealliance.user-profile/")!=NULL)
01219         {
01220                 LM_DBG("matched oma user-profile\n");
01221                 ret = USER_PROFILE;
01222                 goto done;
01223         }
01224 
01225         if(s.len> 37 && strstr(s.s, "/org.openmobilealliance.pres-content/")!=NULL)
01226         {
01227                 LM_DBG("matched oma pres-content\n");
01228                 ret = PRES_CONTENT;
01229                 goto done;
01230         }
01231 
01232         if(s.len>31 && strstr(s.s, "/org.openmobilealliance.search?")!=NULL)
01233         {
01234                 LM_DBG("matched oma search\n");
01235                 ret = SEARCH;
01236                 goto done;
01237         }
01238 
01239 done:
01240         s.s[s.len] = c;
01241         return ret;
01242 }
01243 
01247 int xcaps_generate_etag_hdr(str *etag)
01248 {
01249         etag->len = snprintf(xcaps_etag_buf, XCAPS_ETAG_SIZE,
01250                         "ETag: \"sr-%d-%d-%d\"\r\n", xcaps_init_time, my_pid(),
01251                         xcaps_etag_counter++);
01252         if(etag->len <0)
01253         {
01254                 LM_ERR("error printing etag\n ");
01255                 return -1;
01256         }
01257         if(etag->len >= XCAPS_ETAG_SIZE)
01258         {
01259                 LM_ERR("etag buffer overflow\n");
01260                 return -1;
01261         }
01262 
01263         etag->s = xcaps_etag_buf;
01264         etag->s[etag->len] = '\0';
01265         return 0;
01266 }
01267 
01271 static int fixup_xcaps_put(void** param, int param_no)
01272 {
01273         str s;
01274         pv_elem_t *xm;
01275         if (param_no == 1) {
01276             return fixup_spve_null(param, 1);
01277         } else if (param_no == 2) {
01278             return fixup_spve_null(param, 1);
01279         } else if (param_no == 3) {
01280                 s.s = (char*)(*param); s.len = strlen(s.s);
01281                 if(pv_parse_format(&s, &xm)<0)
01282                 {
01283                         LM_ERR("wrong format[%s]\n", (char*)(*param));
01284                         return E_UNSPEC;
01285                 }
01286                 *param = (void*)xm;
01287             return 0;
01288         }
01289         return 0;
01290 }
01291 
01292 static int check_preconditions(sip_msg_t *msg, str etag_hdr)
01293 {
01294         struct hdr_field* hdr = msg->headers;
01295         int ifmatch_found=0;
01296         int matched_matched=0;
01297         int matched_nonematched=0;
01298 
01299         if (etag_hdr.len > 0)
01300         {
01301                 str etag;
01302 
01303                 /* Keep the surrounding "s in the ETag */
01304                 etag.s = etag_hdr.s + 6; /* 'ETag: ' */
01305                 etag.len = etag_hdr.len - 8; /* 'ETag: "  "\r\n' */
01306 
01307                 while (hdr!=NULL)
01308                 {
01309                         if(cmp_hdrname_strzn(&hdr->name, "If-Match", 8)==0)
01310                         {
01311                                 ifmatch_found = 1;
01312                                 if (check_match_header(hdr->body, &etag)>0)
01313                                         matched_matched = 1;
01314                         }
01315                         else if (cmp_hdrname_strzn(&hdr->name, "If-None-Match", 13)==0)
01316                         {
01317                                 if (check_match_header(hdr->body, &etag)>0)
01318                                         matched_nonematched = 1;
01319                         }
01320                         hdr = hdr->next;
01321                 }
01322         } else {
01323                 while (hdr!=NULL)
01324                 {
01325                         if(cmp_hdrname_strzn(&hdr->name, "If-Match", 8)==0)
01326                                 ifmatch_found = 1;
01327 
01328                         hdr = hdr->next;
01329                 }
01330         }
01331 
01332         if (ifmatch_found == 1 && matched_matched == 0)
01333                 return -1;
01334         else if (matched_nonematched == 1)
01335                 return -2;
01336         else
01337                 return 1;
01338 }
01339 
01340 static int check_match_header(str body, str *etag)
01341 {
01342         do
01343         {
01344                 char *start_pos, *end_pos, *old_body_pos;
01345                 int cur_etag_len;
01346 
01347                 if ((start_pos = strchr(body.s, '"')) == NULL)
01348                         return -1;
01349                 if ((end_pos = strchr(start_pos + 1, '"')) == NULL)
01350                         return -1;
01351                 cur_etag_len = end_pos - start_pos + 1;
01352         
01353                 if (strncmp(start_pos, etag->s, cur_etag_len)==0)
01354                         return 1;
01355                 else if (strncmp(start_pos, "\"*\"", cur_etag_len)==0)
01356                         return 1;
01357 
01358                 old_body_pos = body.s;
01359                 if ((body.s = strchr(end_pos, ',')) == NULL)
01360                         return -1;
01361                 body.len -= body.s - old_body_pos;
01362         } while (body.len > 0);
01363 
01364         return -1;
01365 }