presence: pass str parameters via pointer to couple of functions
[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
281                                 if (pres_subs_dbmode != NO_DB) {
282                                         delete_db_subs(&rec->to_tag, &rec->from_tag, &rec->callid);
283                                 }
284
285                                 if (rec->contact.s!=NULL) {
286                                         shm_free(rec->contact.s);
287                                 }
288
289                                 shm_free(rec);
290                                 break;
291                         }
292                         prev_rec = rec;
293                         rec = rec->next;
294                 }
295                 lock_release(&htable[hash_code].lock);
296         }
297
298         new_rec = mem_copy_subs_noc(subs);
299         if(new_rec == NULL) {
300                 LM_ERR("copying in share memory a subs_t structure\n");
301                 return -1;
302         }
303         new_rec->expires += (int)time(NULL);
304
305         lock_get(&htable[hash_code].lock);
306         new_rec->next = htable[hash_code].entries->next;
307         htable[hash_code].entries->next = new_rec;
308         lock_release(&htable[hash_code].lock);
309
310         return 0;
311 }
312
313 int delete_shtable(shtable_t htable, unsigned int hash_code, subs_t *subs)
314 {
315         subs_t *s = NULL, *ps = NULL;
316         int found = -1;
317
318         lock_get(&htable[hash_code].lock);
319
320         ps = htable[hash_code].entries;
321         s = ps ? ps->next : NULL;
322
323         while(s) {
324                 if(pres_subs_remove_match == 0) {
325                         /* match on to-tag only (unique, local generated - faster) */
326                         if(s->to_tag.len == subs->to_tag.len
327                                         && strncmp(s->to_tag.s, subs->to_tag.s, subs->to_tag.len)
328                                                            == 0) {
329                                 found = 0;
330                         }
331                 } else {
332                         /* match on all dialog attributes (distributed systems) */
333                         if(s->callid.len == subs->callid.len
334                                         && s->to_tag.len == subs->to_tag.len
335                                         && s->from_tag.len == subs->from_tag.len
336                                         && strncmp(s->callid.s, subs->callid.s, subs->callid.len)
337                                                            == 0
338                                         && strncmp(s->to_tag.s, subs->to_tag.s, subs->to_tag.len)
339                                                            == 0
340                                         && strncmp(s->from_tag.s, subs->from_tag.s,
341                                                            subs->from_tag.len)
342                                                            == 0) {
343                                 found = 0;
344                         }
345                 }
346                 if(found == 0) {
347                         found = s->local_cseq + 1;
348                         ps->next = s->next;
349                         if(s->contact.s != NULL) {
350                                 shm_free(s->contact.s);
351                                 s->contact.s = NULL;
352                         }
353                         if(s->record_route.s != NULL) {
354                                 shm_free(s->record_route.s);
355                                 s->record_route.s = NULL;
356                         }
357                         if(s) {
358                                 shm_free(s);
359                                 s = NULL;
360                         }
361                         break;
362                 }
363                 ps = s;
364                 s = s->next;
365         }
366         lock_release(&htable[hash_code].lock);
367         return found;
368 }
369
370 void free_subs_list(subs_t *s_array, int mem_type, int ic)
371 {
372         subs_t *s;
373
374         while(s_array) {
375                 s = s_array;
376                 s_array = s_array->next;
377                 if(mem_type & PKG_MEM_TYPE) {
378                         if(ic) {
379                                 pkg_free(s->contact.s);
380                                 s->contact.s = NULL;
381                         }
382                         pkg_free(s);
383                         s = NULL;
384                 } else {
385                         if(ic) {
386                                 shm_free(s->contact.s);
387                                 s->contact.s = NULL;
388                         }
389                         shm_free(s);
390                         s = NULL;
391                 }
392         }
393 }
394
395 int update_shtable(
396                 shtable_t htable, unsigned int hash_code, subs_t *subs, int type)
397 {
398         subs_t *s;
399
400         lock_get(&htable[hash_code].lock);
401
402         s = search_shtable(
403                         htable, subs->callid, subs->to_tag, subs->from_tag, hash_code);
404         if(s == NULL) {
405                 LM_DBG("record not found in hash table\n");
406                 lock_release(&htable[hash_code].lock);
407                 return -1;
408         }
409
410         if(type & REMOTE_TYPE) {
411                 s->expires = subs->expires + (int)time(NULL);
412                 s->remote_cseq = subs->remote_cseq;
413         } else {
414                 subs->local_cseq = ++s->local_cseq;
415                 subs->version = ++s->version;
416         }
417
418         if(presence_sip_uri_match(&s->contact, &subs->contact)) {
419                 shm_free(s->contact.s);
420                 s->contact.s = (char *)shm_malloc(subs->contact.len * sizeof(char));
421                 if(s->contact.s == NULL) {
422                         lock_release(&htable[hash_code].lock);
423                         LM_ERR("no more shared memory\n");
424                         return -1;
425                 }
426                 memcpy(s->contact.s, subs->contact.s, subs->contact.len);
427                 s->contact.len = subs->contact.len;
428         }
429
430         shm_free(s->record_route.s);
431         s->record_route.s =
432                         (char *)shm_malloc(subs->record_route.len * sizeof(char));
433         if(s->record_route.s == NULL) {
434                 lock_release(&htable[hash_code].lock);
435                 LM_ERR("no more shared memory\n");
436                 return -1;
437         }
438         memcpy(s->record_route.s, subs->record_route.s, subs->record_route.len);
439         s->record_route.len = subs->record_route.len;
440
441         s->status = subs->status;
442         s->event = subs->event;
443         subs->db_flag = s->db_flag;
444
445         if(s->db_flag & NO_UPDATEDB_FLAG)
446                 s->db_flag = UPDATEDB_FLAG;
447
448         lock_release(&htable[hash_code].lock);
449
450         return 0;
451 }
452
453 phtable_t *new_phtable(void)
454 {
455         phtable_t *htable = NULL;
456         int i, j;
457
458         i = 0;
459         htable = (phtable_t *)shm_malloc(phtable_size * sizeof(phtable_t));
460         if(htable == NULL) {
461                 ERR_MEM(SHARE_MEM);
462         }
463         memset(htable, 0, phtable_size * sizeof(phtable_t));
464
465         for(i = 0; i < phtable_size; i++) {
466                 if(lock_init(&htable[i].lock) == 0) {
467                         LM_ERR("initializing lock [%d]\n", i);
468                         goto error;
469                 }
470                 htable[i].entries = (pres_entry_t *)shm_malloc(sizeof(pres_entry_t));
471                 if(htable[i].entries == NULL) {
472                         ERR_MEM(SHARE_MEM);
473                 }
474                 memset(htable[i].entries, 0, sizeof(pres_entry_t));
475                 htable[i].entries->next = NULL;
476         }
477
478         return htable;
479
480 error:
481         if(htable) {
482                 for(j = 0; j < i; j++) {
483                         if(htable[i].entries)
484                                 shm_free(htable[i].entries);
485                         else
486                                 break;
487                         lock_destroy(&htable[i].lock);
488                 }
489                 shm_free(htable);
490         }
491         return NULL;
492 }
493
494 void destroy_phtable(void)
495 {
496         int i;
497         pres_entry_t *p, *prev_p;
498
499         if(pres_htable == NULL)
500                 return;
501
502         for(i = 0; i < phtable_size; i++) {
503                 lock_destroy(&pres_htable[i].lock);
504                 p = pres_htable[i].entries;
505                 while(p) {
506                         prev_p = p;
507                         p = p->next;
508                         if(prev_p->sphere)
509                                 shm_free(prev_p->sphere);
510                         shm_free(prev_p);
511                 }
512         }
513         shm_free(pres_htable);
514 }
515 /* entry must be locked before calling this function */
516
517 pres_entry_t *search_phtable(str *pres_uri, int event, unsigned int hash_code)
518 {
519         pres_entry_t *p;
520
521         LM_DBG("pres_uri= %.*s\n", pres_uri->len, pres_uri->s);
522         p = pres_htable[hash_code].entries->next;
523         while(p) {
524                 if(p->event == event && p->pres_uri.len == pres_uri->len
525                                 && presence_sip_uri_match(&p->pres_uri, pres_uri) == 0)
526                         return p;
527                 p = p->next;
528         }
529         return NULL;
530 }
531
532 int insert_phtable(str *pres_uri, int event, char *sphere)
533 {
534         unsigned int hash_code;
535         pres_entry_t *p = NULL;
536         int size;
537
538         hash_code = core_case_hash(pres_uri, NULL, phtable_size);
539
540         lock_get(&pres_htable[hash_code].lock);
541
542         p = search_phtable(pres_uri, event, hash_code);
543         if(p) {
544                 p->publ_count++;
545                 lock_release(&pres_htable[hash_code].lock);
546                 return 0;
547         }
548         size = sizeof(pres_entry_t) + pres_uri->len * sizeof(char);
549
550         p = (pres_entry_t *)shm_malloc(size);
551         if(p == NULL) {
552                 lock_release(&pres_htable[hash_code].lock);
553                 ERR_MEM(SHARE_MEM);
554         }
555         memset(p, 0, size);
556
557         size = sizeof(pres_entry_t);
558         p->pres_uri.s = (char *)p + size;
559         memcpy(p->pres_uri.s, pres_uri->s, pres_uri->len);
560         p->pres_uri.len = pres_uri->len;
561
562         if(sphere) {
563                 p->sphere = (char *)shm_malloc((strlen(sphere) + 1) * sizeof(char));
564                 if(p->sphere == NULL) {
565                         lock_release(&pres_htable[hash_code].lock);
566                         shm_free(p);
567                         ERR_MEM(SHARE_MEM);
568                 }
569                 strcpy(p->sphere, sphere);
570         }
571
572         p->event = event;
573         p->publ_count = 1;
574
575         /* link the item in the hash table */
576         p->next = pres_htable[hash_code].entries->next;
577         pres_htable[hash_code].entries->next = p;
578
579         lock_release(&pres_htable[hash_code].lock);
580
581         return 0;
582
583 error:
584         return -1;
585 }
586
587 int delete_phtable(str *pres_uri, int event)
588 {
589         unsigned int hash_code;
590         pres_entry_t *p = NULL, *prev_p = NULL;
591
592         hash_code = core_case_hash(pres_uri, NULL, phtable_size);
593
594         lock_get(&pres_htable[hash_code].lock);
595
596         p = search_phtable(pres_uri, event, hash_code);
597         if(p == NULL) {
598                 LM_DBG("record not found\n");
599                 lock_release(&pres_htable[hash_code].lock);
600                 return 0;
601         }
602
603         p->publ_count--;
604         if(p->publ_count == 0) {
605                 /* delete record */
606                 prev_p = pres_htable[hash_code].entries;
607                 while(prev_p->next) {
608                         if(prev_p->next == p)
609                                 break;
610                         prev_p = prev_p->next;
611                 }
612                 if(prev_p->next == NULL) {
613                         LM_ERR("record not found\n");
614                         lock_release(&pres_htable[hash_code].lock);
615                         return -1;
616                 }
617                 prev_p->next = p->next;
618                 if(p->sphere)
619                         shm_free(p->sphere);
620
621                 shm_free(p);
622         }
623         lock_release(&pres_htable[hash_code].lock);
624
625         return 0;
626 }
627
628 int update_phtable(presentity_t *presentity, str *pres_uri, str *body)
629 {
630         char *sphere = NULL;
631         unsigned int hash_code;
632         pres_entry_t *p;
633         int ret = 0;
634         str *xcap_doc = NULL;
635
636         /* get new sphere */
637         sphere = extract_sphere(body);
638         if(sphere == NULL) {
639                 LM_DBG("no sphere defined in new body\n");
640                 return 0;
641         }
642
643         /* search for record in hash table */
644         hash_code = core_case_hash(pres_uri, NULL, phtable_size);
645
646         lock_get(&pres_htable[hash_code].lock);
647
648         p = search_phtable(pres_uri, presentity->event->evp->type, hash_code);
649         if(p == NULL) {
650                 lock_release(&pres_htable[hash_code].lock);
651                 goto done;
652         }
653
654         if(p->sphere) {
655                 if(strcmp(p->sphere, sphere) != 0) {
656                         /* new sphere definition */
657                         shm_free(p->sphere);
658                 } else {
659                         /* no change in sphere definition */
660                         lock_release(&pres_htable[hash_code].lock);
661                         pkg_free(sphere);
662                         return 0;
663                 }
664         }
665
666
667         p->sphere = (char *)shm_malloc((strlen(sphere) + 1) * sizeof(char));
668         if(p->sphere == NULL) {
669                 lock_release(&pres_htable[hash_code].lock);
670                 ret = -1;
671                 goto done;
672         }
673         strcpy(p->sphere, sphere);
674
675         lock_release(&pres_htable[hash_code].lock);
676
677         /* call for watchers status update */
678
679         if(presentity->event->get_rules_doc(
680                            &presentity->user, &presentity->domain, &xcap_doc)
681                         < 0) {
682                 LM_ERR("failed to retrieve xcap document\n");
683                 ret = -1;
684                 goto done;
685         }
686
687         update_watchers_status(pres_uri, presentity->event, xcap_doc);
688
689
690 done:
691
692         if(xcap_doc) {
693                 if(xcap_doc->s)
694                         pkg_free(xcap_doc->s);
695                 pkg_free(xcap_doc);
696         }
697
698         if(sphere)
699                 pkg_free(sphere);
700         return ret;
701 }