presence_dialoginfo/notify_body.c

Go to the documentation of this file.
00001 /*
00002  * $Id: notify_body.c 1337 2006-12-07 18:05:05Z bogdan_iancu $
00003  *
00004  * presence_dialoginfo module -  
00005  *
00006  * Copyright (C) 2006 Voice Sistem S.R.L.
00007  * Copyright (C) 2008 Klaus Darilion, IPCom
00008  *
00009  * This file is part of Kamailio, a free SIP server.
00010  *
00011  * Kamailio is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version
00015  *
00016  * Kamailio is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00024  *
00025  * History:
00026  * --------
00027  *  2008-08-25  initial version (kd)
00028  */
00034 #define MAX_INT_LEN 11 /* 2^32: 10 chars + 1 char sign */
00035 
00036 #include <string.h>
00037 #include <stdlib.h>
00038 #include <libxml/parser.h>
00039 
00040 #include "../../mem/mem.h"
00041 #include "../presence/utils_func.h"
00042 #include "../presence/hash.h"
00043 #include "../presence/event_list.h"
00044 #include "../presence/presence.h"
00045 #include "../presence/presentity.h"
00046 #include "notify_body.h"
00047 #include "pidf.h"
00048 
00049 str* agregate_xmls(str* pres_user, str* pres_domain, str** body_array, int n);
00050 extern int force_single_dialog;
00051 
00052 void free_xml_body(char* body)
00053 {
00054         if(body== NULL)
00055                 return;
00056 
00057         xmlFree(body);
00058         body= NULL;
00059 }
00060 
00061 
00062 str* dlginfo_agg_nbody(str* pres_user, str* pres_domain, str** body_array, int n, int off_index)
00063 {
00064         str* n_body= NULL;
00065 
00066         LM_DBG("[pres_user]=%.*s [pres_domain]= %.*s, [n]=%d\n",
00067                         pres_user->len, pres_user->s, pres_domain->len, pres_domain->s, n);
00068 
00069         if(body_array== NULL)
00070                 return NULL;
00071 
00072         n_body= agregate_xmls(pres_user, pres_domain, body_array, n);
00073         LM_DBG("[n_body]=%p\n", n_body);
00074         if(n_body) {
00075                 LM_DBG("[*n_body]=%.*s\n",
00076                         n_body->len, n_body->s);
00077         }
00078         if(n_body== NULL && n!= 0)
00079         {
00080                 LM_ERR("while aggregating body\n");
00081         }
00082 
00083         xmlCleanupParser();
00084     xmlMemoryDump();
00085 
00086         return n_body;
00087 }       
00088 
00089 str* agregate_xmls(str* pres_user, str* pres_domain, str** body_array, int n)
00090 {
00091         int i, j= 0;
00092 
00093         xmlDocPtr  doc = NULL;
00094         xmlNodePtr root_node = NULL;
00095         xmlNsPtr   namespace = NULL;
00096 
00097         xmlNodePtr p_root= NULL;
00098         xmlDocPtr* xml_array ;
00099         xmlNodePtr node = NULL;
00100         char *state;
00101         int winner_priority = -1, priority ;
00102         xmlNodePtr winner_dialog_node = NULL ;
00103         str *body= NULL;
00104     char buf[MAX_URI_SIZE+1];
00105 
00106         LM_DBG("[pres_user]=%.*s [pres_domain]= %.*s, [n]=%d\n",
00107                         pres_user->len, pres_user->s, pres_domain->len, pres_domain->s, n);
00108 
00109         xml_array = (xmlDocPtr*)pkg_malloc( n*sizeof(xmlDocPtr));
00110         if(xml_array== NULL)
00111         {
00112                 LM_ERR("while allocating memory");
00113                 return NULL;
00114         }
00115         memset(xml_array, 0, n*sizeof(xmlDocPtr)) ;
00116 
00117         /* parse all the XML documents */
00118         for(i=0; i<n; i++)
00119         {
00120                 if(body_array[i] == NULL )
00121                         continue;
00122 
00123                 xml_array[j] = NULL;
00124                 xml_array[j] = xmlParseMemory( body_array[i]->s, body_array[i]->len );
00125                 
00126                 /* LM_DBG("parsing XML body: [n]=%d, [i]=%d, [j]=%d xml_array[j]=%p\n", n, i, j, xml_array[j] ); */
00127 
00128                 if( xml_array[j]== NULL)
00129                 {
00130                         LM_ERR("while parsing xml body message\n");
00131                         goto error;
00132                 }
00133                 j++;
00134 
00135         } 
00136 
00137         if(j== 0)  /* no body */
00138         {
00139                 if(xml_array)
00140                         pkg_free(xml_array);
00141                 return NULL;
00142         }
00143 
00144         /* n: number of bodies in total */
00145         /* j: number of useful bodies; created XML structures */
00146         /* i: loop counter */
00147         /* LM_DBG("number of bodies in total [n]=%d, number of useful bodies [j]=%d\n", n, j ); */
00148 
00149         /* create the new NOTIFY body  */
00150     if ( (pres_user->len + pres_domain->len + 1) > MAX_URI_SIZE) {
00151         LM_ERR("entity URI too long, maximum=%d\n", MAX_URI_SIZE);
00152         return NULL;
00153     }
00154     memcpy(buf, pres_user->s, pres_user->len);
00155     buf[pres_user->len] = '@';
00156     memcpy(buf + pres_user->len + 1, pres_domain->s, pres_domain->len);
00157     buf[pres_user->len + 1 + pres_domain->len]= '\0';
00158 
00159     doc = xmlNewDoc(BAD_CAST "1.0");
00160     if(doc==0)
00161         return NULL;
00162 
00163     root_node = xmlNewNode(NULL, BAD_CAST "dialog-info");
00164     if(root_node==0)
00165         goto error;
00166 
00167     xmlDocSetRootElement(doc, root_node);
00168         namespace = xmlNewNs(root_node, BAD_CAST "urn:ietf:params:xml:ns:dialog-info", NULL);
00169         if (!namespace) {
00170                 LM_ERR("creating namespace failed\n");
00171         }
00172         xmlSetNs(root_node, namespace);
00173         /* The version must be increased for each new document and is a 32bit int.
00174        As the version is different for each watcher, we can not set here the
00175        correct value. Thus, we just put here a placeholder which will be 
00176            replaced by the correct value in the aux_body_processing callback.
00177            Thus we have CPU intensive XML aggregation only once and can use
00178            quick search&replace in the per-watcher aux_body_processing callback.
00179            We use 11 chracters as an signed int (although RFC says unsigned int we
00180            use signed int as presence module stores "version" in DB as
00181            signed int) has max. 10 characters + 1 character for the sign
00182         */
00183     xmlNewProp(root_node, BAD_CAST "version", BAD_CAST "00000000000");
00184     xmlNewProp(root_node, BAD_CAST  "state",  BAD_CAST "full" );
00185     xmlNewProp(root_node, BAD_CAST "entity",  BAD_CAST buf);
00186 
00187         /* loop over all bodies and create the aggregated body */
00188         for(i=0; i<j; i++)
00189         {
00190                 /* LM_DBG("[n]=%d, [i]=%d, [j]=%d xml_array[i]=%p\n", n, i, j, xml_array[j] ); */
00191                 p_root= xmlDocGetRootElement(xml_array[i]);
00192                         if(p_root ==NULL) {
00193                                 LM_ERR("while geting the xml_tree root element\n");
00194                                 goto error;
00195                         }
00196                         if (p_root->children) {
00197                         for (node = p_root->children; node; node = node->next) {
00198                                 if (node->type == XML_ELEMENT_NODE) {
00199                                         LM_DBG("node type: Element, name: %s\n", node->name);
00200                                         /* we do not copy the node, but unlink it and then add it ot the new node
00201                                          * this destroys the original document but we do not need it anyway.
00202                                          * using "copy" instead of "unlink" would also copy the namespace which 
00203                                          * would then be declared redundant (libxml unfortunately can not remove 
00204                                          * namespaces)
00205                                          */
00206                                         if (!force_single_dialog || (j==1)) {
00207                                                 xmlUnlinkNode(node);
00208                                                 if(xmlAddChild(root_node, node)== NULL) {
00209                                                         LM_ERR("while adding child\n");
00210                                                         goto error;
00211                                                 }
00212                                         } else {
00213                                                 /* try to put only the most important into the XML document
00214                                                  * order of importance: terminated->trying->proceeding->confirmed->early
00215                                                  */
00216                                                 state = xmlNodeGetNodeContentByName(node, "state", NULL);
00217                                                 if (state) {
00218                                                         LM_DBG("state element content = %s\n", state);
00219                                                         priority = get_dialog_state_priority(state);
00220                                                         if (priority > winner_priority) {
00221                                                                 winner_priority = priority;
00222                                                                 LM_DBG("new winner priority = %s (%d)\n", state, winner_priority);
00223                                                                 winner_dialog_node = node;
00224                                                         }
00225                                                         xmlFree(state);
00226                                                 }
00227                                         }
00228                                 }
00229                         }
00230                 }
00231         }
00232 
00233         if (force_single_dialog && (j!=1)) {
00234                 xmlUnlinkNode(winner_dialog_node);
00235                 if(xmlAddChild(root_node, winner_dialog_node)== NULL) {
00236                         LM_ERR("while adding winner-child\n");
00237                         goto error;
00238                 }
00239         }
00240 
00241         body = (str*)pkg_malloc(sizeof(str));
00242         if(body == NULL) {
00243                 ERR_MEM(PKG_MEM_STR);
00244         }
00245 
00246         xmlDocDumpFormatMemory(doc,(xmlChar**)(void*)&body->s, 
00247                         &body->len, 1); 
00248 
00249         for(i=0; i<j; i++)
00250         {
00251                 if(xml_array[i]!=NULL)
00252                         xmlFreeDoc( xml_array[i]);
00253         }
00254         if (doc)
00255                 xmlFreeDoc(doc);
00256         if(xml_array!=NULL)
00257                 pkg_free(xml_array);
00258     
00259         xmlCleanupParser();
00260     xmlMemoryDump();
00261 
00262         return body;
00263 
00264 error:
00265         if(xml_array!=NULL)
00266         {
00267                 for(i=0; i<j; i++)
00268                 {
00269                         if(xml_array[i]!=NULL)
00270                                 xmlFreeDoc( xml_array[i]);
00271                 }
00272                 pkg_free(xml_array);
00273         }
00274         if(body)
00275                 pkg_free(body);
00276 
00277         return NULL;
00278 }
00279 
00280 
00281 int get_dialog_state_priority(char *state) {
00282         if (strcasecmp(state,"terminated") == 0)
00283                 return 0;
00284         if (strcasecmp(state,"trying") == 0)
00285                 return 1;
00286         if (strcasecmp(state,"proceeding") == 0)
00287                 return 2;
00288         if (strcasecmp(state,"confirmed") == 0)
00289                 return 3;
00290         if (strcasecmp(state,"early") == 0)
00291                 return 4;
00292 
00293         return 0;
00294 }
00295 
00296 
00297 str *dlginfo_body_setversion(subs_t *subs, str *body) {
00298         char *version_start=0;
00299         char version[MAX_INT_LEN + 2]; /* +2 becasue of trailing " and \0 */
00300         int version_len;
00301 
00302         if (!body) {
00303                 return NULL;
00304         }
00305 
00306         /* xmlDocDumpFormatMemory creates \0 terminated string */
00307         /* version parameters starts at minimum at character 34 */
00308         if (body->len < 41) {
00309                 LM_ERR("body string too short!\n");
00310                 return NULL;
00311         }
00312         version_start = strstr(body->s + 34, "version=");
00313         if (!version_start) {
00314             LM_ERR("version string not found!\n");
00315                 return NULL;
00316         }
00317         version_start += 9;
00318 
00319         /* safety check for placeholder - if it is body not set by the module,
00320          * don't update the version */
00321         if(strncmp(version_start, "00000000000\"", 12)!=0)
00322                 return NULL;
00323 
00324         version_len = snprintf(version, MAX_INT_LEN + 2,"%d\"", subs->version);
00325         if (version_len >= MAX_INT_LEN + 2) {
00326                 LM_ERR("failed to convert 'version' to string\n");
00327                 memcpy(version_start, "00000000000\"", 12);
00328                 return NULL;
00329         }
00330         /* Replace the placeholder 00000000000 with the version.
00331          * Put the padding behind the ""
00332          */
00333         LM_DBG("replace version with \"%s\n",version);
00334         memcpy(version_start, version, version_len);
00335         memset(version_start + version_len, ' ', 12 - version_len);
00336 
00337         return NULL;
00338 }