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