presence: pass str parameters via pointer to couple of functions
[sip-router] / src / modules / presence / presence.c
1 /*
2  * Copyright (C) 2006 Voice Sistem S.R.L.
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * Kamailio is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version
10  *
11  * Kamailio is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 /*!
23  * \defgroup presence Presence :: A generic implementation of the SIP event package (PUBLISH, SUBSCRIBE, NOTIFY)
24  * The Kamailio presence module is a generic module for SIP event packages, which is much more than presence.
25  * It is extensible by developing other modules that use the internal developer API.
26  * Examples:
27  *- \ref presence_mwi
28  *- \ref presence_xml
29  */
30
31 /*!
32  * \file
33  * \brief Kamailio presence module :: Core
34  * \ingroup presence
35  */
36
37
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <sys/types.h>
42 #include <sys/ipc.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <time.h>
46
47 #include "../../lib/srdb1/db.h"
48 #include "../../core/sr_module.h"
49 #include "../../core/dprint.h"
50 #include "../../core/error.h"
51 #include "../../core/ut.h"
52 #include "../../core/parser/parse_to.h"
53 #include "../../core/parser/parse_uri.h"
54 #include "../../core/parser/parse_content.h"
55 #include "../../core/parser/parse_from.h"
56 #include "../../core/mem/mem.h"
57 #include "../../core/mem/shm_mem.h"
58 #include "../../core/usr_avp.h"
59 #include "../../core/rand/kam_rand.h"
60 #include "../../modules/tm/tm_load.h"
61 #include "../../modules/sl/sl.h"
62 #include "../../core/pt.h"
63 #include "../../core/hashes.h"
64 #include "../pua/hash.h"
65 #include "presence.h"
66 #include "publish.h"
67 #include "subscribe.h"
68 #include "event_list.h"
69 #include "bind_presence.h"
70 #include "notify.h"
71 #include "presence_dmq.h"
72 #include "../../core/mod_fix.h"
73 #include "../../core/kemi.h"
74 #include "../../core/timer_proc.h"
75 #include "../../core/rpc.h"
76 #include "../../core/rpc_lookup.h"
77
78 MODULE_VERSION
79
80 #define S_TABLE_VERSION 3
81 #define P_TABLE_VERSION 5
82 #define ACTWATCH_TABLE_VERSION 12
83
84 static int pres_clean_period = 100;
85 static int pres_db_update_period = 100;
86 int pres_local_log_level = L_INFO;
87
88 static char *pres_log_facility_str =
89                 0; /*!< Syslog: log facility that is used */
90 int pres_local_log_facility;
91
92 /* database connection */
93 db1_con_t *pa_db = NULL;
94 db_func_t pa_dbf;
95 str presentity_table = str_init("presentity");
96 str active_watchers_table = str_init("active_watchers");
97 str watchers_table = str_init("watchers");
98
99 int pres_fetch_rows = 500;
100 static int pres_library_mode = 0;
101 str pres_server_address = {0, 0};
102 evlist_t *pres_evlist = NULL;
103 int pres_subs_remove_match = 0;
104 int _pres_subs_mode = 1;
105
106 int pres_uri_match = 0;
107 /* sip uri match function pointer */
108 sip_uri_match_f presence_sip_uri_match;
109 static int sip_uri_case_sensitive_match(str *s1, str *s2);
110 static int sip_uri_case_insensitive_match(str *s1, str *s2);
111
112 /* TM bind */
113 struct tm_binds tmb;
114 /* SL API structure */
115 sl_api_t slb;
116
117 /** module functions */
118
119 static int mod_init(void);
120 static int child_init(int);
121 static void destroy(void);
122 int stored_pres_info(struct sip_msg *msg, char *pres_uri, char *s);
123 static int fixup_presence(void **param, int param_no);
124 static int fixup_subscribe(void **param, int param_no);
125 static int update_pw_dialogs(
126                 subs_t *subs, unsigned int hash_code, subs_t **subs_array);
127 static int w_pres_auth_status(struct sip_msg *_msg, char *_sp1, char *_sp2);
128 static int w_pres_refresh_watchers(
129                 struct sip_msg *msg, char *puri, char *pevent, char *ptype);
130 static int w_pres_refresh_watchers5(struct sip_msg *msg, char *puri,
131                 char *pevent, char *ptype, char *furi, char *fname);
132 static int w_pres_update_watchers(
133                 struct sip_msg *msg, char *puri, char *pevent);
134 static int fixup_refresh_watchers(void **param, int param_no);
135 static int fixup_update_watchers(void **param, int param_no);
136 static int presence_init_rpc(void);
137
138 static int w_pres_has_subscribers(struct sip_msg *_msg, char *_sp1, char *_sp2);
139 static int fixup_has_subscribers(void **param, int param_no);
140
141 int pres_counter = 0;
142 int pres_pid = 0;
143 char pres_prefix = 'a';
144 int pres_startup_time = 0;
145 str pres_db_url = {0, 0};
146 int pres_expires_offset = 0;
147 int pres_cseq_offset = 0;
148 uint32_t pres_min_expires = 0;
149 int pres_min_expires_action = 1;
150 uint32_t pres_max_expires = 3600;
151 int shtable_size = 9;
152 shtable_t subs_htable = NULL;
153 int pres_subs_dbmode = WRITE_BACK;
154 int pres_sphere_enable = 0;
155 int pres_timeout_rm_subs = 1;
156 int pres_send_fast_notify = 1;
157 int publ_cache_enabled = 1;
158 int pres_waitn_time = 5;
159 int pres_notifier_poll_rate = 10;
160 int pres_notifier_processes = 1;
161 int pres_force_delete = 0;
162 int pres_startup_mode = 1;
163 str pres_xavp_cfg = {0};
164 int pres_retrieve_order = 0;
165 str pres_retrieve_order_by = str_init("priority");
166 int pres_enable_dmq = 0;
167 int pres_delete_same_subs = 0;
168
169 int pres_db_table_lock_type = 1;
170 db_locking_t pres_db_table_lock = DB_LOCKING_WRITE;
171
172 int *pres_notifier_id = NULL;
173
174 int phtable_size = 9;
175 phtable_t *pres_htable = NULL;
176
177 sruid_t pres_sruid;
178
179 /* clang-format off */
180 static cmd_export_t cmds[]=
181 {
182         {"handle_publish",        (cmd_function)w_handle_publish,        0,
183                 fixup_presence, 0, REQUEST_ROUTE},
184         {"handle_publish",        (cmd_function)w_handle_publish,        1,
185                 fixup_presence, 0, REQUEST_ROUTE},
186         {"handle_subscribe",      (cmd_function)handle_subscribe0,       0,
187                 fixup_subscribe, 0, REQUEST_ROUTE},
188         {"handle_subscribe",      (cmd_function)w_handle_subscribe,      1,
189                 fixup_subscribe, 0, REQUEST_ROUTE},
190         {"pres_auth_status",      (cmd_function)w_pres_auth_status,      2,
191                 fixup_spve_spve, fixup_free_spve_spve, REQUEST_ROUTE},
192         {"pres_refresh_watchers", (cmd_function)w_pres_refresh_watchers, 3,
193                 fixup_refresh_watchers, 0, ANY_ROUTE},
194         {"pres_refresh_watchers", (cmd_function)w_pres_refresh_watchers5,5,
195                 fixup_refresh_watchers, 0, ANY_ROUTE},
196         {"pres_update_watchers",  (cmd_function)w_pres_update_watchers,  2,
197                 fixup_update_watchers, 0, ANY_ROUTE},
198         {"pres_has_subscribers",  (cmd_function)w_pres_has_subscribers,  2,
199                 fixup_has_subscribers, 0, ANY_ROUTE},
200         {"bind_presence",         (cmd_function)bind_presence,           1,
201                 0, 0, 0},
202         { 0, 0, 0, 0, 0, 0}
203 };
204 /* clang-format on */
205
206 /* clang-format off */
207 static param_export_t params[]={
208         { "db_url",                 PARAM_STR, &pres_db_url},
209         { "presentity_table",       PARAM_STR, &presentity_table},
210         { "active_watchers_table",  PARAM_STR, &active_watchers_table},
211         { "watchers_table",         PARAM_STR, &watchers_table},
212         { "clean_period",           INT_PARAM, &pres_clean_period },
213         { "db_update_period",       INT_PARAM, &pres_db_update_period },
214         { "waitn_time",             INT_PARAM, &pres_waitn_time },
215         { "notifier_poll_rate",     INT_PARAM, &pres_notifier_poll_rate },
216         { "notifier_processes",     INT_PARAM, &pres_notifier_processes },
217         { "force_delete",           INT_PARAM, &pres_force_delete },
218         { "startup_mode",           INT_PARAM, &pres_startup_mode },
219         { "expires_offset",         INT_PARAM, &pres_expires_offset },
220         { "max_expires",            INT_PARAM, &pres_max_expires },
221         { "min_expires",            INT_PARAM, &pres_min_expires },
222         { "min_expires_action",     INT_PARAM, &pres_min_expires_action },
223         { "server_address",         PARAM_STR, &pres_server_address},
224         { "subs_htable_size",       INT_PARAM, &shtable_size},
225         { "pres_htable_size",       INT_PARAM, &phtable_size},
226         { "subs_db_mode",           INT_PARAM, &pres_subs_dbmode},
227         { "publ_cache",             INT_PARAM, &publ_cache_enabled},
228         { "enable_sphere_check",    INT_PARAM, &pres_sphere_enable},
229         { "timeout_rm_subs",        INT_PARAM, &pres_timeout_rm_subs},
230         { "send_fast_notify",       INT_PARAM, &pres_send_fast_notify},
231         { "fetch_rows",             INT_PARAM, &pres_fetch_rows},
232         { "db_table_lock_type",     INT_PARAM, &pres_db_table_lock_type},
233         { "local_log_level",        PARAM_INT, &pres_local_log_level},
234         { "local_log_facility",     PARAM_STR, &pres_log_facility_str},
235         { "subs_remove_match",      PARAM_INT, &pres_subs_remove_match},
236         { "xavp_cfg",               PARAM_STR, &pres_xavp_cfg},
237         { "retrieve_order",         PARAM_INT, &pres_retrieve_order},
238         { "retrieve_order_by",      PARAM_STR, &pres_retrieve_order_by},
239         { "sip_uri_match",          PARAM_INT, &pres_uri_match},
240         { "cseq_offset",            PARAM_INT, &pres_cseq_offset},
241         { "enable_dmq",             PARAM_INT, &pres_enable_dmq},
242         { "pres_subs_mode",         PARAM_INT, &_pres_subs_mode},
243         { "delete_same_subs",       PARAM_INT, &pres_delete_same_subs},
244         {0,0,0}
245 };
246 /* clang-format on */
247
248 /* clang-format off */
249 static pv_export_t pres_mod_pvs[] = {
250         { {"subs", (sizeof("subs")-1)}, PVT_OTHER,
251                 pv_get_subscription, 0, pv_parse_subscription_name, 0, 0, 0},
252         { {"notify_reply", (sizeof("notify_reply")-1)}, PVT_OTHER,
253                 pv_get_notify_reply, 0, pv_parse_notify_reply_var_name, 0, 0, 0},
254         { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
255 };
256 /* clang-format on */
257
258 /** module exports */
259 /* clang-format off */
260 struct module_exports exports= {
261         "presence",                     /* module name */
262         DEFAULT_DLFLAGS,        /* dlopen flags */
263         cmds,                           /* exported functions */
264         params,                         /* exported parameters */
265         0,                                      /* RPC method exports */
266         pres_mod_pvs,           /* exported pseudo-variables */
267         0,                                      /* response handling function */
268         mod_init,                       /* module initialization function */
269         child_init,                     /* per-child init function */
270         destroy                         /* module destroy function */
271 };
272 /* clang-format on */
273
274 /**
275  * init module function
276  */
277 static int mod_init(void)
278 {
279         if(pres_uri_match == 1) {
280                 presence_sip_uri_match = sip_uri_case_insensitive_match;
281         } else {
282                 presence_sip_uri_match = sip_uri_case_sensitive_match;
283         }
284
285         if(presence_init_rpc() != 0) {
286                 LM_ERR("failed to register RPC commands\n");
287                 return -1;
288         }
289
290         LM_DBG("db_url=%s (len=%d addr=%p)\n", ZSW(pres_db_url.s), pres_db_url.len,
291                         pres_db_url.s);
292
293         if(pres_db_url.s == NULL || pres_db_url.len == 0) {
294                 LM_DBG("db url is not set - switch to library mode\n");
295                 pres_library_mode = 1;
296         }
297
298         pres_evlist = init_evlist();
299         if(!pres_evlist) {
300                 LM_ERR("unsuccessful initialize event list\n");
301                 return -1;
302         }
303
304         if(pres_library_mode == 1) {
305                 LM_DBG("Presence module used for API library purpose only\n");
306                 return 0;
307         }
308
309         if(sruid_init(&pres_sruid, '-', "pres", SRUID_INC) < 0) {
310                 return -1;
311         }
312
313         if(pres_expires_offset < 0) {
314                 pres_expires_offset = 0;
315         }
316
317         if(pres_max_expires <= 0) {
318                 pres_max_expires = 3600;
319         }
320
321         if(pres_min_expires > pres_max_expires) {
322                 pres_min_expires = pres_max_expires;
323         }
324
325         if(pres_min_expires_action < 1 || pres_min_expires_action > 2) {
326                 LM_ERR("min_expires_action must be 1 = RFC 6665/3261 Reply 423, 2 = "
327                            "force min_expires value\n");
328                 return -1;
329         }
330
331         if(pres_server_address.s == NULL || pres_server_address.len==0) {
332                 LM_DBG("server_address parameter not set in configuration file\n");
333         }
334
335         /* bind the SL API */
336         if(sl_load_api(&slb) != 0) {
337                 LM_ERR("cannot bind to SL API\n");
338                 return -1;
339         }
340
341         /* load all TM stuff */
342         if(load_tm_api(&tmb) == -1) {
343                 LM_ERR("Can't load tm functions. Module TM not loaded?\n");
344                 return -1;
345         }
346
347         if(pres_db_url.s == NULL) {
348                 LM_ERR("database url not set!\n");
349                 return -1;
350         }
351
352         /* binding to database module  */
353         if(db_bind_mod(&pres_db_url, &pa_dbf)) {
354                 LM_ERR("Database module not found\n");
355                 return -1;
356         }
357
358         if(!DB_CAPABILITY(pa_dbf, DB_CAP_ALL)) {
359                 LM_ERR("Database module does not implement all functions"
360                            " needed by presence module\n");
361                 return -1;
362         }
363
364         pa_db = pa_dbf.init(&pres_db_url);
365         if(!pa_db) {
366                 LM_ERR("Connection to database failed\n");
367                 return -1;
368         }
369
370         /*verify table versions */
371         if((db_check_table_version(
372                                 &pa_dbf, pa_db, &presentity_table, P_TABLE_VERSION)
373                            < 0)
374                         || (db_check_table_version(
375                                                 &pa_dbf, pa_db, &watchers_table, S_TABLE_VERSION)
376                                            < 0)) {
377                 DB_TABLE_VERSION_ERROR(presentity_table);
378                 goto dberror;
379         }
380
381         if(pres_subs_dbmode != NO_DB
382                         && db_check_table_version(&pa_dbf, pa_db, &active_watchers_table,
383                                            ACTWATCH_TABLE_VERSION)
384                                            < 0) {
385                 DB_TABLE_VERSION_ERROR(active_watchers_table);
386                 goto dberror;
387         }
388
389         if(pres_subs_dbmode != DB_ONLY) {
390                 if(shtable_size < 1)
391                         shtable_size = 512;
392                 else
393                         shtable_size = 1 << shtable_size;
394
395                 subs_htable = new_shtable(shtable_size);
396                 if(subs_htable == NULL) {
397                         LM_ERR(" initializing subscribe hash table\n");
398                         goto dberror;
399                 }
400                 if(restore_db_subs() < 0) {
401                         LM_ERR("restoring subscribe info from database\n");
402                         goto dberror;
403                 }
404         }
405
406         if(publ_cache_enabled) {
407                 if(phtable_size < 1)
408                         phtable_size = 256;
409                 else
410                         phtable_size = 1 << phtable_size;
411
412                 pres_htable = new_phtable();
413                 if(pres_htable == NULL) {
414                         LM_ERR("initializing presentity hash table\n");
415                         goto dberror;
416                 }
417
418                 if(pres_htable_restore() < 0) {
419                         LM_ERR("filling in presentity hash table from database\n");
420                         goto dberror;
421                 }
422         }
423
424         pres_startup_time = (int)time(NULL);
425         if(pres_clean_period > 0) {
426                 register_timer(msg_presentity_clean, 0, pres_clean_period);
427                 register_timer(msg_watchers_clean, 0, pres_clean_period);
428         }
429
430         if(pres_db_update_period > 0) {
431                 register_timer(timer_db_update, 0, pres_db_update_period);
432         }
433
434         if(pres_waitn_time <= 0) {
435                 pres_waitn_time = 5;
436         }
437
438         if(pres_notifier_poll_rate <= 0) {
439                 pres_notifier_poll_rate = 10;
440         }
441
442         if(pres_notifier_processes < 0 || pres_subs_dbmode != DB_ONLY) {
443                 pres_notifier_processes = 0;
444         }
445
446         if(pres_notifier_processes > 0) {
447                 if((pres_notifier_id =
448                                                    shm_malloc(sizeof(int) * pres_notifier_processes))
449                                 == NULL) {
450                         LM_ERR("allocating shared memory\n");
451                         goto dberror;
452                 }
453
454                 register_basic_timers(pres_notifier_processes);
455         }
456
457         if(pres_force_delete > 0)
458                 pres_force_delete = 1;
459
460         if(pres_log_facility_str) {
461                 int tmp = str2facility(pres_log_facility_str);
462
463                 if(tmp != -1) {
464                         pres_local_log_facility = tmp;
465                 } else {
466                         LM_ERR("invalid log facility configured\n");
467                         goto dberror;
468                 }
469         } else {
470                 pres_local_log_facility = cfg_get(core, core_cfg, log_facility);
471         }
472
473         if(pres_db_table_lock_type != 1) {
474                 pres_db_table_lock = DB_LOCKING_NONE;
475         }
476
477         pa_dbf.close(pa_db);
478         pa_db = NULL;
479
480         goto_on_notify_reply = route_lookup(&event_rt, "presence:notify-reply");
481         if(goto_on_notify_reply >= 0 && event_rt.rlist[goto_on_notify_reply] == 0)
482                 goto_on_notify_reply = -1; /* disable */
483
484         if(pres_enable_dmq > 0 && pres_dmq_initialize() != 0) {
485                 LM_ERR("failed to initialize dmq integration\n");
486                 return -1;
487         }
488
489         return 0;
490
491 dberror:
492         pa_dbf.close(pa_db);
493         pa_db = NULL;
494         return -1;
495 }
496
497 /**
498  * Initialize children
499  */
500 static int child_init(int rank)
501 {
502         if(rank == PROC_INIT || rank == PROC_TCP_MAIN) {
503                 return 0;
504         }
505
506         pres_pid = my_pid();
507
508         if(pres_library_mode) {
509                 return 0;
510         }
511
512         if(sruid_init(&pres_sruid, '-', "pres", SRUID_INC) < 0) {
513                 return -1;
514         }
515
516         if(rank == PROC_MAIN) {
517                 int i;
518
519                 for(i = 0; i < pres_notifier_processes; i++) {
520                         char tmp[21];
521                         snprintf(tmp, 21, "PRESENCE NOTIFIER %d", i);
522                         pres_notifier_id[i] = i;
523
524                         if(fork_basic_utimer(PROC_TIMER, tmp, 1, pres_timer_send_notify,
525                                            &pres_notifier_id[i], 1000000 / pres_notifier_poll_rate)
526                                         < 0) {
527                                 LM_ERR("Failed to start PRESENCE NOTIFIER %d\n", i);
528                                 return -1;
529                         }
530                 }
531
532                 return 0;
533         }
534
535         if(pa_dbf.init == 0) {
536                 LM_CRIT("child_init: database not bound\n");
537                 return -1;
538         }
539         /* Do not pool the connections where possible when running notifier
540          * processes. */
541         if(pres_notifier_processes > 0 && pa_dbf.init2)
542                 pa_db = pa_dbf.init2(&pres_db_url, DB_POOLING_NONE);
543         else
544                 pa_db = pa_dbf.init(&pres_db_url);
545         if(!pa_db) {
546                 LM_ERR("child %d: unsuccessful connecting to database\n", rank);
547                 return -1;
548         }
549
550         if(pa_dbf.use_table(pa_db, &presentity_table) < 0) {
551                 LM_ERR("child %d:unsuccessful use_table presentity_table\n", rank);
552                 return -1;
553         }
554
555         if(pa_dbf.use_table(pa_db, &active_watchers_table) < 0) {
556                 LM_ERR("child %d:unsuccessful use_table active_watchers_table\n", rank);
557                 return -1;
558         }
559
560         if(pa_dbf.use_table(pa_db, &watchers_table) < 0) {
561                 LM_ERR("child %d:unsuccessful use_table watchers_table\n", rank);
562                 return -1;
563         }
564
565         LM_DBG("child %d: Database connection opened successfully\n", rank);
566
567         return 0;
568 }
569
570
571 /*
572  * destroy function
573  */
574 static void destroy(void)
575 {
576         if(subs_htable && pres_subs_dbmode == WRITE_BACK) {
577                 /* open database connection */
578                 pa_db = pa_dbf.init(&pres_db_url);
579                 if(!pa_db) {
580                         LM_ERR("mod_destroy: unsuccessful connecting to database\n");
581                 } else
582                         timer_db_update(0, 0);
583         }
584
585         if(subs_htable)
586                 destroy_shtable(subs_htable, shtable_size);
587
588         if(pres_htable)
589                 destroy_phtable();
590
591         if(pa_db && pa_dbf.close)
592                 pa_dbf.close(pa_db);
593
594         if(pres_notifier_id != NULL)
595                 shm_free(pres_notifier_id);
596
597         destroy_evlist();
598 }
599
600 static int fixup_presence(void **param, int param_no)
601 {
602         if(pres_library_mode) {
603                 LM_ERR("Bad config - you can not call 'handle_publish' function"
604                            " (db_url not set)\n");
605                 return -1;
606         }
607         if(param_no == 0)
608                 return 0;
609
610         return fixup_spve_null(param, 1);
611 }
612
613 static int fixup_subscribe(void **param, int param_no)
614 {
615
616         if(pres_library_mode) {
617                 LM_ERR("Bad config - you can not call 'handle_subscribe' function"
618                            " (db_url not set)\n");
619                 return -1;
620         }
621         if(param_no == 1) {
622                 return fixup_spve_null(param, 1);
623         }
624         return 0;
625 }
626
627 int pres_refresh_watchers(
628                 str *pres, str *event, int type, str *file_uri, str *filename)
629 {
630         pres_ev_t *ev;
631         struct sip_uri uri;
632         str *rules_doc = NULL;
633         int result;
634
635         ev = contains_event(event, NULL);
636         if(ev == NULL) {
637                 LM_ERR("wrong event parameter\n");
638                 return -1;
639         }
640
641         if(type == 0) {
642                 /* if a request to refresh watchers authorization */
643                 if(ev->get_rules_doc == NULL) {
644                         LM_ERR("wrong request for a refresh watchers authorization status"
645                                    "for an event that does not require authorization\n");
646                         goto error;
647                 }
648
649                 if(parse_uri(pres->s, pres->len, &uri) < 0) {
650                         LM_ERR("parsing uri [%.*s]\n", pres->len, pres->s);
651                         goto error;
652                 }
653
654                 result = ev->get_rules_doc(&uri.user, &uri.host, &rules_doc);
655                 if(result < 0 || rules_doc == NULL || rules_doc->s == NULL) {
656                         LM_ERR("no rules doc found for the user\n");
657                         goto error;
658                 }
659
660                 if(update_watchers_status(pres, ev, rules_doc) < 0) {
661                         LM_ERR("failed to update watchers\n");
662                         goto error;
663                 }
664
665                 pkg_free(rules_doc->s);
666                 pkg_free(rules_doc);
667                 rules_doc = NULL;
668
669         } else {
670                 if(type == 2) {
671                         if(update_hard_presentity(pres, ev, file_uri, filename) < 0) {
672                                 LM_ERR("updating hard presentity\n");
673                                 goto error;
674                         }
675                 }
676
677                 /* if a request to refresh notified info */
678                 if(query_db_notify(pres, ev, NULL) < 0) {
679                         LM_ERR("sending Notify requests\n");
680                         goto error;
681                 }
682         }
683         return 0;
684
685 error:
686         if(rules_doc) {
687                 if(rules_doc->s)
688                         pkg_free(rules_doc->s);
689                 pkg_free(rules_doc);
690         }
691         return -1;
692 }
693
694 int _api_pres_refresh_watchers(str *pres, str *event, int type)
695 {
696         return pres_refresh_watchers(pres, event, type, NULL, NULL);
697 }
698
699 int ki_pres_refresh_watchers(sip_msg_t *msg, str *pres, str *event, int type)
700 {
701         return pres_refresh_watchers(pres, event, type, NULL, NULL);
702 }
703
704 int ki_pres_refresh_watchers_file(sip_msg_t *msg, str *pres, str *event,
705                 int type, str *file_uri, str *filename)
706 {
707         return pres_refresh_watchers(pres, event, type, file_uri, filename);
708 }
709
710 int pres_update_status(subs_t *subs, str reason, db_key_t *query_cols,
711                 db_val_t *query_vals, int n_query_cols, subs_t **subs_array)
712 {
713         db_key_t update_cols[5];
714         db_val_t update_vals[5];
715         int n_update_cols = 0;
716         int u_status_col, u_reason_col, q_wuser_col, q_wdomain_col;
717         int status;
718         query_cols[q_wuser_col = n_query_cols] = &str_watcher_username_col;
719         query_vals[n_query_cols].nul = 0;
720         query_vals[n_query_cols].type = DB1_STR;
721         n_query_cols++;
722
723         query_cols[q_wdomain_col = n_query_cols] = &str_watcher_domain_col;
724         query_vals[n_query_cols].nul = 0;
725         query_vals[n_query_cols].type = DB1_STR;
726         n_query_cols++;
727
728         update_cols[u_status_col = n_update_cols] = &str_status_col;
729         update_vals[u_status_col].nul = 0;
730         update_vals[u_status_col].type = DB1_INT;
731         n_update_cols++;
732
733         update_cols[u_reason_col = n_update_cols] = &str_reason_col;
734         update_vals[u_reason_col].nul = 0;
735         update_vals[u_reason_col].type = DB1_STR;
736         n_update_cols++;
737
738         status = subs->status;
739         if(subs->event->get_auth_status(subs) < 0) {
740                 LM_ERR("getting status from rules document\n");
741                 return -1;
742         }
743         LM_DBG("subs.status= %d\n", subs->status);
744         if(get_status_str(subs->status) == NULL) {
745                 LM_ERR("wrong status: %d\n", subs->status);
746                 return -1;
747         }
748
749         if(subs->status != status || reason.len != subs->reason.len
750                         || (reason.s && subs->reason.s
751                                            && strncmp(reason.s, subs->reason.s, reason.len))) {
752                 /* update in watchers_table */
753                 query_vals[q_wuser_col].val.str_val = subs->watcher_user;
754                 query_vals[q_wdomain_col].val.str_val = subs->watcher_domain;
755
756                 update_vals[u_status_col].val.int_val = subs->status;
757                 update_vals[u_reason_col].val.str_val = subs->reason;
758
759                 if(pa_dbf.use_table(pa_db, &watchers_table) < 0) {
760                         LM_ERR("in use_table\n");
761                         return -1;
762                 }
763
764                 if(pa_dbf.update(pa_db, query_cols, 0, query_vals, update_cols,
765                                    update_vals, n_query_cols, n_update_cols)
766                                 < 0) {
767                         LM_ERR("in sql update\n");
768                         return -1;
769                 }
770                 /* save in the list all affected dialogs */
771                 /* if status switches to terminated -> delete dialog */
772                 if(update_pw_dialogs(subs, subs->db_flag, subs_array) < 0) {
773                         LM_ERR("extracting dialogs from [watcher]=%.*s@%.*s to"
774                                    " [presentity]=%.*s\n",
775                                         subs->watcher_user.len, subs->watcher_user.s,
776                                         subs->watcher_domain.len, subs->watcher_domain.s,
777                                         subs->pres_uri.len, subs->pres_uri.s);
778                         return -1;
779                 }
780         }
781         return 0;
782 }
783
784 int pres_db_delete_status(subs_t *s)
785 {
786         int n_query_cols = 0;
787         db_key_t query_cols[5];
788         db_val_t query_vals[5];
789
790         if(pa_dbf.use_table(pa_db, &active_watchers_table) < 0) {
791                 LM_ERR("sql use table failed\n");
792                 return -1;
793         }
794
795         query_cols[n_query_cols] = &str_event_col;
796         query_vals[n_query_cols].nul = 0;
797         query_vals[n_query_cols].type = DB1_STR;
798         query_vals[n_query_cols].val.str_val = s->event->name;
799         n_query_cols++;
800
801         query_cols[n_query_cols] = &str_presentity_uri_col;
802         query_vals[n_query_cols].nul = 0;
803         query_vals[n_query_cols].type = DB1_STR;
804         query_vals[n_query_cols].val.str_val = s->pres_uri;
805         n_query_cols++;
806
807         query_cols[n_query_cols] = &str_watcher_username_col;
808         query_vals[n_query_cols].nul = 0;
809         query_vals[n_query_cols].type = DB1_STR;
810         query_vals[n_query_cols].val.str_val = s->watcher_user;
811         n_query_cols++;
812
813         query_cols[n_query_cols] = &str_watcher_domain_col;
814         query_vals[n_query_cols].nul = 0;
815         query_vals[n_query_cols].type = DB1_STR;
816         query_vals[n_query_cols].val.str_val = s->watcher_domain;
817         n_query_cols++;
818
819         if(pa_dbf.delete(pa_db, query_cols, 0, query_vals, n_query_cols) < 0) {
820                 LM_ERR("sql delete failed\n");
821                 return -1;
822         }
823         return 0;
824 }
825
826 int update_watchers_status(str *pres_uri, pres_ev_t *ev, str *rules_doc)
827 {
828         subs_t subs;
829         db_key_t query_cols[6], result_cols[5];
830         db_val_t query_vals[6];
831         int n_result_cols = 0, n_query_cols = 0;
832         db1_res_t *result = NULL;
833         db_row_t *row;
834         db_val_t *row_vals;
835         int i;
836         str w_user, w_domain, reason = {0, 0};
837         unsigned int status;
838         int status_col, w_user_col, w_domain_col, reason_col;
839         subs_t *subs_array = NULL, *s;
840         unsigned int hash_code;
841         int err_ret = -1;
842         int n = 0;
843
844         typedef struct ws
845         {
846                 int status;
847                 str reason;
848                 str w_user;
849                 str w_domain;
850         } ws_t;
851
852         ws_t *ws_list = NULL;
853
854         LM_DBG("start\n");
855
856         if(ev->content_type.s == NULL) {
857                 ev = contains_event(&ev->name, NULL);
858                 if(ev == NULL) {
859                         LM_ERR("wrong event parameter\n");
860                         return 0;
861                 }
862         }
863
864         memset(&subs, 0, sizeof(subs_t));
865         subs.pres_uri = *pres_uri;
866         subs.event = ev;
867         subs.auth_rules_doc = rules_doc;
868
869         /* update in watchers_table */
870         query_cols[n_query_cols] = &str_presentity_uri_col;
871         query_vals[n_query_cols].nul = 0;
872         query_vals[n_query_cols].type = DB1_STR;
873         query_vals[n_query_cols].val.str_val = *pres_uri;
874         n_query_cols++;
875
876         query_cols[n_query_cols] = &str_event_col;
877         query_vals[n_query_cols].nul = 0;
878         query_vals[n_query_cols].type = DB1_STR;
879         query_vals[n_query_cols].val.str_val = ev->name;
880         n_query_cols++;
881
882         result_cols[status_col = n_result_cols++] = &str_status_col;
883         result_cols[reason_col = n_result_cols++] = &str_reason_col;
884         result_cols[w_user_col = n_result_cols++] = &str_watcher_username_col;
885         result_cols[w_domain_col = n_result_cols++] = &str_watcher_domain_col;
886
887         if(pa_dbf.use_table(pa_db, &watchers_table) < 0) {
888                 LM_ERR("in use_table\n");
889                 goto done;
890         }
891
892         if(pa_dbf.query(pa_db, query_cols, 0, query_vals, result_cols, n_query_cols,
893                            n_result_cols, 0, &result)
894                         < 0) {
895                 LM_ERR("in sql query\n");
896                 goto done;
897         }
898         if(result == NULL)
899                 return 0;
900
901         if(result->n <= 0) {
902                 err_ret = 0;
903                 goto done;
904         }
905
906         LM_DBG("found %d record-uri in watchers_table\n", result->n);
907         hash_code = core_case_hash(pres_uri, &ev->name, shtable_size);
908         subs.db_flag = hash_code;
909
910         /* must do a copy as sphere_check requires database queries */
911         if(pres_sphere_enable) {
912                 n = result->n;
913                 ws_list = (ws_t *)pkg_malloc(n * sizeof(ws_t));
914                 if(ws_list == NULL) {
915                         LM_ERR("No more private memory\n");
916                         goto done;
917                 }
918                 memset(ws_list, 0, n * sizeof(ws_t));
919
920                 for(i = 0; i < result->n; i++) {
921                         row = &result->rows[i];
922                         row_vals = ROW_VALUES(row);
923
924                         status = row_vals[status_col].val.int_val;
925
926                         reason.s = (char *)row_vals[reason_col].val.string_val;
927                         reason.len = reason.s ? strlen(reason.s) : 0;
928
929                         w_user.s = (char *)row_vals[w_user_col].val.string_val;
930                         w_user.len = strlen(w_user.s);
931
932                         w_domain.s = (char *)row_vals[w_domain_col].val.string_val;
933                         w_domain.len = strlen(w_domain.s);
934
935                         if(reason.len) {
936                                 ws_list[i].reason.s =
937                                                 (char *)pkg_malloc(reason.len * sizeof(char));
938                                 if(ws_list[i].reason.s == NULL) {
939                                         LM_ERR("No more private memory\n");
940                                         goto done;
941                                 }
942                                 memcpy(ws_list[i].reason.s, reason.s, reason.len);
943                                 ws_list[i].reason.len = reason.len;
944                         } else
945                                 ws_list[i].reason.s = NULL;
946
947                         ws_list[i].w_user.s = (char *)pkg_malloc(w_user.len * sizeof(char));
948                         if(ws_list[i].w_user.s == NULL) {
949                                 LM_ERR("No more private memory\n");
950                                 goto done;
951                         }
952                         memcpy(ws_list[i].w_user.s, w_user.s, w_user.len);
953                         ws_list[i].w_user.len = w_user.len;
954
955                         ws_list[i].w_domain.s =
956                                         (char *)pkg_malloc(w_domain.len * sizeof(char));
957                         if(ws_list[i].w_domain.s == NULL) {
958                                 LM_ERR("No more private memory\n");
959                                 goto done;
960                         }
961                         memcpy(ws_list[i].w_domain.s, w_domain.s, w_domain.len);
962                         ws_list[i].w_domain.len = w_domain.len;
963
964                         ws_list[i].status = status;
965                 }
966
967                 pa_dbf.free_result(pa_db, result);
968                 result = NULL;
969
970                 for(i = 0; i < n; i++) {
971                         subs.watcher_user = ws_list[i].w_user;
972                         subs.watcher_domain = ws_list[i].w_domain;
973                         subs.status = ws_list[i].status;
974                         memset(&subs.reason, 0, sizeof(str));
975
976                         if(pres_update_status(&subs, reason, query_cols, query_vals,
977                                            n_query_cols, &subs_array)
978                                         < 0) {
979                                 LM_ERR("failed to update watcher status\n");
980                                 goto done;
981                         }
982                 }
983
984                 for(i = 0; i < n; i++) {
985                         pkg_free(ws_list[i].w_user.s);
986                         pkg_free(ws_list[i].w_domain.s);
987                         if(ws_list[i].reason.s)
988                                 pkg_free(ws_list[i].reason.s);
989                 }
990                 pkg_free(ws_list);
991                 ws_list = NULL;
992
993                 goto send_notify;
994         }
995
996         for(i = 0; i < result->n; i++) {
997                 row = &result->rows[i];
998                 row_vals = ROW_VALUES(row);
999
1000                 status = row_vals[status_col].val.int_val;
1001
1002                 reason.s = (char *)row_vals[reason_col].val.string_val;
1003                 reason.len = reason.s ? strlen(reason.s) : 0;
1004
1005                 w_user.s = (char *)row_vals[w_user_col].val.string_val;
1006                 w_user.len = strlen(w_user.s);
1007
1008                 w_domain.s = (char *)row_vals[w_domain_col].val.string_val;
1009                 w_domain.len = strlen(w_domain.s);
1010
1011                 subs.watcher_user = w_user;
1012                 subs.watcher_domain = w_domain;
1013                 subs.status = status;
1014                 memset(&subs.reason, 0, sizeof(str));
1015
1016                 if(pres_update_status(&subs, reason, query_cols, query_vals,
1017                                    n_query_cols, &subs_array)
1018                                 < 0) {
1019                         LM_ERR("failed to update watcher status\n");
1020                         goto done;
1021                 }
1022         }
1023
1024         pa_dbf.free_result(pa_db, result);
1025         result = NULL;
1026
1027 send_notify:
1028
1029         if(pres_notifier_processes == 0) {
1030                 s = subs_array;
1031
1032                 while(s) {
1033                         if(notify(s, NULL, NULL, 0, 0) < 0) {
1034                                 LM_ERR("sending Notify request\n");
1035                                 goto done;
1036                         }
1037
1038                         /* delete from database also */
1039                         if(s->status == TERMINATED_STATUS) {
1040                                 if(pres_db_delete_status(s) < 0) {
1041                                         LM_ERR("failed to delete terminated "
1042                                                    "dialog from database\n");
1043                                         goto done;
1044                                 }
1045                         }
1046
1047                         s = s->next;
1048                 }
1049         }
1050
1051         free_subs_list(subs_array, PKG_MEM_TYPE, 0);
1052         return 0;
1053
1054 done:
1055         if(result)
1056                 pa_dbf.free_result(pa_db, result);
1057         free_subs_list(subs_array, PKG_MEM_TYPE, 0);
1058         if(ws_list) {
1059                 for(i = 0; i < n; i++) {
1060                         if(ws_list[i].w_user.s)
1061                                 pkg_free(ws_list[i].w_user.s);
1062                         if(ws_list[i].w_domain.s)
1063                                 pkg_free(ws_list[i].w_domain.s);
1064                         if(ws_list[i].reason.s)
1065                                 pkg_free(ws_list[i].reason.s);
1066                 }
1067                 pkg_free(ws_list);
1068         }
1069         return err_ret;
1070 }
1071
1072 /********************************************************************************/
1073
1074 static int update_pw_dialogs_dbonlymode(subs_t *subs, subs_t **subs_array)
1075 {
1076         db_key_t query_cols[5], db_cols[3];
1077         db_val_t query_vals[5], db_vals[3];
1078         db_key_t result_cols[26];
1079         int n_query_cols = 0, n_result_cols = 0, n_update_cols = 0;
1080         int event_col, pres_uri_col, watcher_user_col, watcher_domain_col;
1081         int r_pres_uri_col, r_to_user_col, r_to_domain_col;
1082         int r_from_user_col, r_from_domain_col, r_callid_col;
1083         int r_to_tag_col, r_from_tag_col, r_sockinfo_col;
1084         int r_event_id_col, r_local_contact_col, r_contact_col;
1085         int r_record_route_col, r_reason_col;
1086         int r_event_col, r_local_cseq_col, r_remote_cseq_col;
1087         int r_status_col, r_version_col;
1088         int r_expires_col, r_watcher_user_col, r_watcher_domain_col;
1089         int r_flags_col, r_user_agent_col;
1090         db1_res_t *result = NULL;
1091         db_val_t *row_vals;
1092         db_row_t *rows;
1093         int nr_rows, loop;
1094         subs_t s, *cs;
1095         str ev_sname;
1096
1097         if(pa_db == NULL) {
1098                 LM_ERR("null database connection\n");
1099                 return (-1);
1100         }
1101
1102         if(pa_dbf.use_table(pa_db, &active_watchers_table) < 0) {
1103                 LM_ERR("use table failed\n");
1104                 return (-1);
1105         }
1106
1107         query_cols[event_col = n_query_cols] = &str_event_col;
1108         query_vals[event_col].nul = 0;
1109         query_vals[event_col].type = DB1_STR;
1110         query_vals[event_col].val.str_val = subs->event->name;
1111         n_query_cols++;
1112
1113         query_cols[pres_uri_col = n_query_cols] = &str_presentity_uri_col;
1114         query_vals[pres_uri_col].nul = 0;
1115         query_vals[pres_uri_col].type = DB1_STR;
1116         query_vals[pres_uri_col].val.str_val = subs->pres_uri;
1117         n_query_cols++;
1118
1119         query_cols[watcher_user_col = n_query_cols] = &str_watcher_username_col;
1120         query_vals[watcher_user_col].nul = 0;
1121         query_vals[watcher_user_col].type = DB1_STR;
1122         query_vals[watcher_user_col].val.str_val = subs->watcher_user;
1123         n_query_cols++;
1124
1125         query_cols[watcher_domain_col = n_query_cols] = &str_watcher_domain_col;
1126         query_vals[watcher_domain_col].nul = 0;
1127         query_vals[watcher_domain_col].type = DB1_STR;
1128         query_vals[watcher_domain_col].val.str_val = subs->watcher_domain;
1129         n_query_cols++;
1130
1131
1132         result_cols[r_to_user_col = n_result_cols++] = &str_to_user_col;
1133         result_cols[r_to_domain_col = n_result_cols++] = &str_to_domain_col;
1134         result_cols[r_from_user_col = n_result_cols++] = &str_from_user_col;
1135         result_cols[r_from_domain_col = n_result_cols++] = &str_from_domain_col;
1136         result_cols[r_watcher_user_col = n_result_cols++] =
1137                         &str_watcher_username_col;
1138         result_cols[r_watcher_domain_col = n_result_cols++] =
1139                         &str_watcher_domain_col;
1140         result_cols[r_callid_col = n_result_cols++] = &str_callid_col;
1141         result_cols[r_to_tag_col = n_result_cols++] = &str_to_tag_col;
1142         result_cols[r_from_tag_col = n_result_cols++] = &str_from_tag_col;
1143         result_cols[r_sockinfo_col = n_result_cols++] = &str_socket_info_col;
1144         result_cols[r_event_id_col = n_result_cols++] = &str_event_id_col;
1145         result_cols[r_local_contact_col = n_result_cols++] = &str_local_contact_col;
1146         result_cols[r_record_route_col = n_result_cols++] = &str_record_route_col;
1147         result_cols[r_reason_col = n_result_cols++] = &str_reason_col;
1148         result_cols[r_local_cseq_col = n_result_cols++] = &str_local_cseq_col;
1149         result_cols[r_version_col = n_result_cols++] = &str_version_col;
1150         result_cols[r_expires_col = n_result_cols++] = &str_expires_col;
1151         result_cols[r_event_col = n_result_cols++] = &str_event_col;
1152         result_cols[r_pres_uri_col = n_result_cols++] = &str_presentity_uri_col;
1153         result_cols[r_contact_col = n_result_cols++] = &str_contact_col;
1154
1155         /* these ones are unused for some reason !!! */
1156         result_cols[r_remote_cseq_col = n_result_cols++] = &str_remote_cseq_col;
1157         result_cols[r_status_col = n_result_cols++] = &str_status_col;
1158         /*********************************************/
1159
1160         result_cols[r_flags_col = n_result_cols++] = &str_flags_col;
1161         result_cols[r_user_agent_col = n_result_cols++] = &str_user_agent_col;
1162
1163         if(pa_dbf.query(pa_db, query_cols, 0, query_vals, result_cols, n_query_cols,
1164                            n_result_cols, 0, &result)
1165                         < 0) {
1166                 LM_ERR("Can't query db\n");
1167                 if(result)
1168                         pa_dbf.free_result(pa_db, result);
1169                 return (-1);
1170         }
1171
1172         if(result == NULL)
1173                 return (-1);
1174
1175         nr_rows = RES_ROW_N(result);
1176
1177         LM_DBG("found %d matching dialogs\n", nr_rows);
1178
1179         if(nr_rows <= 0) {
1180                 pa_dbf.free_result(pa_db, result);
1181                 return 0;
1182         }
1183
1184         rows = RES_ROWS(result);
1185         /* get the results and fill in return data structure */
1186         for(loop = 0; loop < nr_rows; loop++) {
1187                 row_vals = ROW_VALUES(&rows[loop]);
1188
1189                 memset(&s, 0, sizeof(subs_t));
1190                 s.status = subs->status;
1191
1192                 s.reason.s = subs->reason.s;
1193                 s.reason.len = s.reason.s ? strlen(s.reason.s) : 0; //>>>>>>>>>>
1194
1195                 s.pres_uri.s = (char *)row_vals[r_pres_uri_col].val.string_val;
1196                 s.pres_uri.len = s.pres_uri.s ? strlen(s.pres_uri.s) : 0;
1197
1198                 s.to_user.s = (char *)row_vals[r_to_user_col].val.string_val;
1199                 s.to_user.len = s.to_user.s ? strlen(s.to_user.s) : 0;
1200
1201                 s.to_domain.s = (char *)row_vals[r_to_domain_col].val.string_val;
1202                 s.to_domain.len = s.to_domain.s ? strlen(s.to_domain.s) : 0;
1203
1204                 s.from_user.s = (char *)row_vals[r_from_user_col].val.string_val;
1205                 s.from_user.len = s.from_user.s ? strlen(s.from_user.s) : 0;
1206
1207                 s.from_domain.s = (char *)row_vals[r_from_domain_col].val.string_val;
1208                 s.from_domain.len = s.from_domain.s ? strlen(s.from_domain.s) : 0;
1209
1210                 s.watcher_user.s = (char *)row_vals[r_watcher_user_col].val.string_val;
1211                 s.watcher_user.len = s.watcher_user.s ? strlen(s.watcher_user.s) : 0;
1212
1213                 s.watcher_domain.s =
1214                                 (char *)row_vals[r_watcher_domain_col].val.string_val;
1215                 s.watcher_domain.len =
1216                                 s.watcher_domain.s ? strlen(s.watcher_domain.s) : 0;
1217
1218                 s.event_id.s = (char *)row_vals[r_event_id_col].val.string_val;
1219                 s.event_id.len = (s.event_id.s) ? strlen(s.event_id.s) : 0;
1220
1221                 s.to_tag.s = (char *)row_vals[r_to_tag_col].val.string_val;
1222                 s.to_tag.len = s.to_tag.s ? strlen(s.to_tag.s) : 0;
1223
1224                 s.from_tag.s = (char *)row_vals[r_from_tag_col].val.string_val;
1225                 s.from_tag.len = s.from_tag.s ? strlen(s.from_tag.s) : 0;
1226
1227                 s.callid.s = (char *)row_vals[r_callid_col].val.string_val;
1228                 s.callid.len = s.callid.s ? strlen(s.callid.s) : 0;
1229
1230                 s.record_route.s = (char *)row_vals[r_record_route_col].val.string_val;
1231                 s.record_route.len = (s.record_route.s) ? strlen(s.record_route.s) : 0;
1232
1233                 s.contact.s = (char *)row_vals[r_contact_col].val.string_val;
1234                 s.contact.len = s.contact.s ? strlen(s.contact.s) : 0;
1235
1236                 s.sockinfo_str.s = (char *)row_vals[r_sockinfo_col].val.string_val;
1237                 s.sockinfo_str.len = s.sockinfo_str.s ? strlen(s.sockinfo_str.s) : 0;
1238
1239                 s.local_contact.s =
1240                                 (char *)row_vals[r_local_contact_col].val.string_val;
1241                 s.local_contact.len = s.local_contact.s ? strlen(s.local_contact.s) : 0;
1242
1243                 ev_sname.s = (char *)row_vals[r_event_col].val.string_val;
1244                 ev_sname.len = ev_sname.s ? strlen(ev_sname.s) : 0;
1245
1246                 s.event = contains_event(&ev_sname, NULL);
1247
1248                 if(s.event == NULL) {
1249                         LM_ERR("event not found and set to NULL\n");
1250                 }
1251
1252                 s.local_cseq = row_vals[r_local_cseq_col].val.int_val;
1253
1254                 s.expires = row_vals[r_expires_col].val.int_val;
1255
1256                 if(s.expires > (int)time(NULL) + pres_expires_offset)
1257                         s.expires -= (int)time(NULL);
1258                 else
1259                         s.expires = 0;
1260
1261                 s.version = row_vals[r_version_col].val.int_val;
1262
1263                 s.flags = row_vals[r_flags_col].val.int_val;
1264                 s.user_agent.s = (char *)row_vals[r_user_agent_col].val.string_val;
1265                 s.user_agent.len = (s.user_agent.s) ? strlen(s.user_agent.s) : 0;
1266
1267
1268                 cs = mem_copy_subs(&s, PKG_MEM_TYPE);
1269                 if(cs == NULL) {
1270                         LM_ERR("while copying subs_t structure\n");
1271                         /* tidy up and return */
1272                         pa_dbf.free_result(pa_db, result);
1273                         return (-1);
1274                 }
1275                 cs->local_cseq++;
1276                 cs->next = (*subs_array);
1277                 (*subs_array) = cs;
1278
1279                 printf_subs(cs);
1280         }
1281
1282         pa_dbf.free_result(pa_db, result);
1283
1284         if(pres_notifier_processes == 0 && subs->status == TERMINATED_STATUS) {
1285                 /* delete the records */
1286                 if(pa_dbf.delete(pa_db, query_cols, 0, query_vals, n_query_cols) < 0) {
1287                         LM_ERR("sql delete failed\n");
1288                         return (-1);
1289                 }
1290
1291                 return (0);
1292         }
1293
1294         /* otherwise we update the records */
1295         db_cols[n_update_cols] = &str_status_col;
1296         db_vals[n_update_cols].type = DB1_INT;
1297         db_vals[n_update_cols].nul = 0;
1298         db_vals[n_update_cols].val.int_val = subs->status;
1299         n_update_cols++;
1300
1301         db_cols[n_update_cols] = &str_reason_col;
1302         db_vals[n_update_cols].type = DB1_STR;
1303         db_vals[n_update_cols].nul = 0;
1304         db_vals[n_update_cols].val.str_val = subs->reason;
1305         n_update_cols++;
1306
1307         db_cols[n_update_cols] = &str_updated_col;
1308         db_vals[n_update_cols].type = DB1_INT;
1309         db_vals[n_update_cols].nul = 0;
1310         if(subs->callid.len == 0 || subs->from_tag.len == 0) {
1311                 db_vals[n_update_cols].val.int_val =
1312                                 (int)((kam_rand() / (KAM_RAND_MAX + 1.0))
1313                                                 * (pres_waitn_time * pres_notifier_poll_rate
1314                                                                   * pres_notifier_processes));
1315         } else {
1316                 db_vals[n_update_cols].val.int_val =
1317                                 core_case_hash(&subs->callid, &subs->from_tag, 0)
1318                                 % (pres_waitn_time * pres_notifier_poll_rate
1319                                                   * pres_notifier_processes);
1320         }
1321         n_update_cols++;
1322
1323         if(pa_dbf.update(pa_db, query_cols, 0, query_vals, db_cols, db_vals,
1324                            n_query_cols, n_update_cols)
1325                         < 0) {
1326                 LM_ERR("DB update failed\n");
1327                 return (-1);
1328         }
1329
1330         return (0);
1331 }
1332
1333 /********************************************************************************/
1334
1335 static int update_pw_dialogs(
1336                 subs_t *subs, unsigned int hash_code, subs_t **subs_array)
1337 {
1338         subs_t *s, *ps, *cs;
1339         int i = 0;
1340
1341         LM_DBG("start\n");
1342
1343         if(pres_subs_dbmode == DB_ONLY) {
1344                 return (update_pw_dialogs_dbonlymode(subs, subs_array));
1345         }
1346
1347         lock_get(&subs_htable[hash_code].lock);
1348
1349         ps = subs_htable[hash_code].entries;
1350
1351         while(ps && ps->next) {
1352                 s = ps->next;
1353
1354                 if(s->event == subs->event && s->pres_uri.len == subs->pres_uri.len
1355                                 && s->watcher_user.len == subs->watcher_user.len
1356                                 && s->watcher_domain.len == subs->watcher_domain.len
1357                                 && presence_sip_uri_match(&s->pres_uri, &subs->pres_uri) == 0
1358                                 && presence_sip_uri_match(&s->watcher_user, &subs->watcher_user)
1359                                                    == 0
1360                                 && presence_sip_uri_match(
1361                                                    &s->watcher_domain, &subs->watcher_domain)
1362                                                    == 0) {
1363                         i++;
1364                         s->status = subs->status;
1365                         s->reason = subs->reason;
1366                         s->db_flag = UPDATEDB_FLAG;
1367
1368                         cs = mem_copy_subs(s, PKG_MEM_TYPE);
1369                         if(cs == NULL) {
1370                                 LM_ERR("copying subs_t structure\n");
1371                                 lock_release(&subs_htable[hash_code].lock);
1372                                 return -1;
1373                         }
1374                         cs->local_cseq++;
1375                         cs->expires -= (int)time(NULL);
1376                         cs->next = (*subs_array);
1377                         (*subs_array) = cs;
1378                         if(subs->status == TERMINATED_STATUS) {
1379                                 ps->next = s->next;
1380                                 shm_free(s->contact.s);
1381                                 shm_free(s);
1382                                 LM_DBG(" deleted terminated dialog from hash table\n");
1383                         } else
1384                                 ps = s;
1385
1386                         printf_subs(cs);
1387                 } else
1388                         ps = s;
1389         }
1390
1391         LM_DBG("found %d matching dialogs\n", i);
1392         lock_release(&subs_htable[hash_code].lock);
1393
1394         return 0;
1395 }
1396
1397 static int w_pres_auth_status(struct sip_msg *_msg, char *_sp1, char *_sp2)
1398 {
1399         str watcher_uri, presentity_uri;
1400
1401         if(fixup_get_svalue(_msg, (gparam_t *)_sp1, &watcher_uri) != 0) {
1402                 LM_ERR("invalid watcher uri parameter");
1403                 return -1;
1404         }
1405
1406         if(fixup_get_svalue(_msg, (gparam_t *)_sp2, &presentity_uri) != 0) {
1407                 LM_ERR("invalid presentity uri parameter");
1408                 return -1;
1409         }
1410
1411         if(watcher_uri.len == 0 || watcher_uri.s == NULL) {
1412                 LM_ERR("missing watcher uri\n");
1413                 return -1;
1414         }
1415
1416         if(presentity_uri.len == 0 || presentity_uri.s == NULL) {
1417                 LM_DBG("missing presentity uri\n");
1418                 return -1;
1419         }
1420
1421         return pres_auth_status(_msg, watcher_uri, presentity_uri);
1422 }
1423
1424 int ki_pres_auth_status(sip_msg_t *msg, str *watcher_uri, str *presentity_uri)
1425 {
1426         if(watcher_uri == NULL || presentity_uri == NULL) {
1427                 LM_ERR("invalid parameters\n");
1428                 return -1;
1429         }
1430         return pres_auth_status(msg, *watcher_uri, *presentity_uri);
1431 }
1432
1433 int pres_auth_status(struct sip_msg *msg, str watcher_uri, str presentity_uri)
1434 {
1435         str event;
1436         struct sip_uri uri;
1437         pres_ev_t *ev;
1438         str *rules_doc = NULL;
1439         subs_t subs;
1440         int res;
1441
1442         event.s = "presence";
1443         event.len = 8;
1444
1445         ev = contains_event(&event, NULL);
1446         if(ev == NULL) {
1447                 LM_ERR("event is not registered\n");
1448                 return -1;
1449         }
1450         if(ev->get_rules_doc == NULL) {
1451                 LM_DBG("event does not require authorization");
1452                 return ACTIVE_STATUS;
1453         }
1454         if(parse_uri(presentity_uri.s, presentity_uri.len, &uri) < 0) {
1455                 LM_ERR("failed to parse presentity uri\n");
1456                 return -1;
1457         }
1458         res = ev->get_rules_doc(&uri.user, &uri.host, &rules_doc);
1459         if((res < 0) || (rules_doc == NULL) || (rules_doc->s == NULL)) {
1460                 LM_DBG("no xcap rules doc found for presentity uri\n");
1461                 return PENDING_STATUS;
1462         }
1463
1464         if(parse_uri(watcher_uri.s, watcher_uri.len, &uri) < 0) {
1465                 LM_ERR("failed to parse watcher uri\n");
1466                 goto err;
1467         }
1468
1469         subs.watcher_user = uri.user;
1470         subs.watcher_domain = uri.host;
1471         subs.pres_uri = presentity_uri;
1472         subs.auth_rules_doc = rules_doc;
1473         if(ev->get_auth_status(&subs) < 0) {
1474                 LM_ERR("getting status from rules document\n");
1475                 goto err;
1476         }
1477         LM_DBG("auth status of watcher <%.*s> on presentity <%.*s> is %d\n",
1478                         watcher_uri.len, watcher_uri.s, presentity_uri.len,
1479                         presentity_uri.s, subs.status);
1480         pkg_free(rules_doc->s);
1481         pkg_free(rules_doc);
1482         if((subs.reason.len == 12)
1483                         && (strncmp(subs.reason.s, "polite-block", 12) == 0))
1484                 return POLITE_BLOCK_STATUS;
1485         return subs.status;
1486
1487 err:
1488         pkg_free(rules_doc->s);
1489         pkg_free(rules_doc);
1490         return -1;
1491 }
1492
1493 /**
1494  * wrapper for pres_refresh_watchers to use in config
1495  */
1496 static int w_pres_refresh_watchers(
1497                 struct sip_msg *msg, char *puri, char *pevent, char *ptype)
1498 {
1499         str pres_uri;
1500         str event;
1501         int refresh_type;
1502
1503         if(fixup_get_svalue(msg, (gparam_p)puri, &pres_uri) != 0) {
1504                 LM_ERR("invalid uri parameter");
1505                 return -1;
1506         }
1507
1508         if(fixup_get_svalue(msg, (gparam_p)pevent, &event) != 0) {
1509                 LM_ERR("invalid uri parameter");
1510                 return -1;
1511         }
1512
1513         if(fixup_get_ivalue(msg, (gparam_p)ptype, &refresh_type) != 0) {
1514                 LM_ERR("no type value\n");
1515                 return -1;
1516         }
1517
1518         if(refresh_type == 2) {
1519                 LM_ERR("Wrong number of parameters for type 2\n");
1520                 return -1;
1521         }
1522
1523         if(pres_refresh_watchers(&pres_uri, &event, refresh_type, NULL, NULL) < 0)
1524                 return -1;
1525
1526         return 1;
1527 }
1528
1529 static int w_pres_refresh_watchers5(struct sip_msg *msg, char *puri,
1530                 char *pevent, char *ptype, char *furi, char *fname)
1531 {
1532         str pres_uri, event, file_uri, filename;
1533         int refresh_type;
1534
1535         if(fixup_get_svalue(msg, (gparam_p)puri, &pres_uri) != 0) {
1536                 LM_ERR("invalid uri parameter");
1537                 return -1;
1538         }
1539
1540         if(fixup_get_svalue(msg, (gparam_p)pevent, &event) != 0) {
1541                 LM_ERR("invalid event parameter");
1542                 return -1;
1543         }
1544
1545         if(fixup_get_ivalue(msg, (gparam_p)ptype, &refresh_type) != 0) {
1546                 LM_ERR("no type value\n");
1547                 return -1;
1548         }
1549
1550         if(fixup_get_svalue(msg, (gparam_p)furi, &file_uri) != 0) {
1551                 LM_ERR("invalid file uri parameter");
1552                 return -1;
1553         }
1554
1555         if(fixup_get_svalue(msg, (gparam_p)fname, &filename) != 0) {
1556                 LM_ERR("invalid filename parameter");
1557                 return -1;
1558         }
1559
1560         if(refresh_type != 2) {
1561                 LM_ERR("Wrong number of parameters for type %d\n", refresh_type);
1562                 return -1;
1563         }
1564
1565         if(pres_refresh_watchers(
1566                            &pres_uri, &event, refresh_type, &file_uri, &filename)
1567                         < 0)
1568                 return -1;
1569
1570         return 1;
1571 }
1572
1573 /**
1574  * fixup for w_pres_refresh_watchers
1575  */
1576 static int fixup_refresh_watchers(void **param, int param_no)
1577 {
1578         if(param_no == 1) {
1579                 return fixup_spve_null(param, 1);
1580         } else if(param_no == 2) {
1581                 return fixup_spve_null(param, 1);
1582         } else if(param_no == 3) {
1583                 return fixup_igp_null(param, 1);
1584         } else if(param_no == 4) {
1585                 return fixup_spve_null(param, 1);
1586         } else if(param_no == 5) {
1587                 return fixup_spve_null(param, 1);
1588         }
1589
1590         return 0;
1591 }
1592
1593
1594 /**
1595  * wrapper for update_watchers_status to use via kemi
1596  */
1597 static int ki_pres_update_watchers(
1598                 struct sip_msg *msg, str *pres_uri, str *event)
1599 {
1600         pres_ev_t *ev;
1601         struct sip_uri uri;
1602         str *rules_doc = NULL;
1603         int ret;
1604
1605         ev = contains_event(event, NULL);
1606         if(ev == NULL) {
1607                 LM_ERR("event %.*s is not registered\n", event->len, event->s);
1608                 return -1;
1609         }
1610         if(ev->get_rules_doc == NULL) {
1611                 LM_DBG("event  %.*s does not provide rules doc API\n", event->len,
1612                                 event->s);
1613                 return -1;
1614         }
1615         if(parse_uri(pres_uri->s, pres_uri->len, &uri) < 0) {
1616                 LM_ERR("failed to parse presentity uri [%.*s]\n", pres_uri->len,
1617                                 pres_uri->s);
1618                 return -1;
1619         }
1620         ret = ev->get_rules_doc(&uri.user, &uri.host, &rules_doc);
1621         if((ret < 0) || (rules_doc == NULL) || (rules_doc->s == NULL)) {
1622                 LM_DBG("no xcap rules doc found for presentity uri [%.*s]\n",
1623                                 pres_uri->len, pres_uri->s);
1624                 if(rules_doc != NULL)
1625                         pkg_free(rules_doc);
1626                 return -1;
1627         }
1628         ret = 1;
1629         if(update_watchers_status(pres_uri, ev, rules_doc) < 0) {
1630                 LM_ERR("updating watchers in presence\n");
1631                 ret = -1;
1632         }
1633
1634         pkg_free(rules_doc->s);
1635         pkg_free(rules_doc);
1636
1637         return ret;
1638 }
1639
1640 /**
1641  * wrapper for update_watchers_status to use in config
1642  */
1643 static int w_pres_update_watchers(struct sip_msg *msg, char *puri, char *pevent)
1644 {
1645         str pres_uri;
1646         str event;
1647
1648         if(fixup_get_svalue(msg, (gparam_p)puri, &pres_uri) != 0) {
1649                 LM_ERR("invalid uri parameter");
1650                 return -1;
1651         }
1652
1653         if(fixup_get_svalue(msg, (gparam_p)pevent, &event) != 0) {
1654                 LM_ERR("invalid uri parameter");
1655                 return -1;
1656         }
1657         return ki_pres_update_watchers(msg, &pres_uri, &event);
1658 }
1659 /**
1660  * fixup for w_pres_update_watchers
1661  */
1662 static int fixup_update_watchers(void **param, int param_no)
1663 {
1664         if(param_no == 1) {
1665                 return fixup_spve_null(param, 1);
1666         } else if(param_no == 2) {
1667                 return fixup_spve_null(param, 1);
1668         }
1669         return 0;
1670 }
1671
1672 /*! \brief
1673  *  rpc cmd: presence.refreshWatchers
1674  *                      \<presentity_uri>
1675  *                      \<event>
1676  *          \<refresh_type> // can be:  = 0 -> watchers autentification type or
1677  *                                                                        != 0 -> publish type //
1678  *              * */
1679 void rpc_presence_refresh_watchers(rpc_t *rpc, void *ctx)
1680 {
1681         str pres_uri = {0, 0};
1682         str event = {0, 0};
1683         str file_uri = {0, 0};
1684         str filename = {0, 0};
1685         unsigned int refresh_type;
1686         int pn;
1687
1688         LM_DBG("initiation refresh of watchers\n");
1689
1690         pn = rpc->scan(ctx, "SSu*SS", &pres_uri, &event, &refresh_type, &file_uri,
1691                         &filename);
1692         if(pn < 3) {
1693                 rpc->fault(ctx, 500, "Not enough parameters");
1694                 return;
1695         }
1696
1697         if(pres_uri.s == NULL || pres_uri.len == 0) {
1698                 LM_ERR("empty uri\n");
1699                 rpc->fault(ctx, 500, "Empty presentity URI");
1700                 return;
1701         }
1702
1703         if(event.s == NULL || event.len == 0) {
1704                 LM_ERR("empty event parameter\n");
1705                 rpc->fault(ctx, 500, "Empty event parameter");
1706                 return;
1707         }
1708         LM_DBG("event '%.*s'\n", event.len, event.s);
1709
1710         if(refresh_type == 2) {
1711                 if(pn < 5) {
1712                         LM_ERR("empty file uri or name parameters\n");
1713                         rpc->fault(ctx, 500, "No file uri or name parameters");
1714                         return;
1715                 }
1716                 if(file_uri.s == NULL || file_uri.len == 0) {
1717                         LM_ERR("empty file uri parameter\n");
1718                         rpc->fault(ctx, 500, "Empty file uri parameter");
1719                         return;
1720                 }
1721
1722                 if(filename.s == NULL || filename.len == 0) {
1723                         LM_ERR("empty file name parameter\n");
1724                         rpc->fault(ctx, 500, "Empty file name parameter");
1725                         return;
1726                 }
1727         }
1728
1729         if(pres_refresh_watchers(&pres_uri, &event, refresh_type,
1730                            file_uri.len ? &file_uri : NULL, filename.len ? &filename : NULL)
1731                         < 0) {
1732                 rpc->fault(ctx, 500, "Execution failed");
1733                 return;
1734         }
1735 }
1736
1737 static const char *rpc_presence_refresh_watchers_doc[2] = {
1738                 "Trigger refresh of watchers", 0};
1739
1740 /*! \brief
1741  *  rpc cmd: presence.updateWatchers
1742  *                      \<presentity_uri>
1743  *                      \<event>
1744  *              * */
1745 void rpc_presence_update_watchers(rpc_t *rpc, void *ctx)
1746 {
1747         str pres_uri = {0, 0};
1748         str event = {0, 0};
1749         int pn;
1750
1751         LM_DBG("init update of watchers\n");
1752
1753         pn = rpc->scan(ctx, "SS", &pres_uri, &event);
1754         if(pn < 2) {
1755                 rpc->fault(ctx, 500, "Not enough parameters");
1756                 return;
1757         }
1758
1759         if(pres_uri.s == NULL || pres_uri.len == 0) {
1760                 LM_ERR("empty uri\n");
1761                 rpc->fault(ctx, 500, "Empty presentity URI");
1762                 return;
1763         }
1764
1765         if(event.s == NULL || event.len == 0) {
1766                 LM_ERR("empty event parameter\n");
1767                 rpc->fault(ctx, 500, "Empty event parameter");
1768                 return;
1769         }
1770         LM_DBG("uri '%.*s' - event '%.*s'\n", pres_uri.len, pres_uri.s,
1771                         event.len, event.s);
1772
1773         if(ki_pres_update_watchers(NULL, &pres_uri, &event)<0) {
1774                 rpc->fault(ctx, 500, "Processing error");
1775                 return;
1776         }
1777 }
1778
1779 static const char *rpc_presence_update_watchers_doc[2] = {
1780                 "Trigger update of watchers", 0};
1781
1782
1783 void rpc_presence_cleanup(rpc_t *rpc, void *c)
1784 {
1785         LM_DBG("rpc_presence_cleanup:start\n");
1786
1787         (void)msg_watchers_clean(0, 0);
1788         (void)msg_presentity_clean(0, 0);
1789         (void)timer_db_update(0, 0);
1790
1791         rpc->rpl_printf(c, "Reload OK");
1792         return;
1793 }
1794
1795 static const char *rpc_presence_cleanup_doc[2] = {
1796                 "Manually triggers the cleanup functions for the active_watchers, "
1797                 "presentity, and watchers tables.",
1798                 0};
1799
1800 rpc_export_t presence_rpc[] = {
1801                 {"presence.cleanup", rpc_presence_cleanup, rpc_presence_cleanup_doc, 0},
1802                 {"presence.refreshWatchers", rpc_presence_refresh_watchers,
1803                                 rpc_presence_refresh_watchers_doc, 0},
1804                 {"presence.updateWatchers", rpc_presence_update_watchers,
1805                                 rpc_presence_update_watchers_doc, 0},
1806                 {0, 0, 0, 0}};
1807
1808 static int presence_init_rpc(void)
1809 {
1810         if(rpc_register_array(presence_rpc) != 0) {
1811                 LM_ERR("failed to register RPC commands\n");
1812                 return -1;
1813         }
1814         return 0;
1815 }
1816
1817 static int sip_uri_case_sensitive_match(str *s1, str *s2)
1818 {
1819         if(!s1) {
1820                 LM_ERR("null pointer (s1) in sip_uri_match\n");
1821                 return -1;
1822         }
1823         if(!s2) {
1824                 LM_ERR("null pointer (s2) in sip_uri_match\n");
1825                 return -1;
1826         }
1827         return strncmp(s1->s, s2->s, s2->len);
1828 }
1829
1830 static int sip_uri_case_insensitive_match(str *s1, str *s2)
1831 {
1832         if(!s1) {
1833                 LM_ERR("null pointer (s1) in sip_uri_match\n");
1834                 return -1;
1835         }
1836         if(!s2) {
1837                 LM_ERR("null pointer (s2) in sip_uri_match\n");
1838                 return -1;
1839         }
1840         return strncasecmp(s1->s, s2->s, s2->len);
1841 }
1842
1843 static int fixup_has_subscribers(void **param, int param_no)
1844 {
1845         if(param_no == 1) {
1846                 return fixup_spve_null(param, 1);
1847         } else if(param_no == 2) {
1848                 return fixup_spve_null(param, 1);
1849         }
1850
1851         return 0;
1852 }
1853
1854 static int ki_pres_has_subscribers(sip_msg_t *msg, str *pres_uri, str *wevent)
1855 {
1856         pres_ev_t *ev;
1857
1858         ev = contains_event(wevent, NULL);
1859         if(ev == NULL) {
1860                 LM_ERR("event is not registered\n");
1861                 return -1;
1862         }
1863
1864         return get_subscribers_count(msg, *pres_uri, *wevent) > 0 ? 1 : -1;
1865 }
1866
1867 static int w_pres_has_subscribers(sip_msg_t *msg, char *_pres_uri, char *_event)
1868 {
1869         str presentity_uri, watched_event;
1870
1871         if(fixup_get_svalue(msg, (gparam_p)_pres_uri, &presentity_uri) != 0) {
1872                 LM_ERR("invalid presentity_uri parameter");
1873                 return -1;
1874         }
1875         if(fixup_get_svalue(msg, (gparam_p)_event, &watched_event) != 0) {
1876                 LM_ERR("invalid watched_event parameter");
1877                 return -1;
1878         }
1879
1880         return ki_pres_has_subscribers(msg, &presentity_uri, &watched_event);
1881 }
1882
1883 /**
1884  *
1885  */
1886 /* clang-format off */
1887 static sr_kemi_t sr_kemi_presence_exports[] = {
1888         { str_init("presence"), str_init("handle_publish"),
1889                 SR_KEMIP_INT, ki_handle_publish,
1890                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
1891                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1892         },
1893         { str_init("presence"), str_init("handle_publish_uri"),
1894                 SR_KEMIP_INT, ki_handle_publish_uri,
1895                 { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
1896                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1897         },
1898         { str_init("presence"), str_init("handle_subscribe"),
1899                 SR_KEMIP_INT, handle_subscribe0,
1900                 { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
1901                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1902         },
1903         { str_init("presence"), str_init("handle_subscribe_uri"),
1904                 SR_KEMIP_INT, handle_subscribe_uri,
1905                 { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
1906                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1907         },
1908         { str_init("presence"), str_init("pres_refresh_watchers"),
1909                 SR_KEMIP_INT, ki_pres_refresh_watchers,
1910                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_INT,
1911                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1912         },
1913         { str_init("presence"), str_init("pres_refresh_watchers_file"),
1914                 SR_KEMIP_INT, ki_pres_refresh_watchers_file,
1915                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_INT,
1916                         SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE }
1917         },
1918         { str_init("presence"), str_init("pres_update_watchers"),
1919                 SR_KEMIP_INT, ki_pres_update_watchers,
1920                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
1921                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1922         },
1923         { str_init("presence"), str_init("pres_has_subscribers"),
1924                 SR_KEMIP_INT, ki_pres_has_subscribers,
1925                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
1926                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1927         },
1928         { str_init("presence"), str_init("pres_auth_status"),
1929                 SR_KEMIP_INT, ki_pres_auth_status,
1930                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
1931                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
1932         },
1933
1934         { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
1935 };
1936 /* clang-format on */
1937
1938 /**
1939  *
1940  */
1941 int mod_register(char *path, int *dlflags, void *p1, void *p2)
1942 {
1943         sr_kemi_modules_add(sr_kemi_presence_exports);
1944         return 0;
1945 }