- doxygen, formatting changes and small changes in startup error msg
[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 /*! \defgroup presence
30  *  \brief A generic implementation of the SIP event package (PUBLISH, SUBSCRIBE, NOTIFY)
31  *
32  *         The OpenSER presence module is a generic module for SIP event packages, which is much more than presence.
33  *         It is extensible by developing other modules that use the internal developer API.
34  *         Examples:
35  *         - \ref presence_mwi
36  *         - \ref presence_xml
37  */
38
39 /*! \file
40  * \brief OpenSER presence module
41  * 
42  * \ingroup presence 
43  */
44
45
46 #include <stdio.h>
47 #include <string.h>
48 #include <stdlib.h>
49 #include <sys/types.h>
50 #include <sys/ipc.h>
51 #include <unistd.h>
52 #include <fcntl.h>
53 #include <time.h>
54
55 #include "../../db/db.h"
56 #include "../../sr_module.h"
57 #include "../../dprint.h"
58 #include "../../error.h"
59 #include "../../ut.h"
60 #include "../../parser/parse_to.h"
61 #include "../../parser/parse_uri.h" 
62 #include "../../parser/parse_content.h"
63 #include "../../parser/parse_from.h"
64 #include "../../mem/mem.h"
65 #include "../../mem/shm_mem.h"
66 #include "../../usr_avp.h"
67 #include "../tm/tm_load.h"
68 #include "../sl/sl_api.h"
69 #include "../../pt.h"
70 #include "../../mi/mi.h"
71 #include "../pua/hash.h"
72 #include "publish.h"
73 #include "subscribe.h"
74 #include "event_list.h"
75 #include "bind_presence.h"
76 #include "notify.h"
77
78 MODULE_VERSION
79
80 #define S_TABLE_VERSION  3
81 #define P_TABLE_VERSION  2
82 #define ACTWATCH_TABLE_VERSION 9
83
84 char *log_buf = NULL;
85 static int clean_period=100;
86
87 /* database connection */
88 db_con_t *pa_db = NULL;
89 db_func_t pa_dbf;
90 str presentity_table= str_init("presentity");
91 str active_watchers_table = str_init("active_watchers");
92 str watchers_table= str_init("watchers");
93
94 int use_db=1;
95 str server_address= {0, 0};
96 evlist_t* EvList= NULL;
97
98 /* to tag prefix */
99 char* to_tag_pref = "10";
100
101 /* TM bind */
102 struct tm_binds tmb;
103 /* SL bind */
104 struct sl_binds slb;
105
106 /** module functions */
107
108 static int mod_init(void);
109 static int child_init(int);
110 static void destroy(void);
111 int stored_pres_info(struct sip_msg* msg, char* pres_uri, char* s);
112 static int fixup_presence(void** param, int param_no);
113 static struct mi_root* mi_refreshWatchers(struct mi_root* cmd, void* param);
114 static int update_pw_dialogs(subs_t* subs, unsigned int hash_code, subs_t** subs_array);
115 int update_watchers_status(str pres_uri, pres_ev_t* ev, str* rules_doc);
116 static int mi_child_init(void);
117
118 int counter =0;
119 int pid = 0;
120 char prefix='a';
121 int startup_time=0;
122 str db_url = {0, 0};
123 int expires_offset = 0;
124 int max_expires= 3600;
125 int shtable_size= 9;
126 shtable_t subs_htable= NULL;
127 int fallback2db= 0;
128 int sphere_enable= 0;
129
130 int phtable_size= 9;
131 phtable_t* pres_htable;
132
133 static cmd_export_t cmds[]=
134 {
135         {"handle_publish",  (cmd_function)handle_publish,  0,    0,         0, REQUEST_ROUTE},
136         {"handle_publish",  (cmd_function)handle_publish,  1,fixup_presence,0, REQUEST_ROUTE},
137         {"handle_subscribe",(cmd_function)handle_subscribe,0,     0,        0, REQUEST_ROUTE},
138         {"bind_presence", (cmd_function)bind_presence,     1,     0,        0,  0},
139         {0,                     0,                         0,     0,        0,  0}
140 };
141
142 static param_export_t params[]={
143         { "db_url",                 STR_PARAM, &db_url.s},
144         { "presentity_table",       STR_PARAM, &presentity_table.s},
145         { "active_watchers_table",  STR_PARAM, &active_watchers_table.s},
146         { "watchers_table",         STR_PARAM, &watchers_table.s},
147         { "clean_period",           INT_PARAM, &clean_period },
148         { "to_tag_pref",            STR_PARAM, &to_tag_pref },
149         { "expires_offset",         INT_PARAM, &expires_offset },
150         { "max_expires",            INT_PARAM, &max_expires },
151         { "server_address",         STR_PARAM, &server_address.s},
152         { "subs_htable_size",       INT_PARAM, &shtable_size},
153         { "pres_htable_size",       INT_PARAM, &phtable_size},
154         { "fallback2db",            INT_PARAM, &fallback2db},
155         { "enable_sphere_check",    INT_PARAM, &sphere_enable},
156     {0,0,0}
157 };
158
159 static mi_export_t mi_cmds[] = {
160         { "refreshWatchers", mi_refreshWatchers,    0,  0,  mi_child_init},
161         {  0,                0,                     0,  0,  0}
162 };
163
164 /** module exports */
165 struct module_exports exports= {
166         "presence",                     /* module name */
167         DEFAULT_DLFLAGS,                /* dlopen flags */
168         cmds,                           /* exported functions */
169         params,                         /* exported parameters */
170         0,                              /* exported statistics */
171         mi_cmds,                        /* exported MI functions */
172         0,                              /* exported pseudo-variables */
173         0,                              /* extra processes */
174         mod_init,                       /* module initialization function */
175         (response_function) 0,          /* response handling function */
176         (destroy_function) destroy,     /* destroy function */
177         child_init                      /* per-child init function */
178 };
179
180 /**
181  * init module function
182  */
183 static int mod_init(void)
184 {
185         db_url.len = db_url.s ? strlen(db_url.s) : 0;
186         LM_DBG("db_url=%s/%d/%p\n", ZSW(db_url.s), db_url.len,db_url.s);
187         presentity_table.len = strlen(presentity_table.s);
188         active_watchers_table.len = strlen(active_watchers_table.s);
189         watchers_table.len = strlen(watchers_table.s);
190
191         LM_NOTICE("initializing module ...\n");
192
193         if(db_url.s== NULL)
194         {
195                 use_db= 0;
196                 LM_DBG("Presence module used for API library purpose only\n");
197                 EvList= init_evlist();
198                 if(!EvList)
199                 {
200                         LM_ERR("unsuccessful initialize event list\n");
201                         return -1;
202                 }
203                 return 0;
204
205         }
206
207         if(expires_offset<0)
208                 expires_offset = 0;
209         
210         if(to_tag_pref==NULL || strlen(to_tag_pref)==0)
211                 to_tag_pref="10";
212
213         if(max_expires<= 0)
214                 max_expires = 3600;
215
216         if(server_address.s== NULL)
217                 LM_DBG("server_address parameter not set in configuration file\n");
218         
219         if(server_address.s)
220                 server_address.len= strlen(server_address.s);
221         else
222                 server_address.len= 0;
223
224         /* load SL API */
225         if(load_sl_api(&slb)==-1)
226         {
227                 LM_ERR("Can't load sl functions. Module SL not loaded?\n");
228                 return -1;
229         }
230
231         /* load all TM stuff */
232         if(load_tm_api(&tmb)==-1)
233         {
234                 LM_ERR("Can't load tm functions. Module TM not loaded?\n");
235                 return -1;
236         }
237         
238         /* binding to database module  */
239         if (db_bind_mod(&db_url, &pa_dbf))
240         {
241                 LM_ERR("Database module not found\n");
242                 return -1;
243         }
244         
245
246         if (!DB_CAPABILITY(pa_dbf, DB_CAP_ALL))
247         {
248                 LM_ERR("Database module does not implement all functions"
249                                 " needed by presence module\n");
250                 return -1;
251         }
252
253         pa_db = pa_dbf.init(&db_url);
254         if (!pa_db)
255         {
256                 LM_ERR("Connection to database failed\n");
257                 return -1;
258         }
259         
260         /*verify table versions */
261         if((db_check_table_version(&pa_dbf, pa_db, &presentity_table, P_TABLE_VERSION) < 0) ||
262                 (db_check_table_version(&pa_dbf, pa_db, &active_watchers_table, ACTWATCH_TABLE_VERSION) < 0) ||
263                 (db_check_table_version(&pa_dbf, pa_db, &watchers_table, S_TABLE_VERSION) < 0)) {
264                         LM_ERR("error during table version check.\n");
265                         return -1;
266         }
267
268         EvList= init_evlist();
269         if(!EvList)
270         {
271                 LM_ERR("initializing event list\n");
272                 return -1;
273         }
274
275         if(clean_period<=0)
276         {
277                 LM_DBG("wrong clean_period \n");
278                 return -1;
279         }
280
281         if(shtable_size< 1)
282                 shtable_size= 512;
283         else
284                 shtable_size= 1<< shtable_size;
285
286         subs_htable= new_shtable(shtable_size);
287         if(subs_htable== NULL)
288         {
289                 LM_ERR(" initializing subscribe hash table\n");
290                 return -1;
291         }
292         if(restore_db_subs()< 0)
293         {
294                 LM_ERR("restoring subscribe info from database\n");
295                 return -1;
296         }
297
298         if(phtable_size< 1)
299                 phtable_size= 256;
300         else
301                 phtable_size= 1<< phtable_size;
302
303         pres_htable= new_phtable();
304         if(pres_htable== NULL)
305         {
306                 LM_ERR("initializing presentity hash table\n");
307                 return -1;
308         }
309         if(pres_htable_restore()< 0)
310         {
311                 LM_ERR("filling in presentity hash table from database\n");
312                 return -1;
313         }
314
315         startup_time = (int) time(NULL);
316         
317         register_timer(msg_presentity_clean, 0, clean_period);
318         
319         register_timer(msg_watchers_clean, 0, clean_period);
320         
321         register_timer(timer_db_update, 0, clean_period);
322
323         if(pa_db)
324                 pa_dbf.close(pa_db);
325         pa_db = NULL;
326
327         return 0;
328 }
329
330 /**
331  * Initialize children
332  */
333 static int child_init(int rank)
334 {
335         LM_NOTICE("init_child [%d]  pid [%d]\n", rank, getpid());
336
337         pid = my_pid();
338         
339         if(use_db== 0)
340                 return 0;
341
342         if (pa_dbf.init==0)
343         {
344                 LM_CRIT("child_init: database not bound\n");
345                 return -1;
346         }
347         pa_db = pa_dbf.init(&db_url);
348         if (!pa_db)
349         {
350                 LM_ERR("child %d: unsuccessful connecting to database\n", rank);
351                 return -1;
352         }
353         
354         if (pa_dbf.use_table(pa_db, &presentity_table) < 0)  
355         {
356                 LM_ERR( "child %d:unsuccessful use_table presentity_table\n", rank);
357                 return -1;
358         }
359
360         if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)  
361         {
362                 LM_ERR( "child %d:unsuccessful use_table active_watchers_table\n",
363                                 rank);
364                 return -1;
365         }
366
367         if (pa_dbf.use_table(pa_db, &watchers_table) < 0)  
368         {
369                 LM_ERR( "child %d:unsuccessful use_table watchers_table\n", rank);
370                 return -1;
371         }
372
373         LM_DBG("child %d: Database connection opened successfully\n", rank);
374         
375         return 0;
376 }
377
378 static int mi_child_init(void)
379 {
380         if(use_db== 0)
381                 return 0;
382
383
384
385
386         if (pa_dbf.init==0)
387         {
388                 LM_CRIT("database not bound\n");
389                 return -1;
390         }
391         pa_db = pa_dbf.init(&db_url);
392         if (!pa_db)
393         {
394                 LM_ERR("connecting database\n");
395                 return -1;
396         }
397         
398         if (pa_dbf.use_table(pa_db, &presentity_table) < 0)
399         {
400                 LM_ERR( "unsuccessful use_table presentity_table\n");
401                 return -1;
402         }
403
404         if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
405         {
406                 LM_ERR( "unsuccessful use_table active_watchers_table\n");
407                 return -1;
408         }
409
410         if (pa_dbf.use_table(pa_db, &watchers_table) < 0)
411         {
412                 LM_ERR( "unsuccessful use_table watchers_table\n");
413                 return -1;
414         }
415
416         LM_DBG("Database connection opened successfully\n");
417         return 0;
418 }
419
420
421 /*
422  * destroy function
423  */
424 static void destroy(void)
425 {
426         LM_NOTICE("destroy module ...\n");
427
428         if(subs_htable && pa_db)
429                 timer_db_update(0, 0);
430
431         if(subs_htable)
432                 destroy_shtable(subs_htable, shtable_size);
433         
434         if(pres_htable)
435                 destroy_phtable();
436
437         if(pa_db && pa_dbf.close)
438                 pa_dbf.close(pa_db);
439         
440         destroy_evlist();
441 }
442
443 static int fixup_presence(void** param, int param_no)
444 {
445         pv_elem_t *model;
446         str s;
447         if(*param)
448         {
449                 s.s = (char*)(*param); s.len = strlen(s.s);
450                 if(pv_parse_format(&s, &model)<0)
451                 {
452                         LM_ERR( "wrong format[%s]\n",(char*)(*param));
453                         return E_UNSPEC;
454                 }
455  
456                 *param = (void*)model;
457                 return 0;
458         }
459         LM_ERR( "null format\n");
460         return E_UNSPEC;
461 }
462
463 /*! \brief
464  *  mi cmd: refreshWatchers
465  *                      <presentity_uri> 
466  *                      <event>
467  *          <refresh_type> // can be:  = 0 -> watchers autentification type or
468  *                                                                        != 0 -> publish type //                  
469  *              * */
470
471 static struct mi_root* mi_refreshWatchers(struct mi_root* cmd, void* param)
472 {
473         struct mi_node* node= NULL;
474         str pres_uri, event;
475         struct sip_uri uri;
476         pres_ev_t* ev;
477         str* rules_doc= NULL;
478         int result;
479         unsigned int refresh_type;
480
481         LM_DBG("start\n");
482         
483         node = cmd->node.kids;
484         if(node == NULL)
485                 return 0;
486
487         /* Get presentity URI */
488         pres_uri = node->value;
489         if(pres_uri.s == NULL || pres_uri.len== 0)
490         {
491                 LM_ERR( "empty uri\n");
492                 return init_mi_tree(404, "Empty presentity URI", 20);
493         }
494         
495         node = node->next;
496         if(node == NULL)
497                 return 0;
498         event= node->value;
499         if(event.s== NULL || event.len== 0)
500         {
501                 LM_ERR( "empty event parameter\n");
502                 return init_mi_tree(400, "Empty event parameter", 21);
503         }
504         LM_DBG("event '%.*s'\n",  event.len, event.s);
505         
506         node = node->next;
507         if(node == NULL)
508                 return 0;
509         if(node->value.s== NULL || node->value.len== 0)
510         {
511                 LM_ERR( "empty event parameter\n");
512                 return init_mi_tree(400, "Empty event parameter", 21);          
513         }
514         if(str2int(&node->value, &refresh_type)< 0)
515         {
516                 LM_ERR("converting string to int\n");
517                 goto error;
518         }
519
520         if(node->next!= NULL)
521         {
522                 LM_ERR( "Too many parameters\n");
523                 return init_mi_tree(400, "Too many parameters", 19);
524         }
525
526         ev= contains_event(&event, NULL);
527         if(ev== NULL)
528         {
529                 LM_ERR( "wrong event parameter\n");
530                 return 0;
531         }
532         
533         if(refresh_type== 0) /* if a request to refresh watchers authorization*/
534         {
535                 if(ev->get_rules_doc== NULL)
536                 {
537                         LM_ERR("wrong request for a refresh watchers authorization status"
538                                         "for an event that does not require authorization\n");
539                         goto error;
540                 }
541                 
542                 if(parse_uri(pres_uri.s, pres_uri.len, &uri)< 0)
543                 {
544                         LM_ERR( "parsing uri\n");
545                         goto error;
546                 }
547
548                 result= ev->get_rules_doc(&uri.user,&uri.host,&rules_doc);
549                 if(result< 0 || rules_doc==NULL || rules_doc->s== NULL)
550                 {
551                         LM_ERR( "no rules doc found for the user\n");
552                         goto error;
553                 }
554         
555                 if(update_watchers_status(pres_uri, ev, rules_doc)< 0)
556                 {
557                         LM_ERR("failed to update watchers\n");
558                         goto error;
559                 }
560
561                 pkg_free(rules_doc->s);
562                 pkg_free(rules_doc);
563                 rules_doc = NULL;
564
565         }
566         else     /* if a request to refresh Notified info */
567         {
568                 if(query_db_notify(&pres_uri, ev, NULL)< 0)
569                 {
570                         LM_ERR("sending Notify requests\n");
571                         goto error;
572                 }
573
574         }
575                 
576         return init_mi_tree(200, "OK", 2);
577
578 error:
579         if(rules_doc)
580         {
581                 if(rules_doc->s)
582                         pkg_free(rules_doc->s);
583                 pkg_free(rules_doc);
584         }
585         return 0;
586 }
587
588
589 int pres_update_status(subs_t subs, str reason, db_key_t* query_cols,
590         db_val_t* query_vals, int n_query_cols, subs_t** subs_array)
591 {
592         db_key_t update_cols[5];
593         db_val_t update_vals[5];
594         int n_update_cols= 0;
595         int u_status_col, u_reason_col, q_wuser_col, q_wdomain_col;
596         int status;
597         query_cols[q_wuser_col=n_query_cols]= &str_watcher_username_col;
598         query_vals[n_query_cols].nul= 0;
599         query_vals[n_query_cols].type= DB_STR;
600         n_query_cols++;
601
602         query_cols[q_wdomain_col=n_query_cols]= &str_watcher_domain_col;
603         query_vals[n_query_cols].nul= 0;
604         query_vals[n_query_cols].type= DB_STR;
605         n_query_cols++;
606
607         update_cols[u_status_col= n_update_cols]= &str_status_col;
608         update_vals[u_status_col].nul= 0;
609         update_vals[u_status_col].type= DB_INT;
610         n_update_cols++;
611
612         update_cols[u_reason_col= n_update_cols]= &str_reason_col;
613         update_vals[u_reason_col].nul= 0;
614         update_vals[u_reason_col].type= DB_STR;
615         n_update_cols++;
616
617         status= subs.status;
618         if(subs.event->get_auth_status(&subs)< 0)
619         {
620                 LM_ERR( "getting status from rules document\n");
621                 return -1;
622         }
623         LM_DBG("subs.status= %d\n", subs.status);
624         if(get_status_str(subs.status)== NULL)
625         {
626                 LM_ERR("wrong status: %d\n", subs.status);
627                 return -1;
628         }
629
630         if(subs.status!= status || reason.len!= subs.reason.len ||
631                 (reason.s && subs.reason.s && strncmp(reason.s, subs.reason.s,
632                                                                                           reason.len)))
633         {
634                 /* update in watchers_table */
635                 query_vals[q_wuser_col].val.str_val= subs.from_user; 
636                 query_vals[q_wdomain_col].val.str_val= subs.from_domain; 
637
638                 update_vals[u_status_col].val.int_val= subs.status;
639                 update_vals[u_reason_col].val.str_val= subs.reason;
640                 
641                 if (pa_dbf.use_table(pa_db, &watchers_table) < 0) 
642                 {
643                         LM_ERR( "in use_table\n");
644                         return -1;
645                 }
646
647                 if(pa_dbf.update(pa_db, query_cols, 0, query_vals, update_cols,
648                                         update_vals, n_query_cols, n_update_cols)< 0)
649                 {
650                         LM_ERR( "in sql update\n");
651                         return -1;
652                 }
653                 /* save in the list all affected dialogs */
654                 /* if status switches to terminated -> delete dialog */
655                 if(update_pw_dialogs(&subs, subs.db_flag, subs_array)< 0)
656                 {
657                         LM_ERR( "extracting dialogs from [watcher]=%.*s@%.*s to"
658                                 " [presentity]=%.*s\n", subs.from_user.len, subs.from_user.s,
659                                 subs.from_domain.len, subs.from_domain.s, subs.pres_uri.len,
660                                 subs.pres_uri.s);
661                         return -1;
662                 }
663         }
664     return 0;
665 }
666
667 int pres_db_delete_status(subs_t* s)
668 {
669     int n_query_cols= 0;
670     db_key_t query_cols[5];
671     db_val_t query_vals[5];
672
673     if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0) 
674     {
675         LM_ERR("sql use table failed\n");
676         return -1;
677     }
678
679     query_cols[n_query_cols]= &str_event_col;
680     query_vals[n_query_cols].nul= 0;
681     query_vals[n_query_cols].type= DB_STR;
682     query_vals[n_query_cols].val.str_val= s->event->name ;
683     n_query_cols++;
684
685     query_cols[n_query_cols]= &str_presentity_uri_col;
686     query_vals[n_query_cols].nul= 0;
687     query_vals[n_query_cols].type= DB_STR;
688     query_vals[n_query_cols].val.str_val= s->pres_uri;
689     n_query_cols++;
690
691     query_cols[n_query_cols]= &str_watcher_username_col;
692     query_vals[n_query_cols].nul= 0;
693     query_vals[n_query_cols].type= DB_STR;
694     query_vals[n_query_cols].val.str_val= s->from_user;
695     n_query_cols++;
696
697     query_cols[n_query_cols]= &str_watcher_domain_col;
698     query_vals[n_query_cols].nul= 0;
699     query_vals[n_query_cols].type= DB_STR;
700     query_vals[n_query_cols].val.str_val= s->from_domain;
701     n_query_cols++;
702
703     if(pa_dbf.delete(pa_db, query_cols, 0, query_vals, n_query_cols)< 0)
704     {
705         LM_ERR("sql delete failed\n");
706         return -1;
707     }
708     return 0;
709
710 }
711
712 int update_watchers_status(str pres_uri, pres_ev_t* ev, str* rules_doc)
713 {
714         subs_t subs;
715         db_key_t query_cols[6], result_cols[5];
716         db_val_t query_vals[6];
717         int n_result_cols= 0, n_query_cols = 0;
718         db_res_t* result= NULL;
719         db_row_t *row;
720         db_val_t *row_vals ;
721         int i;
722         str w_user, w_domain, reason= {0, 0};
723         unsigned int status;
724         int status_col, w_user_col, w_domain_col, reason_col;
725         subs_t* subs_array= NULL,* s;
726         unsigned int hash_code;
727         int err_ret= -1;
728         int n= 0;
729
730         typedef struct ws
731         {
732                 int status;
733                 str reason;
734                 str w_user;
735                 str w_domain;
736         }ws_t;
737         ws_t* ws_list= NULL;
738
739     LM_DBG("start\n");
740
741         if(ev->content_type.s== NULL)
742         {
743                 ev= contains_event(&ev->name, NULL);
744                 if(ev== NULL)
745                 {
746                         LM_ERR("wrong event parameter\n");
747                         return 0;
748                 }
749         }
750
751         subs.pres_uri= pres_uri;
752         subs.event= ev;
753         subs.auth_rules_doc= rules_doc;
754
755         /* update in watchers_table */
756         query_cols[n_query_cols]= &str_presentity_uri_col;
757         query_vals[n_query_cols].nul= 0;
758         query_vals[n_query_cols].type= DB_STR;
759         query_vals[n_query_cols].val.str_val= pres_uri;
760         n_query_cols++;
761
762         query_cols[n_query_cols]= &str_event_col;
763         query_vals[n_query_cols].nul= 0;
764         query_vals[n_query_cols].type= DB_STR;
765         query_vals[n_query_cols].val.str_val= ev->name;
766         n_query_cols++;
767
768         result_cols[status_col= n_result_cols++]= &str_status_col;
769         result_cols[reason_col= n_result_cols++]= &str_reason_col;
770         result_cols[w_user_col= n_result_cols++]= &str_watcher_username_col;
771         result_cols[w_domain_col= n_result_cols++]= &str_watcher_domain_col;
772
773         if (pa_dbf.use_table(pa_db, &watchers_table) < 0) 
774         {
775                 LM_ERR( "in use_table\n");
776                 goto done;
777         }
778
779         if(pa_dbf.query(pa_db, query_cols, 0, query_vals, result_cols,n_query_cols,
780                                 n_result_cols, 0, &result)< 0)
781         {
782                 LM_ERR( "in sql query\n");
783                 goto done;
784         }
785         if(result== NULL)
786                 return 0;
787
788         if(result->n<= 0)
789         {
790                 err_ret= 0;
791                 goto done;
792         }
793
794     LM_DBG("found %d record-uri in watchers_table\n", result->n);
795         hash_code= core_hash(&pres_uri, &ev->name, shtable_size);
796         subs.db_flag= hash_code;
797
798     /*must do a copy as sphere_check requires database queries */
799         if(sphere_enable)
800         {
801         n= result->n;
802                 ws_list= (ws_t*)pkg_malloc(n * sizeof(ws_t));
803                 if(ws_list== NULL)
804                 {
805                         LM_ERR("No more private memory\n");
806                         goto done;
807                 }
808                 memset(ws_list, 0, n * sizeof(ws_t));
809
810                 for(i= 0; i< result->n ; i++)
811                 {
812                         row= &result->rows[i];
813                         row_vals = ROW_VALUES(row);
814
815                         status= row_vals[status_col].val.int_val;
816         
817                         reason.s= (char*)row_vals[reason_col].val.string_val;
818                         reason.len= reason.s?strlen(reason.s):0;
819
820                         w_user.s= (char*)row_vals[w_user_col].val.string_val;
821                         w_user.len= strlen(w_user.s);
822
823                         w_domain.s= (char*)row_vals[w_domain_col].val.string_val;
824                         w_domain.len= strlen(w_domain.s);
825
826                         if(reason.len)
827                         {
828                                 ws_list[i].reason.s = (char*)pkg_malloc(reason.len* sizeof(char));
829                                 if(ws_list[i].reason.s== NULL)
830                                 {  
831                                         LM_ERR("No more private memory\n");
832                                         goto done;
833                                 }
834                                 memcpy(ws_list[i].reason.s, reason.s, reason.len);
835                                 ws_list[i].reason.len= reason.len;
836                         }
837                         else
838                                 ws_list[i].reason.s= NULL;
839             
840                         ws_list[i].w_user.s = (char*)pkg_malloc(w_user.len* sizeof(char));
841                         if(ws_list[i].w_user.s== NULL)
842                         {
843                                 LM_ERR("No more private memory\n");
844                                 goto done;
845
846                         }
847                         memcpy(ws_list[i].w_user.s, w_user.s, w_user.len);
848                         ws_list[i].w_user.len= w_user.len;
849                 
850                          ws_list[i].w_domain.s = (char*)pkg_malloc(w_domain.len* sizeof(char));
851                         if(ws_list[i].w_domain.s== NULL)
852                         {
853                                 LM_ERR("No more private memory\n");
854                                 goto done;
855                         }
856                         memcpy(ws_list[i].w_domain.s, w_domain.s, w_domain.len);
857                         ws_list[i].w_domain.len= w_domain.len;
858                         
859                         ws_list[i].status= status;
860                 }
861
862                 pa_dbf.free_result(pa_db, result);
863                 result= NULL;
864
865                 for(i=0; i< n; i++)
866                 {
867                         subs.from_user = ws_list[i].w_user;
868                         subs.from_domain = ws_list[i].w_domain;
869                         subs.status = ws_list[i].status;
870                         memset(&subs.reason, 0, sizeof(str));
871
872                         if( pres_update_status(subs, reason, query_cols, query_vals,
873                                         n_query_cols, &subs_array)< 0)
874                         {
875                                 LM_ERR("failed to update watcher status\n");
876                                 goto done;
877                         }
878
879                 }
880         
881                 for(i=0; i< n; i++)
882                 {
883                         pkg_free(ws_list[i].w_user.s);
884                         pkg_free(ws_list[i].w_domain.s);
885                         if(ws_list[i].reason.s)
886                                 pkg_free(ws_list[i].reason.s);
887                 }
888                 ws_list= NULL;
889
890                 goto send_notify;
891
892         }
893         
894         for(i = 0; i< result->n; i++)
895         {
896                 row= &result->rows[i];
897                 row_vals = ROW_VALUES(row);
898
899                 status= row_vals[status_col].val.int_val;
900         
901                 reason.s= (char*)row_vals[reason_col].val.string_val;
902                 reason.len= reason.s?strlen(reason.s):0;
903
904                 w_user.s= (char*)row_vals[w_user_col].val.string_val;
905                 w_user.len= strlen(w_user.s);
906
907                 w_domain.s= (char*)row_vals[w_domain_col].val.string_val;
908                 w_domain.len= strlen(w_domain.s);
909
910                 subs.from_user= w_user;
911                 subs.from_domain= w_domain;
912                 subs.status= status;
913                 memset(&subs.reason, 0, sizeof(str));
914
915                 if( pres_update_status(subs,reason, query_cols, query_vals,
916                                         n_query_cols, &subs_array)< 0)
917                 {
918                         LM_ERR("failed to update watcher status\n");
919                         goto done;
920                 }
921     }
922
923         pa_dbf.free_result(pa_db, result);
924         result= NULL;
925
926 send_notify:
927
928         s= subs_array;
929
930         while(s)
931         {
932
933                 if(notify(s, NULL, NULL, 0)< 0)
934                 {
935                         LM_ERR( "sending Notify request\n");
936                         goto done;
937                 }
938
939         /* delete from database also */
940         if(s->status== TERMINATED_STATUS)
941         {
942             if(pres_db_delete_status(s)<0)
943             {
944                 err_ret= -1;
945                 LM_ERR("failed to delete terminated dialog from database\n");
946                 goto done;
947             }
948         }
949
950         s= s->next;
951         }
952
953         free_subs_list(subs_array, PKG_MEM_TYPE, 0);
954         return 0;
955
956 done:
957         if(result)
958                 pa_dbf.free_result(pa_db, result);
959         free_subs_list(subs_array, PKG_MEM_TYPE, 0);
960         if(ws_list)
961         {
962                 for(i= 0; i< n; i++)
963                 {
964                         if(ws_list[i].w_user.s)
965                                 pkg_free(ws_list[i].w_user.s);
966                         else
967                                 break;
968                         if(ws_list[i].w_domain.s)
969                                 pkg_free(ws_list[i].w_domain.s);
970                         if(ws_list[i].reason.s)
971                                 pkg_free(ws_list[i].reason.s);
972                 }
973         }
974         return err_ret;
975 }
976
977 static int update_pw_dialogs(subs_t* subs, unsigned int hash_code, subs_t** subs_array)
978 {
979         subs_t* s, *ps, *cs;
980         int i= 0;
981
982     LM_DBG("start\n");
983         lock_get(&subs_htable[hash_code].lock);
984         
985     ps= subs_htable[hash_code].entries;
986         
987         while(ps && ps->next)
988         {
989                 s= ps->next;
990
991                 if(s->event== subs->event && s->pres_uri.len== subs->pres_uri.len &&
992                         s->from_user.len== subs->from_user.len && 
993                         s->from_domain.len==subs->from_domain.len &&
994                         strncmp(s->pres_uri.s, subs->pres_uri.s, subs->pres_uri.len)== 0 &&
995                         strncmp(s->from_user.s, subs->from_user.s, s->from_user.len)== 0 &&
996                         strncmp(s->from_domain.s,subs->from_domain.s,s->from_domain.len)==0)
997                 {
998                         i++;
999                         s->status= subs->status;
1000                         s->reason= subs->reason;
1001                         s->db_flag= UPDATEDB_FLAG;
1002
1003                         cs= mem_copy_subs(s, PKG_MEM_TYPE);
1004                         if(cs== NULL)
1005                         {
1006                                 LM_ERR( "copying subs_t stucture\n");
1007                 lock_release(&subs_htable[hash_code].lock);
1008                 return -1;
1009                         }
1010                         cs->expires-= (int)time(NULL);
1011                         cs->next= (*subs_array);
1012                         (*subs_array)= cs;
1013                         if(subs->status== TERMINATED_STATUS)
1014                         {
1015                                 ps->next= s->next;
1016                                 shm_free(s->contact.s);
1017                 shm_free(s);
1018                 LM_DBG(" deleted terminated dialog from hash table\n");
1019             }
1020                         else
1021                                 ps= s;
1022
1023                         printf_subs(cs);
1024                 }
1025                 else
1026                         ps= s;
1027         }
1028         
1029     LM_DBG("found %d matching dialogs\n", i);
1030     lock_release(&subs_htable[hash_code].lock);
1031         
1032     return 0;
1033 }