presence: add delete_same_subs modparam
[sip-router] / src / modules / presence / hash.c
1 /*
2  * presence module - presence server implementation
3  *
4  * Copyright (C) 2007 Voice Sistem S.R.L.
5  *
6  * This file is part of Kamailio, a free SIP server.
7  *
8  * Kamailio is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * Kamailio is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License 
19  * along with this program; if not, write to the Free Software 
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 /*! \file
25  * \brief Kamailio presence module
26  * \ingroup presence 
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include "../../core/mem/shm_mem.h"
32 #include "../../core/hashes.h"
33 #include "../../core/dprint.h"
34 #include "../../core/str.h"
35 #include "../pua/hash.h"
36 #include "presence.h"
37 #include "hash.h"
38 #include "notify.h"
39
40 /* matching mode when removing subscriptions from memory */
41 extern int pres_subs_remove_match;
42
43 /**
44  * create the subscription hash table in shared memory
45  * - hash_size: number of slots
46  */
47 shtable_t new_shtable(int hash_size)
48 {
49         shtable_t htable = NULL;
50         int i, j;
51
52         i = 0;
53         htable = (subs_entry_t *)shm_malloc(hash_size * sizeof(subs_entry_t));
54         if(htable == NULL) {
55                 ERR_MEM(SHARE_MEM);
56         }
57         memset(htable, 0, hash_size * sizeof(subs_entry_t));
58         for(i = 0; i < hash_size; i++) {
59                 if(lock_init(&htable[i].lock) == 0) {
60                         LM_ERR("initializing lock [%d]\n", i);
61                         goto error;
62                 }
63                 htable[i].entries = (subs_t *)shm_malloc(sizeof(subs_t));
64                 if(htable[i].entries == NULL) {
65                         lock_destroy(&htable[i].lock);
66                         ERR_MEM(SHARE_MEM);
67                 }
68                 memset(htable[i].entries, 0, sizeof(subs_t));
69                 htable[i].entries->next = NULL;
70         }
71
72         return htable;
73
74 error:
75         if(htable) {
76                 for(j = 0; j < i; j++) {
77                         lock_destroy(&htable[j].lock);
78                         shm_free(htable[j].entries);
79                 }
80                 shm_free(htable);
81         }
82         return NULL;
83 }
84
85 void destroy_shtable(shtable_t htable, int hash_size)
86 {
87         int i;
88
89         if(htable == NULL)
90                 return;
91
92         for(i = 0; i < hash_size; i++) {
93                 lock_destroy(&htable[i].lock);
94                 free_subs_list(htable[i].entries->next, SHM_MEM_TYPE, 1);
95                 shm_free(htable[i].entries);
96                 htable[i].entries = NULL;
97         }
98         shm_free(htable);
99         htable = NULL;
100 }
101
102 subs_t *search_shtable(shtable_t htable, str callid, str to_tag, str from_tag,
103                 unsigned int hash_code)
104 {
105         subs_t *s;
106
107         s = htable[hash_code].entries ? htable[hash_code].entries->next : NULL;
108
109         while(s) {
110                 if(s->callid.len == callid.len
111                                 && strncmp(s->callid.s, callid.s, callid.len) == 0
112                                 && s->to_tag.len == to_tag.len
113                                 && strncmp(s->to_tag.s, to_tag.s, to_tag.len) == 0
114                                 && s->from_tag.len == from_tag.len
115                                 && strncmp(s->from_tag.s, from_tag.s, from_tag.len) == 0)
116                         return s;
117                 s = s->next;
118         }
119
120         return NULL;
121 }
122
123 subs_t *mem_copy_subs(subs_t *s, int mem_type)
124 {
125         int size;
126         subs_t *dest;
127
128         size = sizeof(subs_t)
129                    + (s->pres_uri.len + s->to_user.len + s->to_domain.len
130                                          + s->from_user.len + s->from_domain.len + s->callid.len
131                                          + s->to_tag.len + s->from_tag.len + s->sockinfo_str.len
132                                          + s->event_id.len + s->local_contact.len + s->contact.len
133                                          + s->record_route.len + s->reason.len + s->watcher_user.len
134                                          + s->watcher_domain.len + s->user_agent.len + 1)
135                                          * sizeof(char);
136
137         if(mem_type & PKG_MEM_TYPE)
138                 dest = (subs_t *)pkg_malloc(size);
139         else
140                 dest = (subs_t *)shm_malloc(size);
141
142         if(dest == NULL) {
143                 ERR_MEM((mem_type == PKG_MEM_TYPE) ? PKG_MEM_STR : SHARE_MEM);
144         }
145         memset(dest, 0, size);
146         size = sizeof(subs_t);
147
148         CONT_COPY(dest, dest->pres_uri, s->pres_uri);
149         CONT_COPY(dest, dest->to_user, s->to_user);
150         CONT_COPY(dest, dest->to_domain, s->to_domain);
151         CONT_COPY(dest, dest->from_user, s->from_user);
152         CONT_COPY(dest, dest->from_domain, s->from_domain);
153         CONT_COPY(dest, dest->watcher_user, s->watcher_user);
154         CONT_COPY(dest, dest->watcher_domain, s->watcher_domain);
155         CONT_COPY(dest, dest->to_tag, s->to_tag);
156         CONT_COPY(dest, dest->from_tag, s->from_tag);
157         CONT_COPY(dest, dest->callid, s->callid);
158         CONT_COPY(dest, dest->sockinfo_str, s->sockinfo_str);
159         CONT_COPY(dest, dest->local_contact, s->local_contact);
160         CONT_COPY(dest, dest->contact, s->contact);
161         CONT_COPY(dest, dest->record_route, s->record_route);
162         CONT_COPY(dest, dest->user_agent, s->user_agent);
163         if(s->event_id.s)
164                 CONT_COPY(dest, dest->event_id, s->event_id);
165         if(s->reason.s)
166                 CONT_COPY(dest, dest->reason, s->reason);
167
168         dest->event = s->event;
169         dest->local_cseq = s->local_cseq;
170         dest->remote_cseq = s->remote_cseq;
171         dest->status = s->status;
172         dest->version = s->version;
173         dest->send_on_cback = s->send_on_cback;
174         dest->expires = s->expires;
175         dest->db_flag = s->db_flag;
176         dest->flags = s->flags;
177
178         return dest;
179
180 error:
181         return NULL;
182 }
183
184
185 subs_t *mem_copy_subs_noc(subs_t *s)
186 {
187         int size;
188         subs_t *dest;
189
190         size = sizeof(subs_t)
191                    + (s->pres_uri.len + s->to_user.len + s->to_domain.len
192                                          + s->from_user.len + s->from_domain.len + s->callid.len
193                                          + s->to_tag.len + s->from_tag.len + s->sockinfo_str.len
194                                          + s->event_id.len + s->local_contact.len + s->reason.len
195                                          + s->watcher_user.len + s->watcher_domain.len
196                                          + s->user_agent.len + 1)
197                                          * sizeof(char);
198
199         dest = (subs_t *)shm_malloc(size);
200         if(dest == NULL) {
201                 ERR_MEM(SHARE_MEM);
202         }
203         memset(dest, 0, size);
204         size = sizeof(subs_t);
205
206         CONT_COPY(dest, dest->pres_uri, s->pres_uri);
207         CONT_COPY(dest, dest->to_user, s->to_user);
208         CONT_COPY(dest, dest->to_domain, s->to_domain);
209         CONT_COPY(dest, dest->from_user, s->from_user);
210         CONT_COPY(dest, dest->from_domain, s->from_domain);
211         CONT_COPY(dest, dest->watcher_user, s->watcher_user);
212         CONT_COPY(dest, dest->watcher_domain, s->watcher_domain);
213         CONT_COPY(dest, dest->to_tag, s->to_tag);
214         CONT_COPY(dest, dest->from_tag, s->from_tag);
215         CONT_COPY(dest, dest->callid, s->callid);
216         CONT_COPY(dest, dest->sockinfo_str, s->sockinfo_str);
217         CONT_COPY(dest, dest->local_contact, s->local_contact);
218         CONT_COPY(dest, dest->user_agent, s->user_agent);
219         if(s->event_id.s)
220                 CONT_COPY(dest, dest->event_id, s->event_id);
221         if(s->reason.s)
222                 CONT_COPY(dest, dest->reason, s->reason);
223
224         dest->event = s->event;
225         dest->local_cseq = s->local_cseq;
226         dest->remote_cseq = s->remote_cseq;
227         dest->status = s->status;
228         dest->version = s->version;
229         dest->send_on_cback = s->send_on_cback;
230         dest->expires = s->expires;
231         dest->db_flag = s->db_flag;
232         dest->flags = s->flags;
233
234         dest->contact.s = (char *)shm_malloc(s->contact.len * sizeof(char));
235         if(dest->contact.s == NULL) {
236                 ERR_MEM(SHARE_MEM);
237         }
238         memcpy(dest->contact.s, s->contact.s, s->contact.len);
239         dest->contact.len = s->contact.len;
240
241         dest->record_route.s =
242                         (char *)shm_malloc((s->record_route.len + 1) * sizeof(char));
243         if(dest->record_route.s == NULL) {
244                 ERR_MEM(SHARE_MEM);
245         }
246         memcpy(dest->record_route.s, s->record_route.s, s->record_route.len);
247         dest->record_route.len = s->record_route.len;
248
249         return dest;
250
251 error:
252         if(dest)
253                 shm_free(dest);
254         return NULL;
255 }
256
257 int insert_shtable(shtable_t htable, unsigned int hash_code, subs_t *subs)
258 {
259         subs_t *new_rec = NULL;
260
261         if (pres_delete_same_subs) {
262                 subs_t* rec = NULL, *prev_rec = NULL;
263
264                 lock_get(&htable[hash_code].lock);
265                 /* search if there is another record with the same pres_uri & callid */
266                 rec = htable[hash_code].entries->next;
267                 while (rec) {
268                         if (subs->pres_uri.len == rec->pres_uri.len && subs->callid.len == rec->callid.len &&
269                                         memcmp(subs->pres_uri.s, rec->pres_uri.s, subs->pres_uri.len) == 0 &&
270                                         memcmp(subs->callid.s, rec->callid.s, subs->callid.len) == 0) {
271                                 LM_NOTICE("Found another record with the same pres_uri[%.*s] and callid[%.*s]\n",
272                                         subs->pres_uri.len, subs->pres_uri.s, subs->callid.len, subs->callid.s);
273                                 /* delete this record */
274
275                                 if (prev_rec)
276                                         prev_rec->next = rec->next;
277                                 else
278                                         htable[hash_code].entries->next = rec->next;
279
280                                 if (subs_dbmode != NO_DB)
281                                         delete_db_subs(&rec->to_tag, &rec->from_tag, &rec->callid);
282
283                                 if (rec->contact.s!=NULL)
284                                         shm_free(rec->contact.s);
285
286                                 shm_free(rec);
287                                 break;
288                         }
289                         prev_rec = rec;
290                         rec = rec->next;
291                 }
292                 lock_release(&htable[hash_code].lock);
293         }
294
295         new_rec = mem_copy_subs_noc(subs);
296         if(new_rec == NULL) {
297                 LM_ERR("copying in share memory a subs_t structure\n");
298                 return -1;
299         }
300         new_rec->expires += (int)time(NULL);
301
302         lock_get(&htable[hash_code].lock);
303         new_rec->next = htable[hash_code].entries->next;
304         htable[hash_code].entries->next = new_rec;
305         lock_release(&htable[hash_code].lock);
306
307         return 0;
308 }
309
310 int delete_shtable(shtable_t htable, unsigned int hash_code, subs_t *subs)
311 {
312         subs_t *s = NULL, *ps = NULL;
313         int found = -1;
314
315         lock_get(&htable[hash_code].lock);
316
317         ps = htable[hash_code].entries;
318         s = ps ? ps->next : NULL;
319
320         while(s) {
321                 if(pres_subs_remove_match == 0) {
322                         /* match on to-tag only (unique, local generated - faster) */
323                         if(s->to_tag.len == subs->to_tag.len
324                                         && strncmp(s->to_tag.s, subs->to_tag.s, subs->to_tag.len)
325                                                            == 0) {
326                                 found = 0;
327                         }
328                 } else {
329                         /* match on all dialog attributes (distributed systems) */
330                         if(s->callid.len == subs->callid.len
331                                         && s->to_tag.len == subs->to_tag.len
332                                         && s->from_tag.len == subs->from_tag.len
333                                         && strncmp(s->callid.s, subs->callid.s, subs->callid.len)
334                                                            == 0
335                                         && strncmp(s->to_tag.s, subs->to_tag.s, subs->to_tag.len)
336                                                            == 0
337                                         && strncmp(s->from_tag.s, subs->from_tag.s,
338                                                            subs->from_tag.len)
339                                                            == 0) {
340                                 found = 0;
341                         }
342                 }
343                 if(found == 0) {
344                         found = s->local_cseq + 1;
345                         ps->next = s->next;
346                         if(s->contact.s != NULL) {
347                                 shm_free(s->contact.s);
348                                 s->contact.s = NULL;
349                         }
350                         if(s->record_route.s != NULL) {
351                                 shm_free(s->record_route.s);
352                                 s->record_route.s = NULL;
353                         }
354                         if(s) {
355                                 shm_free(s);
356                                 s = NULL;
357                         }
358                         break;
359                 }
360                 ps = s;
361                 s = s->next;
362         }
363         lock_release(&htable[hash_code].lock);
364         return found;
365 }
366
367 void free_subs_list(subs_t *s_array, int mem_type, int ic)
368 {
369         subs_t *s;
370
371         while(s_array) {
372                 s = s_array;
373                 s_array = s_array->next;
374                 if(mem_type & PKG_MEM_TYPE) {
375                         if(ic) {
376                                 pkg_free(s->contact.s);
377                                 s->contact.s = NULL;
378                         }
379                         pkg_free(s);
380                         s = NULL;
381                 } else {
382                         if(ic) {
383                                 shm_free(s->contact.s);
384                                 s->contact.s = NULL;
385                         }
386                         shm_free(s);
387                         s = NULL;
388                 }
389         }
390 }
391
392 int update_shtable(
393                 shtable_t htable, unsigned int hash_code, subs_t *subs, int type)
394 {
395         subs_t *s;
396
397         lock_get(&htable[hash_code].lock);
398
399         s = search_shtable(
400                         htable, subs->callid, subs->to_tag, subs->from_tag, hash_code);
401         if(s == NULL) {
402                 LM_DBG("record not found in hash table\n");
403                 lock_release(&htable[hash_code].lock);
404                 return -1;
405         }
406
407         if(type & REMOTE_TYPE) {
408                 s->expires = subs->expires + (int)time(NULL);
409                 s->remote_cseq = subs->remote_cseq;
410         } else {
411                 subs->local_cseq = ++s->local_cseq;
412                 subs->version = ++s->version;
413         }
414
415         if(presence_sip_uri_match(&s->contact, &subs->contact)) {
416                 shm_free(s->contact.s);
417                 s->contact.s = (char *)shm_malloc(subs->contact.len * sizeof(char));
418                 if(s->contact.s == NULL) {
419                         lock_release(&htable[hash_code].lock);
420                         LM_ERR("no more shared memory\n");
421                         return -1;
422                 }
423                 memcpy(s->contact.s, subs->contact.s, subs->contact.len);
424                 s->contact.len = subs->contact.len;
425         }
426
427         shm_free(s->record_route.s);
428         s->record_route.s =
429                         (char *)shm_malloc(subs->record_route.len * sizeof(char));
430         if(s->record_route.s == NULL) {
431                 lock_release(&htable[hash_code].lock);
432                 LM_ERR("no more shared memory\n");
433                 return -1;
434         }
435         memcpy(s->record_route.s, subs->record_route.s, subs->record_route.len);
436         s->record_route.len = subs->record_route.len;
437
438         s->status = subs->status;
439         s->event = subs->event;
440         subs->db_flag = s->db_flag;
441
442         if(s->db_flag & NO_UPDATEDB_FLAG)
443                 s->db_flag = UPDATEDB_FLAG;
444
445         lock_release(&htable[hash_code].lock);
446
447         return 0;
448 }
449
450 phtable_t *new_phtable(void)
451 {
452         phtable_t *htable = NULL;
453         int i, j;
454
455         i = 0;
456         htable = (phtable_t *)shm_malloc(phtable_size * sizeof(phtable_t));
457         if(htable == NULL) {
458                 ERR_MEM(SHARE_MEM);
459         }
460         memset(htable, 0, phtable_size * sizeof(phtable_t));
461
462         for(i = 0; i < phtable_size; i++) {
463                 if(lock_init(&htable[i].lock) == 0) {
464                         LM_ERR("initializing lock [%d]\n", i);
465                         goto error;
466                 }
467                 htable[i].entries = (pres_entry_t *)shm_malloc(sizeof(pres_entry_t));
468                 if(htable[i].entries == NULL) {
469                         ERR_MEM(SHARE_MEM);
470                 }
471                 memset(htable[i].entries, 0, sizeof(pres_entry_t));
472                 htable[i].entries->next = NULL;
473         }
474
475         return htable;
476
477 error:
478         if(htable) {
479                 for(j = 0; j < i; j++) {
480                         if(htable[i].entries)
481                                 shm_free(htable[i].entries);
482                         else
483                                 break;
484                         lock_destroy(&htable[i].lock);
485                 }
486                 shm_free(htable);
487         }
488         return NULL;
489 }
490
491 void destroy_phtable(void)
492 {
493         int i;
494         pres_entry_t *p, *prev_p;
495
496         if(pres_htable == NULL)
497                 return;
498
499         for(i = 0; i < phtable_size; i++) {
500                 lock_destroy(&pres_htable[i].lock);
501                 p = pres_htable[i].entries;
502                 while(p) {
503                         prev_p = p;
504                         p = p->next;
505                         if(prev_p->sphere)
506                                 shm_free(prev_p->sphere);
507                         shm_free(prev_p);
508                 }
509         }
510         shm_free(pres_htable);
511 }
512 /* entry must be locked before calling this function */
513
514 pres_entry_t *search_phtable(str *pres_uri, int event, unsigned int hash_code)
515 {
516         pres_entry_t *p;
517
518         LM_DBG("pres_uri= %.*s\n", pres_uri->len, pres_uri->s);
519         p = pres_htable[hash_code].entries->next;
520         while(p) {
521                 if(p->event == event && p->pres_uri.len == pres_uri->len
522                                 && presence_sip_uri_match(&p->pres_uri, pres_uri) == 0)
523                         return p;
524                 p = p->next;
525         }
526         return NULL;
527 }
528
529 int insert_phtable(str *pres_uri, int event, char *sphere)
530 {
531         unsigned int hash_code;
532         pres_entry_t *p = NULL;
533         int size;
534
535         hash_code = core_case_hash(pres_uri, NULL, phtable_size);
536
537         lock_get(&pres_htable[hash_code].lock);
538
539         p = search_phtable(pres_uri, event, hash_code);
540         if(p) {
541                 p->publ_count++;
542                 lock_release(&pres_htable[hash_code].lock);
543                 return 0;
544         }
545         size = sizeof(pres_entry_t) + pres_uri->len * sizeof(char);
546
547         p = (pres_entry_t *)shm_malloc(size);
548         if(p == NULL) {
549                 lock_release(&pres_htable[hash_code].lock);
550                 ERR_MEM(SHARE_MEM);
551         }
552         memset(p, 0, size);
553
554         size = sizeof(pres_entry_t);
555         p->pres_uri.s = (char *)p + size;
556         memcpy(p->pres_uri.s, pres_uri->s, pres_uri->len);
557         p->pres_uri.len = pres_uri->len;
558
559         if(sphere) {
560                 p->sphere = (char *)shm_malloc((strlen(sphere) + 1) * sizeof(char));
561                 if(p->sphere == NULL) {
562                         lock_release(&pres_htable[hash_code].lock);
563                         shm_free(p);
564                         ERR_MEM(SHARE_MEM);
565                 }
566                 strcpy(p->sphere, sphere);
567         }
568
569         p->event = event;
570         p->publ_count = 1;
571
572         /* link the item in the hash table */
573         p->next = pres_htable[hash_code].entries->next;
574         pres_htable[hash_code].entries->next = p;
575
576         lock_release(&pres_htable[hash_code].lock);
577
578         return 0;
579
580 error:
581         return -1;
582 }
583
584 int delete_phtable(str *pres_uri, int event)
585 {
586         unsigned int hash_code;
587         pres_entry_t *p = NULL, *prev_p = NULL;
588
589         hash_code = core_case_hash(pres_uri, NULL, phtable_size);
590
591         lock_get(&pres_htable[hash_code].lock);
592
593         p = search_phtable(pres_uri, event, hash_code);
594         if(p == NULL) {
595                 LM_DBG("record not found\n");
596                 lock_release(&pres_htable[hash_code].lock);
597                 return 0;
598         }
599
600         p->publ_count--;
601         if(p->publ_count == 0) {
602                 /* delete record */
603                 prev_p = pres_htable[hash_code].entries;
604                 while(prev_p->next) {
605                         if(prev_p->next == p)
606                                 break;
607                         prev_p = prev_p->next;
608                 }
609                 if(prev_p->next == NULL) {
610                         LM_ERR("record not found\n");
611                         lock_release(&pres_htable[hash_code].lock);
612                         return -1;
613                 }
614                 prev_p->next = p->next;
615                 if(p->sphere)
616                         shm_free(p->sphere);
617
618                 shm_free(p);
619         }
620         lock_release(&pres_htable[hash_code].lock);
621
622         return 0;
623 }
624
625 int update_phtable(presentity_t *presentity, str pres_uri, str body)
626 {
627         char *sphere = NULL;
628         unsigned int hash_code;
629         pres_entry_t *p;
630         int ret = 0;
631         str *xcap_doc = NULL;
632
633         /* get new sphere */
634         sphere = extract_sphere(body);
635         if(sphere == NULL) {
636                 LM_DBG("no sphere defined in new body\n");
637                 return 0;
638         }
639
640         /* search for record in hash table */
641         hash_code = core_case_hash(&pres_uri, NULL, phtable_size);
642
643         lock_get(&pres_htable[hash_code].lock);
644
645         p = search_phtable(&pres_uri, presentity->event->evp->type, hash_code);
646         if(p == NULL) {
647                 lock_release(&pres_htable[hash_code].lock);
648                 goto done;
649         }
650
651         if(p->sphere) {
652                 if(strcmp(p->sphere, sphere) != 0) {
653                         /* new sphere definition */
654                         shm_free(p->sphere);
655                 } else {
656                         /* no change in sphere definition */
657                         lock_release(&pres_htable[hash_code].lock);
658                         pkg_free(sphere);
659                         return 0;
660                 }
661         }
662
663
664         p->sphere = (char *)shm_malloc((strlen(sphere) + 1) * sizeof(char));
665         if(p->sphere == NULL) {
666                 lock_release(&pres_htable[hash_code].lock);
667                 ret = -1;
668                 goto done;
669         }
670         strcpy(p->sphere, sphere);
671
672         lock_release(&pres_htable[hash_code].lock);
673
674         /* call for watchers status update */
675
676         if(presentity->event->get_rules_doc(
677                            &presentity->user, &presentity->domain, &xcap_doc)
678                         < 0) {
679                 LM_ERR("failed to retrieve xcap document\n");
680                 ret = -1;
681                 goto done;
682         }
683
684         update_watchers_status(pres_uri, presentity->event, xcap_doc);
685
686
687 done:
688
689         if(xcap_doc) {
690                 if(xcap_doc->s)
691                         pkg_free(xcap_doc->s);
692                 pkg_free(xcap_doc);
693         }
694
695         if(sphere)
696                 pkg_free(sphere);
697         return ret;
698 }