511adf79404deb29ffcf1f7c91ecea3688e5b3cb
[sip-router] / modules_k / presence / presence.c
1 /*
2  * $Id$
3  *
4  * presence module - presence server implementation
5  *
6  * Copyright (C) 2006 Voice Sistem S.R.L.
7  *
8  * This file is part of openser, a free SIP server.
9  *
10  * openser is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version
14  *
15  * openser is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License 
21  * along with this program; if not, write to the Free Software 
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  * History:
25  * --------
26  *  2006-08-15  initial version (anca)
27  */
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <sys/ipc.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <time.h>
37
38 #include "../../db/db.h"
39 #include "../../sr_module.h"
40 #include "../../dprint.h"
41 #include "../../error.h"
42 #include "../../ut.h"
43 #include "../../parser/parse_to.h"
44 #include "../../parser/parse_uri.h" 
45 #include "../../parser/parse_content.h"
46 #include "../../parser/parse_from.h"
47 #include "../../mem/mem.h"
48 #include "../../mem/shm_mem.h"
49 #include "../../usr_avp.h"
50 #include "../tm/tm_load.h"
51 #include "../sl/sl_api.h"
52 #include "../../pt.h"
53 #include "../../mi/mi.h"
54 #include "../pua/hash.h"
55 #include "publish.h"
56 #include "subscribe.h"
57 #include "event_list.h"
58 #include "bind_presence.h"
59 #include "notify.h"
60
61 MODULE_VERSION
62
63 #define S_TABLE_VERSION  2
64 #define P_TABLE_VERSION  2
65 #define ACTWATCH_TABLE_VERSION 7
66
67 char *log_buf = NULL;
68 static int clean_period=100;
69
70 /* database connection */
71 db_con_t *pa_db = NULL;
72 db_func_t pa_dbf;
73 char *presentity_table="presentity";
74 char *active_watchers_table = "active_watchers";
75 char *watchers_table= "watchers";  
76
77 int use_db=1;
78 str server_address= {0, 0};
79 evlist_t* EvList= NULL;
80
81 /* to tag prefix */
82 char* to_tag_pref = "10";
83
84 /* the avp storing the To tag for replies */
85 int reply_tag_avp_id = 25;
86
87 /* TM bind */
88 struct tm_binds tmb;
89 /* SL bind */
90 struct sl_binds slb;
91
92 /** module functions */
93
94 static int mod_init(void);
95 static int child_init(int);
96 void destroy(void);
97 int handle_publish(struct sip_msg*, char*, char*);
98 int handle_subscribe(struct sip_msg*, char*, char*);
99 int stored_pres_info(struct sip_msg* msg, char* pres_uri, char* s);
100 static int fixup_presence(void** param, int param_no);
101 struct mi_root* mi_refreshWatchers(struct mi_root* cmd, void* param);
102 int update_pw_dialogs(subs_t* subs, unsigned int hash_code, subs_t** subs_array);
103 int mi_child_init(void);
104
105 int counter =0;
106 int pid = 0;
107 char prefix='a';
108 int startup_time=0;
109 str db_url = {0, 0};
110 int expires_offset = 0;
111 int max_expires= 3600;
112 int shtable_size;
113 shtable_t subs_htable= NULL;
114 int fallback2db= 0;
115
116 int phtable_size;
117 phtable_t* pres_htable;
118
119 static cmd_export_t cmds[]=
120 {
121         {"handle_publish",              handle_publish,      0,    0,         REQUEST_ROUTE},
122         {"handle_publish",              handle_publish,      1,fixup_presence,REQUEST_ROUTE},
123         {"handle_subscribe",    handle_subscribe,        0,        0,         REQUEST_ROUTE},
124         {"bind_presence",(cmd_function)bind_presence,1,    0,            0         },
125         {0,                                             0,                                   0,    0,            0             }         
126 };
127
128 static param_export_t params[]={
129         { "db_url",                                     STR_PARAM, &db_url.s},
130         { "presentity_table",           STR_PARAM, &presentity_table},
131         { "active_watchers_table",      STR_PARAM, &active_watchers_table},
132         { "watchers_table",                     STR_PARAM, &watchers_table},
133         { "clean_period",                       INT_PARAM, &clean_period },
134         { "to_tag_pref",                        STR_PARAM, &to_tag_pref },
135         { "totag_avpid",                        INT_PARAM, &reply_tag_avp_id },
136         { "expires_offset",                     INT_PARAM, &expires_offset },
137         { "max_expires",                        INT_PARAM, &max_expires },
138         { "server_address",         STR_PARAM, &server_address.s},
139         { "hash_size",              INT_PARAM, &shtable_size},
140         { "fallback2db",            INT_PARAM, &fallback2db},
141         {0,0,0}
142 };
143
144 static mi_export_t mi_cmds[] = {
145         { "refreshWatchers", mi_refreshWatchers,    0,  0,  mi_child_init},
146         {  0,                0,                     0,  0,  0}
147 };
148
149 /** module exports */
150 struct module_exports exports= {
151         "presence",                                     /* module name */
152         DEFAULT_DLFLAGS,                        /* dlopen flags */
153         cmds,                                           /* exported functions */
154         params,                                         /* exported parameters */
155         0,                                                      /* exported statistics */
156         mi_cmds,                                        /* exported MI functions */
157         0,                                                      /* exported pseudo-variables */
158         0,                                                      /* extra processes */
159         mod_init,                                       /* module initialization function */
160         (response_function) 0,      /* response handling function */
161         (destroy_function) destroy, /* destroy function */
162         child_init                  /* per-child init function */
163 };
164
165 /**
166  * init module function
167  */
168 static int mod_init(void)
169 {
170         str _s;
171         int ver = 0;
172
173         DBG("PRESENCE: initializing module ...\n");
174
175         if(db_url.s== NULL)
176         {
177                 use_db= 0;
178                 DBG("PRESENCE:mod_init: presence module used for library"
179                                 " purpose only\n");
180                 EvList= init_evlist();
181                 if(!EvList)
182                 {
183                         LOG(L_ERR,"PRESENCE:mod_init: ERROR while initializing event list\n");
184                         return -1;
185                 }
186                 return 0;
187
188         }
189
190         if(expires_offset<0)
191                 expires_offset = 0;
192         
193         if(to_tag_pref==NULL || strlen(to_tag_pref)==0)
194                 to_tag_pref="10";
195
196         if(max_expires<= 0)
197                 max_expires = 3600;
198
199         if(server_address.s== NULL)
200         {
201                 DBG("PRESENCE:mod_init: server_address parameter not set in"
202                                 " configuration file\n");
203         }
204         
205         if(server_address.s)
206                 server_address.len= strlen(server_address.s);
207         else
208                 server_address.len= 0;
209
210         /* load SL API */
211         if(load_sl_api(&slb)==-1)
212         {
213                 LOG(L_ERR, "PRESENCE:mod_init:ERROR can't load sl functions\n");
214                 return -1;
215         }
216
217         /* load all TM stuff */
218         if(load_tm_api(&tmb)==-1)
219         {
220                 LOG(L_ERR, "PRESENCE:mod_init:ERROR can't load tm functions\n");
221                 return -1;
222         }
223
224         db_url.len = db_url.s ? strlen(db_url.s) : 0;
225         DBG("presence:mod_init: db_url=%s/%d/%p\n", ZSW(db_url.s), db_url.len,
226                         db_url.s);
227         
228         /* binding to mysql module  */
229         if (bind_dbmod(db_url.s, &pa_dbf))
230         {
231                 DBG("PRESENCE:mod_init: ERROR: Database module not found\n");
232                 return -1;
233         }
234         
235
236         if (!DB_CAPABILITY(pa_dbf, DB_CAP_ALL)) {
237                 LOG(L_ERR,"PRESENCE:mod_init: ERROR Database module does not implement "
238                     "all functions needed by the module\n");
239                 return -1;
240         }
241
242         pa_db = pa_dbf.init(db_url.s);
243         if (!pa_db)
244         {
245                 LOG(L_ERR,"PRESENCE:mod_init: Error while connecting database\n");
246                 return -1;
247         }
248         // verify table version 
249         _s.s = presentity_table;
250         _s.len = strlen(presentity_table);
251          ver =  table_version(&pa_dbf, pa_db, &_s);
252         if(ver!=P_TABLE_VERSION)
253         {
254                 LOG(L_ERR,"PRESENCE:mod_init: Wrong version v%d for table <%s>,"
255                                 " need v%d\n", ver, _s.s, P_TABLE_VERSION);
256                 return -1;
257         }
258         
259         _s.s = active_watchers_table;
260         _s.len = strlen(active_watchers_table);
261          ver =  table_version(&pa_dbf, pa_db, &_s);
262         if(ver!=ACTWATCH_TABLE_VERSION)
263         {
264                 LOG(L_ERR,"PRESENCE:mod_init: Wrong version v%d for table <%s>,"
265                                 " need v%d\n", ver, _s.s, ACTWATCH_TABLE_VERSION);
266                 return -1;
267         }
268
269         _s.s = watchers_table;
270         _s.len = strlen(watchers_table);
271          ver =  table_version(&pa_dbf, pa_db, &_s);
272         if(ver!=S_TABLE_VERSION)
273         {
274                 LOG(L_ERR,"PRESENCE:mod_init: Wrong version v%d for table <%s>,"
275                                 " need v%d\n", ver, _s.s, S_TABLE_VERSION);
276                 return -1;
277         }
278
279         EvList= init_evlist();
280         if(!EvList)
281         {
282                 LOG(L_ERR,"PRESENCE:mod_init: ERROR while initializing event list\n");
283                 return -1;
284         }       
285
286         if(clean_period<=0)
287         {
288                 DBG("PRESENCE: ERROR: mod_init: wrong clean_period \n");
289                 return -1;
290         }
291
292         if(shtable_size< 1)
293                 shtable_size= 512;
294         else
295                 shtable_size= 1<< shtable_size;
296
297         subs_htable= new_shtable();
298         if(subs_htable== NULL)
299         {
300                 LOG(L_ERR, "PRESENCE: ERROR: mod_init: ERROR while initializing"
301                                 " subscribe hash table\n");
302                 return -1;
303         }
304         if(restore_db_subs()< 0)
305         {
306                 LOG(L_ERR, "PRESENCE: ERROR: mod_init: ERROR while "
307                                 "restoring info from database\n");
308                 return -1;
309         }
310
311         if(phtable_size< 1)
312                 phtable_size= 256;
313         else
314                 phtable_size= 1<< phtable_size;
315
316         pres_htable= new_phtable();
317         if(pres_htable== NULL)
318         {
319                 LOG(L_ERR, "PRESENCE: ERROR: mod_init: ERROR while initializing"
320                                 "presentity hash table\n");
321                 return -1;
322         }
323         if(pres_htable_restore()< 0)
324         {
325                 LOG(L_ERR, "PRESENCE: ERROR: mod_init: ERROR filling in presentity"
326                                 " hash table from database\n");
327                 return -1;
328         }
329         
330         startup_time = (int) time(NULL);
331         
332         register_timer(msg_presentity_clean, 0, clean_period);
333         
334         register_timer(msg_watchers_clean, 0, clean_period);
335         
336         register_timer(timer_db_update, 0, clean_period);
337
338         if(pa_db)
339                 pa_dbf.close(pa_db);
340         pa_db = NULL;
341         
342         return 0;
343 }
344
345 /**
346  * Initialize children
347  */
348 static int child_init(int rank)
349 {
350         DBG("PRESENCE: init_child [%d]  pid [%d]\n", rank, getpid());
351         
352         pid = my_pid();
353         
354         if(use_db== 0)
355                 return 0;
356
357         if (pa_dbf.init==0)
358         {
359                 LOG(L_CRIT, "BUG: PRESENCE: child_init: database not bound\n");
360                 return -1;
361         }
362         pa_db = pa_dbf.init(db_url.s);
363         if (!pa_db)
364         {
365                 LOG(L_ERR,"PRESENCE: child %d: Error while connecting database\n",
366                                 rank);
367                 return -1;
368         }
369                 
370         if (pa_dbf.use_table(pa_db, presentity_table) < 0)  
371         {
372                 LOG(L_ERR, "PRESENCE: child %d: Error in use_table presentity_table\n", rank);
373                 return -1;
374         }
375         if (pa_dbf.use_table(pa_db, active_watchers_table) < 0)  
376         {
377                 LOG(L_ERR, "PRESENCE: child %d: Error in use_table active_watchers_table\n",
378                                 rank);
379                 return -1;
380         }
381         if (pa_dbf.use_table(pa_db, watchers_table) < 0)  
382         {
383                 LOG(L_ERR, "PRESENCE: child %d: Error in use_table watchers_table\n", rank);
384                 return -1;
385         }
386
387         DBG("PRESENCE: child %d: Database connection opened successfully\n", rank);
388         
389         return 0;
390 }
391
392 int mi_child_init(void)
393 {
394         if(use_db== 0)
395                 return 0;
396
397         if (pa_dbf.init==0)
398         {
399                 LOG(L_CRIT,"PRESENCE:mi_child_init:ERROR database not bound\n");
400                 return -1;
401         }
402         pa_db = pa_dbf.init(db_url.s);
403         if (!pa_db)
404         {
405                 LOG(L_ERR,"PRESENCE:mi_child_init:Error while connecting database\n");
406                 return -1;
407         }
408         
409         if (pa_dbf.use_table(pa_db, presentity_table) < 0)  
410         {
411                 LOG(L_ERR, "PRESENCE:mi_child_init: Error in use_table"
412                                 " presentity_table\n");
413                 return -1;
414         }
415         if (pa_dbf.use_table(pa_db, active_watchers_table) < 0)  
416         {
417                 LOG(L_ERR, "PRESENCE:mi_child_init: Error in use_table"
418                                 " active_watchers_table\n");
419                 return -1;
420         }
421         if (pa_dbf.use_table(pa_db, watchers_table) < 0)  
422         {
423                 LOG(L_ERR, "PRESENCE:mi_child_init: Error in use_table watchers_table\n");
424                 return -1;
425         }
426
427         DBG("PRESENCE:mi_child_init: Database connection opened successfully\n");
428         return 0;
429 }
430
431
432 /*
433  * destroy function
434  */
435 void destroy(void)
436 {
437         DBG("PRESENCE: destroy module ...\n");
438
439         if(subs_htable && pa_db)
440                 timer_db_update(0, 0);
441
442         if(subs_htable)
443                 destroy_shtable();
444         
445         if(pres_htable)
446                 destroy_phtable();
447
448         if(pa_db && pa_dbf.close)
449                 pa_dbf.close(pa_db);
450         
451         destroy_evlist();
452 }
453
454 static int fixup_presence(void** param, int param_no)
455 {
456         xl_elem_t *model;
457         if(*param)
458         {
459                 if(xl_parse_format((char*)(*param), &model, XL_DISABLE_COLORS)<0)
460                 {
461                         LOG(L_ERR, "PRESENCE:fixup_presence: ERROR wrong format[%s]\n",
462                                 (char*)(*param));
463                         return E_UNSPEC;
464                 }
465  
466                 *param = (void*)model;
467                 return 0;
468         }
469         LOG(L_ERR, "PRESENCE:fixup_presence: ERROR null format\n");
470         return E_UNSPEC;
471 }
472
473 /* 
474  *  mi cmd: refreshWatchers
475  *                      <presentity_uri> 
476  *                      <event>
477  *                      ?? should I receive the changed doc also?? 
478  *                      (faster- does not require a query) 
479  * */
480
481 struct mi_root* mi_refreshWatchers(struct mi_root* cmd, void* param)
482 {
483         struct mi_node* node= NULL;
484         str pres_uri, event;
485         struct sip_uri uri;
486         pres_ev_t* ev;
487         str* rules_doc= NULL;
488         int result;
489
490         DBG("PRESENCE:mi_refreshWatchers: start\n");
491         
492         node = cmd->node.kids;
493         if(node == NULL)
494                 return 0;
495
496         /* Get presentity URI */
497         pres_uri = node->value;
498         if(pres_uri.s == NULL || pres_uri.len== 0)
499         {
500                 LOG(L_ERR, "PRESENCE:mi_refreshWatchers: empty uri\n");
501                 return init_mi_tree(404, "Empty presentity URI", 20);
502         }
503         
504         node = node->next;
505         if(node == NULL)
506                 return 0;
507         event= node->value;
508         if(event.s== NULL || event.len== 0)
509         {
510                 LOG(L_ERR, "PRESENCE:mi_refreshWatchers: "
511                     "empty event parameter\n");
512                 return init_mi_tree(400, "Empty event parameter", 21);
513         }
514         DBG("PRESENCE:mi_refreshWatchers: event '%.*s'\n",
515             event.len, event.s);
516         
517         if(node->next!= NULL)
518         {
519                 LOG(L_ERR, "PRESENCE:mi_refreshWatchers: Too many parameters\n");
520                 return init_mi_tree(400, "Too many parameters", 19);
521         }
522
523         if(parse_uri(pres_uri.s, pres_uri.len, &uri)< 0)
524         {
525                 LOG(L_ERR, "PRESENCE:mi_refreshWatchers: ERROR parsing uri\n");
526                 goto error;
527         }
528
529         ev= contains_event(&event, NULL);
530         if(ev== NULL)
531         {
532                 LOG(L_ERR, "PRESENCE:mi_refreshWatchers: ERROR wrong event parameter\n");
533                 return 0;
534         }
535         
536         result= ev->get_rules_doc(&uri.user,&uri.host,&rules_doc);
537         if(result< 0 || rules_doc==NULL || rules_doc->s== NULL)
538         {
539                 LOG(L_ERR, "PRESENCE:mi_refreshWatchers:ERROR getting rules doc\n");
540                 goto error;
541         }
542         
543         if(update_watchers(pres_uri, ev, rules_doc)< 0)
544         {
545                 LOG(L_ERR, "PRESENCE:mi_refreshWatchers:ERROR updating watchers\n");
546                 goto error;
547         }
548
549         return init_mi_tree(200, "OK", 2);
550
551 error:
552         if(rules_doc)
553         {
554                 if(rules_doc->s)
555                         pkg_free(rules_doc->s);
556                 pkg_free(rules_doc);
557         }
558         return 0;
559 }
560
561 int update_watchers(str pres_uri, pres_ev_t* ev, str* rules_doc)
562 {
563         subs_t subs;
564         db_key_t query_cols[3], result_cols[5], update_cols[5];
565         db_val_t query_vals[3], update_vals[2];
566         int n_update_cols= 0, n_result_cols= 0, n_query_cols = 0;
567         db_res_t* result= NULL;
568         db_row_t *row ; 
569         db_val_t *row_vals ;
570         int i;
571         str w_user, w_domain, reason;
572         int status;
573         int status_col, w_user_col, w_domain_col, reason_col;
574         int u_status_col, u_reason_col;
575         subs_t* subs_array= NULL,* s;
576         unsigned int hash_code;
577                                 
578         if(ev->content_type.s== NULL)
579         {
580                 ev= contains_event(&ev->name, NULL);
581                 if(ev== NULL)
582                 {
583                         LOG(L_ERR,"PRESENCE:update_watchers:ERROR wrong event parameter\n");
584                         return 0;
585                 }
586         }
587
588         subs.pres_uri= pres_uri;
589         subs.event= ev;
590         subs.auth_rules_doc= rules_doc;
591
592         /* update in watchers_table */
593         query_cols[n_query_cols]= "p_uri";
594         query_vals[n_query_cols].nul= 0;
595         query_vals[n_query_cols].type= DB_STR;
596         query_vals[n_query_cols].val.str_val= pres_uri;
597         n_query_cols++;
598
599         query_cols[n_query_cols]= "event";
600         query_vals[n_query_cols].nul= 0;
601         query_vals[n_query_cols].type= DB_STR;
602         query_vals[n_query_cols].val.str_val= ev->name;
603         n_query_cols++;
604
605         result_cols[status_col= n_result_cols++]= "subs_status";
606         result_cols[reason_col= n_result_cols++]= "reason";
607         result_cols[w_user_col= n_result_cols++]= "w_user";
608         result_cols[w_domain_col= n_result_cols++]= "w_domain";
609         
610         update_cols[u_status_col= n_update_cols]= "subs_status";
611         update_vals[u_status_col].nul= 0;
612         update_vals[u_status_col].type= DB_INT;
613         n_update_cols++;
614
615         update_cols[u_reason_col= n_update_cols]= "reason";
616         update_vals[u_reason_col].nul= 0;
617         update_vals[u_reason_col].type= DB_STR;
618         n_update_cols++;
619
620         if (pa_dbf.use_table(pa_db, watchers_table) < 0) 
621         {
622                 LOG(L_ERR, "PRESENCE:update_watchers: ERROR in use_table\n");
623                 goto error;
624         }
625
626         if(pa_dbf.query(pa_db, query_cols, 0, query_vals, result_cols,n_query_cols,
627                                 n_result_cols, 0, &result)< 0)
628         {
629                 LOG(L_ERR, "PRESENCE:update_watchers: ERROR in sql query\n");
630                 goto error;
631         }
632         if(result== NULL)
633                 return 0;
634
635         if(result->n<= 0)
636                 goto done;
637
638         hash_code= core_hash(&pres_uri, &ev->name, shtable_size);
639         lock_get(&subs_htable[hash_code].lock);
640
641         for(i = 0; i< result->n; i++)
642         {
643                 row= &result->rows[i];
644                 row_vals = ROW_VALUES(row);
645
646                 status= row_vals[status_col].val.int_val;
647         
648                 reason.s= (char*)row_vals[reason_col].val.string_val;
649                 reason.len= reason.s?strlen(reason.s):0;
650
651                 w_user.s= (char*)row_vals[w_user_col].val.string_val;
652                 w_user.len= strlen(w_user.s);
653
654                 w_domain.s= (char*)row_vals[w_domain_col].val.string_val;
655                 w_domain.len= strlen(w_domain.s);
656
657                 subs.from_user= w_user;
658                 subs.from_domain= w_domain;
659                 memset(&subs.reason, 0, sizeof(str));
660                 if(ev->get_auth_status(&subs)< 0)
661                 {
662                         LOG(L_ERR, "PRESENCE:update_watchers: ERROR while getting status"
663                                         " from rules document\n");
664                         lock_release(&subs_htable[hash_code].lock);
665                         goto error;
666                 }
667                 if(subs.status!= status || reason.len!= subs.reason.len ||
668                         (reason.s && subs.reason.s && strncmp(reason.s, subs.reason.s,
669                                                                                                   reason.len)))
670                 {
671                         /* update in watchers_table */
672                         update_vals[u_status_col].val.int_val= subs.status;
673                         update_vals[u_reason_col].val.str_val= subs.reason;
674                         if (pa_dbf.use_table(pa_db, watchers_table) < 0) 
675                         {
676                                 LOG(L_ERR, "PRESENCE:update_watchers: ERROR in use_table\n");
677                                 lock_release(&subs_htable[hash_code].lock);
678                                 goto error;
679                         }
680
681                         if(pa_dbf.update(pa_db, query_cols, 0, query_vals, update_cols,
682                                                 update_vals, n_query_cols, n_update_cols)< 0)
683                         {
684                                 LOG(L_ERR, "PRESENCE:update_watchers: ERROR in sql update\n");
685                                 lock_release(&subs_htable[hash_code].lock);
686                                 goto error;
687                         }
688                         /* save in the list all affected dialogs */
689                         /* if status switches to terminated -> delete dialog */
690                         if(update_pw_dialogs(&subs, hash_code, &subs_array)< 0)
691                         {
692                                 LOG(L_ERR, "PRESENCE:update_watchers: ERROR extracting dialogs"
693                                                 "from [watcher]=%.*s@%.*s to [presentity]=%.*s\n",
694                                                 w_user.len, w_user.s, w_domain.len, w_domain.s, 
695                                                 pres_uri.len, pres_uri.s);
696                                 lock_release(&subs_htable[hash_code].lock);
697                                 goto error;
698                         }
699                  }
700                         
701         }
702         lock_release(&subs_htable[hash_code].lock);
703         pa_dbf.free_result(pa_db, result);
704         result= NULL;
705         s= subs_array;
706
707         while(s)
708         {
709                 if(notify(s, NULL, NULL, 0)< 0)
710                 {
711                         LOG(L_ERR, "PRESENCE:update_watchers: ERROR sending"
712                                         " Notify request\n");
713                         goto error;
714                 }
715                 s= s->next;
716         }
717         
718 done:
719         pa_dbf.free_result(pa_db, result);
720         free_subs_list(subs_array, PKG_MEM_TYPE);
721         return 0;
722
723 error:
724         if(result)
725                 pa_dbf.free_result(pa_db, result);
726         free_subs_list(subs_array, PKG_MEM_TYPE);
727         return 0;
728 }
729
730 int update_pw_dialogs(subs_t* subs, unsigned int hash_code, subs_t** subs_array)
731 {
732         subs_t* s, *ps, *cs;
733         int i= 0;
734
735         ps= subs_htable[hash_code].entries;
736         
737         while(ps && ps->next)
738         {
739                 s= ps->next;
740
741                 if(s->event== subs->event && s->pres_uri.len== subs->pres_uri.len &&
742                         s->from_user.len== subs->from_user.len && 
743                         s->from_domain.len==subs->from_domain.len &&
744                         strncmp(s->pres_uri.s, subs->pres_uri.s, subs->pres_uri.len)== 0 &&
745                         strncmp(s->from_user.s, subs->from_user.s, s->from_user.len)== 0 &&
746                         strncmp(s->from_domain.s,subs->from_domain.s,s->from_domain.len)==0)
747                 {
748                         i++;
749                         s->status= subs->status;
750                         s->reason= subs->reason;
751                         s->db_flag= UPDATEDB_FLAG;
752
753                         cs= mem_copy_subs(s, PKG_MEM_TYPE);
754                         if(cs== NULL)
755                         {
756                                 LOG(L_ERR, "PRESENCE:update_dialogs:ERROR copying subs_t"
757                                                 " stucture\n");
758                                 return -1;
759                         }
760                         cs->expires-= (int)time(NULL);
761                         cs->next= (*subs_array);
762                         (*subs_array)= cs;
763
764                         if(s->status== TERMINATED_STATUS)
765                         {
766                                 cs->expires= 0;
767                                 ps->next= s->next;
768                                 shm_free(s);
769                         }
770                 }
771                 ps= ps->next;
772         }
773         DBG("PRESENCE:update_dialogs:found %d matching dialogs\n", i);
774
775         return 0;
776 }