presence: compute presentity table slot index using correct structure
[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 <time.h>
32 #include "../../core/mem/shm_mem.h"
33 #include "../../core/hashes.h"
34 #include "../../core/dprint.h"
35 #include "../../core/str.h"
36 #include "../pua/hash.h"
37 #include "presence.h"
38 #include "hash.h"
39 #include "notify.h"
40
41 /* matching mode when removing subscriptions from memory */
42 extern int pres_subs_remove_match;
43
44 /**
45  * create the subscription hash table in shared memory
46  * - hash_size: number of slots
47  */
48 shtable_t new_shtable(int hash_size)
49 {
50         shtable_t htable = NULL;
51         int i, j;
52
53         i = 0;
54         htable = (subs_entry_t *)shm_malloc(hash_size * sizeof(subs_entry_t));
55         if(htable == NULL) {
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                 if(lock_init(&htable[i].lock) == 0) {
61                         LM_ERR("initializing lock [%d]\n", i);
62                         goto error;
63                 }
64                 htable[i].entries = (subs_t *)shm_malloc(sizeof(subs_t));
65                 if(htable[i].entries == NULL) {
66                         lock_destroy(&htable[i].lock);
67                         ERR_MEM(SHARE_MEM);
68                 }
69                 memset(htable[i].entries, 0, sizeof(subs_t));
70                 htable[i].entries->next = NULL;
71         }
72
73         return htable;
74
75 error:
76         if(htable) {
77                 for(j = 0; j < i; j++) {
78                         lock_destroy(&htable[j].lock);
79                         shm_free(htable[j].entries);
80                 }
81                 shm_free(htable);
82         }
83         return NULL;
84 }
85
86 void destroy_shtable(shtable_t htable, int hash_size)
87 {
88         int i;
89
90         if(htable == NULL)
91                 return;
92
93         for(i = 0; i < hash_size; i++) {
94                 lock_destroy(&htable[i].lock);
95                 free_subs_list(htable[i].entries->next, SHM_MEM_TYPE, 1);
96                 shm_free(htable[i].entries);
97                 htable[i].entries = NULL;
98         }
99         shm_free(htable);
100         htable = NULL;
101 }
102
103 subs_t *search_shtable(shtable_t htable, str callid, str to_tag, str from_tag,
104                 unsigned int hash_code)
105 {
106         subs_t *s;
107
108         s = htable[hash_code].entries ? htable[hash_code].entries->next : NULL;
109
110         while(s) {
111                 if(s->callid.len == callid.len
112                                 && strncmp(s->callid.s, callid.s, callid.len) == 0
113                                 && s->to_tag.len == to_tag.len
114                                 && strncmp(s->to_tag.s, to_tag.s, to_tag.len) == 0
115                                 && s->from_tag.len == from_tag.len
116                                 && strncmp(s->from_tag.s, from_tag.s, from_tag.len) == 0)
117                         return s;
118                 s = s->next;
119         }
120
121         return NULL;
122 }
123
124 subs_t *mem_copy_subs(subs_t *s, int mem_type)
125 {
126         int size;
127         subs_t *dest;
128
129         size = sizeof(subs_t)
130                    + (s->pres_uri.len + s->to_user.len + s->to_domain.len
131                                          + s->from_user.len + s->from_domain.len + s->callid.len
132                                          + s->to_tag.len + s->from_tag.len + s->sockinfo_str.len
133                                          + s->event_id.len + s->local_contact.len + s->contact.len
134                                          + s->record_route.len + s->reason.len + s->watcher_user.len
135                                          + s->watcher_domain.len + s->user_agent.len + 1)
136                                          * sizeof(char);
137
138         if(mem_type & PKG_MEM_TYPE)
139                 dest = (subs_t *)pkg_malloc(size);
140         else
141                 dest = (subs_t *)shm_malloc(size);
142
143         if(dest == NULL) {
144                 ERR_MEM((mem_type == PKG_MEM_TYPE) ? PKG_MEM_STR : SHARE_MEM);
145         }
146         memset(dest, 0, size);
147         size = sizeof(subs_t);
148
149         CONT_COPY(dest, dest->pres_uri, s->pres_uri);
150         CONT_COPY(dest, dest->to_user, s->to_user);
151         CONT_COPY(dest, dest->to_domain, s->to_domain);
152         CONT_COPY(dest, dest->from_user, s->from_user);
153         CONT_COPY(dest, dest->from_domain, s->from_domain);
154         CONT_COPY(dest, dest->watcher_user, s->watcher_user);
155         CONT_COPY(dest, dest->watcher_domain, s->watcher_domain);
156         CONT_COPY(dest, dest->to_tag, s->to_tag);
157         CONT_COPY(dest, dest->from_tag, s->from_tag);
158         CONT_COPY(dest, dest->callid, s->callid);
159         CONT_COPY(dest, dest->sockinfo_str, s->sockinfo_str);
160         CONT_COPY(dest, dest->local_contact, s->local_contact);
161         CONT_COPY(dest, dest->contact, s->contact);
162         CONT_COPY(dest, dest->record_route, s->record_route);
163         CONT_COPY(dest, dest->user_agent, s->user_agent);
164         if(s->event_id.s)
165                 CONT_COPY(dest, dest->event_id, s->event_id);
166         if(s->reason.s)
167                 CONT_COPY(dest, dest->reason, s->reason);
168
169         dest->event = s->event;
170         dest->local_cseq = s->local_cseq;
171         dest->remote_cseq = s->remote_cseq;
172         dest->status = s->status;
173         dest->version = s->version;
174         dest->send_on_cback = s->send_on_cback;
175         dest->expires = s->expires;
176         dest->db_flag = s->db_flag;
177         dest->flags = s->flags;
178
179         return dest;
180
181 error:
182         return NULL;
183 }
184
185
186 subs_t *mem_copy_subs_noc(subs_t *s)
187 {
188         int size;
189         subs_t *dest;
190
191         size = sizeof(subs_t)
192                    + (s->pres_uri.len + s->to_user.len + s->to_domain.len
193                                          + s->from_user.len + s->from_domain.len + s->callid.len
194                                          + s->to_tag.len + s->from_tag.len + s->sockinfo_str.len
195                                          + s->event_id.len + s->local_contact.len + s->reason.len
196                                          + s->watcher_user.len + s->watcher_domain.len
197                                          + s->user_agent.len + 1)
198                                          * sizeof(char);
199
200         dest = (subs_t *)shm_malloc(size);
201         if(dest == NULL) {
202                 ERR_MEM(SHARE_MEM);
203         }
204         memset(dest, 0, size);
205         size = sizeof(subs_t);
206
207         CONT_COPY(dest, dest->pres_uri, s->pres_uri);
208         CONT_COPY(dest, dest->to_user, s->to_user);
209         CONT_COPY(dest, dest->to_domain, s->to_domain);
210         CONT_COPY(dest, dest->from_user, s->from_user);
211         CONT_COPY(dest, dest->from_domain, s->from_domain);
212         CONT_COPY(dest, dest->watcher_user, s->watcher_user);
213         CONT_COPY(dest, dest->watcher_domain, s->watcher_domain);
214         CONT_COPY(dest, dest->to_tag, s->to_tag);
215         CONT_COPY(dest, dest->from_tag, s->from_tag);
216         CONT_COPY(dest, dest->callid, s->callid);
217         CONT_COPY(dest, dest->sockinfo_str, s->sockinfo_str);
218         CONT_COPY(dest, dest->local_contact, s->local_contact);
219         CONT_COPY(dest, dest->user_agent, s->user_agent);
220         if(s->event_id.s)
221                 CONT_COPY(dest, dest->event_id, s->event_id);
222         if(s->reason.s)
223                 CONT_COPY(dest, dest->reason, s->reason);
224
225         dest->event = s->event;
226         dest->local_cseq = s->local_cseq;
227         dest->remote_cseq = s->remote_cseq;
228         dest->status = s->status;
229         dest->version = s->version;
230         dest->send_on_cback = s->send_on_cback;
231         dest->expires = s->expires;
232         dest->db_flag = s->db_flag;
233         dest->flags = s->flags;
234
235         dest->contact.s = (char *)shm_malloc(s->contact.len * sizeof(char));
236         if(dest->contact.s == NULL) {
237                 ERR_MEM(SHARE_MEM);
238         }
239         memcpy(dest->contact.s, s->contact.s, s->contact.len);
240         dest->contact.len = s->contact.len;
241
242         dest->record_route.s =
243                         (char *)shm_malloc((s->record_route.len + 1) * sizeof(char));
244         if(dest->record_route.s == NULL) {
245                 ERR_MEM(SHARE_MEM);
246         }
247         memcpy(dest->record_route.s, s->record_route.s, s->record_route.len);
248         dest->record_route.len = s->record_route.len;
249
250         return dest;
251
252 error:
253         if(dest)
254                 shm_free(dest);
255         return NULL;
256 }
257
258 int insert_shtable(shtable_t htable, unsigned int hash_code, subs_t *subs)
259 {
260         subs_t *new_rec = NULL;
261
262         if (pres_delete_same_subs) {
263                 subs_t* rec = NULL, *prev_rec = NULL;
264
265                 lock_get(&htable[hash_code].lock);
266                 /* search if there is another record with the same pres_uri & callid */
267                 rec = htable[hash_code].entries->next;
268                 while (rec) {
269                         if (subs->pres_uri.len == rec->pres_uri.len && subs->callid.len == rec->callid.len &&
270                                         memcmp(subs->pres_uri.s, rec->pres_uri.s, subs->pres_uri.len) == 0 &&
271                                         memcmp(subs->callid.s, rec->callid.s, subs->callid.len) == 0) {
272                                 LM_NOTICE("Found another record with the same pres_uri[%.*s] and callid[%.*s]\n",
273                                         subs->pres_uri.len, subs->pres_uri.s, subs->callid.len, subs->callid.s);
274                                 /* delete this record */
275
276                                 if (prev_rec) {
277                                         prev_rec->next = rec->next;
278                                 } else {
279                                         htable[hash_code].entries->next = rec->next;
280                                 }
281
282                                 if (pres_subs_dbmode != NO_DB) {
283                                         delete_db_subs(&rec->to_tag, &rec->from_tag, &rec->callid);
284                                 }
285
286                                 if (rec->contact.s!=NULL) {
287                                         shm_free(rec->contact.s);
288                                 }
289
290                                 shm_free(rec);
291                                 break;
292                         }
293                         prev_rec = rec;
294                         rec = rec->next;
295                 }
296                 lock_release(&htable[hash_code].lock);
297         }
298
299         new_rec = mem_copy_subs_noc(subs);
300         if(new_rec == NULL) {
301                 LM_ERR("copying in share memory a subs_t structure\n");
302                 return -1;
303         }
304         new_rec->expires += (int)time(NULL);
305
306         lock_get(&htable[hash_code].lock);
307         new_rec->next = htable[hash_code].entries->next;
308         htable[hash_code].entries->next = new_rec;
309         lock_release(&htable[hash_code].lock);
310
311         return 0;
312 }
313
314 int delete_shtable(shtable_t htable, unsigned int hash_code, subs_t *subs)
315 {
316         subs_t *s = NULL, *ps = NULL;
317         int found = -1;
318
319         lock_get(&htable[hash_code].lock);
320
321         ps = htable[hash_code].entries;
322         s = ps ? ps->next : NULL;
323
324         while(s) {
325                 if(pres_subs_remove_match == 0) {
326                         /* match on to-tag only (unique, local generated - faster) */
327                         if(s->to_tag.len == subs->to_tag.len
328                                         && strncmp(s->to_tag.s, subs->to_tag.s, subs->to_tag.len)
329                                                            == 0) {
330                                 found = 0;
331                         }
332                 } else {
333                         /* match on all dialog attributes (distributed systems) */
334                         if(s->callid.len == subs->callid.len
335                                         && s->to_tag.len == subs->to_tag.len
336                                         && s->from_tag.len == subs->from_tag.len
337                                         && strncmp(s->callid.s, subs->callid.s, subs->callid.len)
338                                                            == 0
339                                         && strncmp(s->to_tag.s, subs->to_tag.s, subs->to_tag.len)
340                                                            == 0
341                                         && strncmp(s->from_tag.s, subs->from_tag.s,
342                                                            subs->from_tag.len)
343                                                            == 0) {
344                                 found = 0;
345                         }
346                 }
347                 if(found == 0) {
348                         found = s->local_cseq + 1;
349                         ps->next = s->next;
350                         if(s->contact.s != NULL) {
351                                 shm_free(s->contact.s);
352                                 s->contact.s = NULL;
353                         }
354                         if(s->record_route.s != NULL) {
355                                 shm_free(s->record_route.s);
356                                 s->record_route.s = NULL;
357                         }
358                         if(s) {
359                                 shm_free(s);
360                                 s = NULL;
361                         }
362                         break;
363                 }
364                 ps = s;
365                 s = s->next;
366         }
367         lock_release(&htable[hash_code].lock);
368         return found;
369 }
370
371 void free_subs_list(subs_t *s_array, int mem_type, int ic)
372 {
373         subs_t *s;
374
375         while(s_array) {
376                 s = s_array;
377                 s_array = s_array->next;
378                 if(mem_type & PKG_MEM_TYPE) {
379                         if(ic) {
380                                 pkg_free(s->contact.s);
381                                 s->contact.s = NULL;
382                         }
383                         pkg_free(s);
384                         s = NULL;
385                 } else {
386                         if(ic) {
387                                 shm_free(s->contact.s);
388                                 s->contact.s = NULL;
389                         }
390                         shm_free(s);
391                         s = NULL;
392                 }
393         }
394 }
395
396 int update_shtable(
397                 shtable_t htable, unsigned int hash_code, subs_t *subs, int type)
398 {
399         subs_t *s;
400
401         lock_get(&htable[hash_code].lock);
402
403         s = search_shtable(
404                         htable, subs->callid, subs->to_tag, subs->from_tag, hash_code);
405         if(s == NULL) {
406                 LM_DBG("record not found in hash table\n");
407                 lock_release(&htable[hash_code].lock);
408                 return -1;
409         }
410
411         if(type & REMOTE_TYPE) {
412                 s->expires = subs->expires + (int)time(NULL);
413                 s->remote_cseq = subs->remote_cseq;
414         } else {
415                 subs->local_cseq = ++s->local_cseq;
416                 subs->version = ++s->version;
417         }
418
419         if(presence_sip_uri_match(&s->contact, &subs->contact)) {
420                 shm_free(s->contact.s);
421                 s->contact.s = (char *)shm_malloc(subs->contact.len * sizeof(char));
422                 if(s->contact.s == NULL) {
423                         lock_release(&htable[hash_code].lock);
424                         LM_ERR("no more shared memory\n");
425                         return -1;
426                 }
427                 memcpy(s->contact.s, subs->contact.s, subs->contact.len);
428                 s->contact.len = subs->contact.len;
429         }
430
431         shm_free(s->record_route.s);
432         s->record_route.s =
433                         (char *)shm_malloc(subs->record_route.len * sizeof(char));
434         if(s->record_route.s == NULL) {
435                 lock_release(&htable[hash_code].lock);
436                 LM_ERR("no more shared memory\n");
437                 return -1;
438         }
439         memcpy(s->record_route.s, subs->record_route.s, subs->record_route.len);
440         s->record_route.len = subs->record_route.len;
441
442         s->status = subs->status;
443         s->event = subs->event;
444         subs->db_flag = s->db_flag;
445
446         if(s->db_flag & NO_UPDATEDB_FLAG)
447                 s->db_flag = UPDATEDB_FLAG;
448
449         lock_release(&htable[hash_code].lock);
450
451         return 0;
452 }
453
454 phtable_t *new_phtable(void)
455 {
456         phtable_t *htable = NULL;
457         int i, j;
458
459         i = 0;
460         htable = (phtable_t *)shm_malloc(phtable_size * sizeof(phtable_t));
461         if(htable == NULL) {
462                 ERR_MEM(SHARE_MEM);
463         }
464         memset(htable, 0, phtable_size * sizeof(phtable_t));
465
466         for(i = 0; i < phtable_size; i++) {
467                 if(lock_init(&htable[i].lock) == 0) {
468                         LM_ERR("initializing lock [%d]\n", i);
469                         goto error;
470                 }
471                 htable[i].entries = (pres_entry_t *)shm_malloc(sizeof(pres_entry_t));
472                 if(htable[i].entries == NULL) {
473                         ERR_MEM(SHARE_MEM);
474                 }
475                 memset(htable[i].entries, 0, sizeof(pres_entry_t));
476                 htable[i].entries->next = NULL;
477         }
478
479         return htable;
480
481 error:
482         if(htable) {
483                 for(j = 0; j < i; j++) {
484                         if(htable[i].entries)
485                                 shm_free(htable[i].entries);
486                         else
487                                 break;
488                         lock_destroy(&htable[i].lock);
489                 }
490                 shm_free(htable);
491         }
492         return NULL;
493 }
494
495 void destroy_phtable(void)
496 {
497         int i;
498         pres_entry_t *p, *prev_p;
499
500         if(pres_htable == NULL)
501                 return;
502
503         for(i = 0; i < phtable_size; i++) {
504                 lock_destroy(&pres_htable[i].lock);
505                 p = pres_htable[i].entries;
506                 while(p) {
507                         prev_p = p;
508                         p = p->next;
509                         if(prev_p->sphere)
510                                 shm_free(prev_p->sphere);
511                         shm_free(prev_p);
512                 }
513         }
514         shm_free(pres_htable);
515 }
516 /* entry must be locked before calling this function */
517
518 pres_entry_t *search_phtable(str *pres_uri, int event, unsigned int hash_code)
519 {
520         pres_entry_t *p;
521
522         LM_DBG("pres_uri= %.*s\n", pres_uri->len, pres_uri->s);
523         p = pres_htable[hash_code].entries->next;
524         while(p) {
525                 if(p->event == event && p->pres_uri.len == pres_uri->len
526                                 && presence_sip_uri_match(&p->pres_uri, pres_uri) == 0)
527                         return p;
528                 p = p->next;
529         }
530         return NULL;
531 }
532
533 int insert_phtable(str *pres_uri, int event, char *sphere)
534 {
535         unsigned int hash_code;
536         pres_entry_t *p = NULL;
537         int size;
538
539         hash_code = core_case_hash(pres_uri, NULL, phtable_size);
540
541         lock_get(&pres_htable[hash_code].lock);
542
543         p = search_phtable(pres_uri, event, hash_code);
544         if(p) {
545                 p->publ_count++;
546                 lock_release(&pres_htable[hash_code].lock);
547                 return 0;
548         }
549         size = sizeof(pres_entry_t) + pres_uri->len * sizeof(char);
550
551         p = (pres_entry_t *)shm_malloc(size);
552         if(p == NULL) {
553                 lock_release(&pres_htable[hash_code].lock);
554                 ERR_MEM(SHARE_MEM);
555         }
556         memset(p, 0, size);
557
558         size = sizeof(pres_entry_t);
559         p->pres_uri.s = (char *)p + size;
560         memcpy(p->pres_uri.s, pres_uri->s, pres_uri->len);
561         p->pres_uri.len = pres_uri->len;
562
563         if(sphere) {
564                 p->sphere = (char *)shm_malloc((strlen(sphere) + 1) * sizeof(char));
565                 if(p->sphere == NULL) {
566                         lock_release(&pres_htable[hash_code].lock);
567                         shm_free(p);
568                         ERR_MEM(SHARE_MEM);
569                 }
570                 strcpy(p->sphere, sphere);
571         }
572
573         p->event = event;
574         p->publ_count = 1;
575
576         /* link the item in the hash table */
577         p->next = pres_htable[hash_code].entries->next;
578         pres_htable[hash_code].entries->next = p;
579
580         lock_release(&pres_htable[hash_code].lock);
581
582         return 0;
583
584 error:
585         return -1;
586 }
587
588 int delete_phtable(str *pres_uri, int event)
589 {
590         unsigned int hash_code;
591         pres_entry_t *p = NULL, *prev_p = NULL;
592
593         hash_code = core_case_hash(pres_uri, NULL, phtable_size);
594
595         lock_get(&pres_htable[hash_code].lock);
596
597         p = search_phtable(pres_uri, event, hash_code);
598         if(p == NULL) {
599                 LM_DBG("record not found\n");
600                 lock_release(&pres_htable[hash_code].lock);
601                 return 0;
602         }
603
604         p->publ_count--;
605         if(p->publ_count == 0) {
606                 /* delete record */
607                 prev_p = pres_htable[hash_code].entries;
608                 while(prev_p->next) {
609                         if(prev_p->next == p)
610                                 break;
611                         prev_p = prev_p->next;
612                 }
613                 if(prev_p->next == NULL) {
614                         LM_ERR("record not found\n");
615                         lock_release(&pres_htable[hash_code].lock);
616                         return -1;
617                 }
618                 prev_p->next = p->next;
619                 if(p->sphere)
620                         shm_free(p->sphere);
621
622                 shm_free(p);
623         }
624         lock_release(&pres_htable[hash_code].lock);
625
626         return 0;
627 }
628
629 int update_phtable(presentity_t *presentity, str *pres_uri, str *body)
630 {
631         char *sphere = NULL;
632         unsigned int hash_code;
633         pres_entry_t *p;
634         int ret = 0;
635         str *xcap_doc = NULL;
636
637         /* get new sphere */
638         sphere = extract_sphere(body);
639         if(sphere == NULL) {
640                 LM_DBG("no sphere defined in new body\n");
641                 return 0;
642         }
643
644         /* search for record in hash table */
645         hash_code = core_case_hash(pres_uri, NULL, phtable_size);
646
647         lock_get(&pres_htable[hash_code].lock);
648
649         p = search_phtable(pres_uri, presentity->event->evp->type, hash_code);
650         if(p == NULL) {
651                 lock_release(&pres_htable[hash_code].lock);
652                 goto done;
653         }
654
655         if(p->sphere) {
656                 if(strcmp(p->sphere, sphere) != 0) {
657                         /* new sphere definition */
658                         shm_free(p->sphere);
659                 } else {
660                         /* no change in sphere definition */
661                         lock_release(&pres_htable[hash_code].lock);
662                         pkg_free(sphere);
663                         return 0;
664                 }
665         }
666
667
668         p->sphere = (char *)shm_malloc((strlen(sphere) + 1) * sizeof(char));
669         if(p->sphere == NULL) {
670                 lock_release(&pres_htable[hash_code].lock);
671                 ret = -1;
672                 goto done;
673         }
674         strcpy(p->sphere, sphere);
675
676         lock_release(&pres_htable[hash_code].lock);
677
678         /* call for watchers status update */
679
680         if(presentity->event->get_rules_doc(
681                            &presentity->user, &presentity->domain, &xcap_doc)
682                         < 0) {
683                 LM_ERR("failed to retrieve xcap document\n");
684                 ret = -1;
685                 goto done;
686         }
687
688         update_watchers_status(pres_uri, presentity->event, xcap_doc);
689
690
691 done:
692
693         if(xcap_doc) {
694                 if(xcap_doc->s)
695                         pkg_free(xcap_doc->s);
696                 pkg_free(xcap_doc);
697         }
698
699         if(sphere)
700                 pkg_free(sphere);
701         return ret;
702 }
703
704 /* in-memory presentity records */
705
706 static ps_ptable_t *_ps_ptable = NULL;
707
708 ps_ptable_t *ps_ptable_get(void)
709 {
710         return _ps_ptable;
711 }
712
713 #define PS_PRESENTITY_FIELD_COPY(field) do { \
714                 if (pt->field.s) { \
715                         ptn->field.s = p; \
716                         memcpy(ptn->field.s, pt->field.s, pt->field.len); \
717                 } \
718                 ptn->field.len = pt->field.len; \
719                 p += pt->field.len + 1; \
720         } while(0)
721
722 /**
723  *
724  */
725 ps_presentity_t *ps_presentity_new(ps_presentity_t *pt, int mtype)
726 {
727         int bsize = 0;
728         ps_presentity_t *ptn = NULL;
729         char *p = NULL;
730
731         if(pt==NULL) {
732                 return NULL;
733         }
734         bsize = sizeof(ps_presentity_t)
735                         + pt->user.len + 1
736                         + pt->domain.len + 1
737                         + pt->etag.len + 1
738                         + pt->event.len + 1
739                         + pt->ruid.len + 1
740                         + pt->sender.len + 1
741                         + pt->body.len + 1;
742         if(mtype==0) {
743                 ptn = (ps_presentity_t*)shm_malloc(bsize);
744         } else {
745                 ptn = (ps_presentity_t*)pkg_malloc(bsize);
746         }
747         if(ptn==NULL) {
748                 if(mtype==0) {
749                         SHM_MEM_ERROR;
750                 } else {
751                         PKG_MEM_ERROR;
752                 }
753                 return NULL;
754         }
755         memset(ptn, 0, bsize);
756
757         ptn->bsize = bsize;
758         ptn->hashid = core_case_hash(&pt->user, &pt->domain, 0);
759         ptn->expires = pt->expires;
760         ptn->received_time = pt->received_time;
761         ptn->priority = pt->priority;
762
763         p = (char*)ptn + sizeof(ps_presentity_t);
764         PS_PRESENTITY_FIELD_COPY(user);
765         PS_PRESENTITY_FIELD_COPY(domain);
766         PS_PRESENTITY_FIELD_COPY(etag);
767         PS_PRESENTITY_FIELD_COPY(event);
768         PS_PRESENTITY_FIELD_COPY(ruid);
769         PS_PRESENTITY_FIELD_COPY(sender);
770         PS_PRESENTITY_FIELD_COPY(body);
771
772         return ptn;
773 }
774
775 /**
776  *
777  */
778 void ps_presentity_free(ps_presentity_t *pt, int mtype)
779 {
780         if(pt==NULL) {
781                 return;
782         }
783         if(mtype==0) {
784                 shm_free(pt);
785         } else {
786                 pkg_free(pt);
787         }
788 }
789
790 /**
791  *
792  */
793 void ps_presentity_list_free(ps_presentity_t *pt, int mtype)
794 {
795         ps_presentity_t *ptc = NULL;
796         ps_presentity_t *ptn = NULL;
797
798         if(pt==NULL) {
799                 return;
800         }
801
802         ptn = pt;
803         while(ptn!=NULL) {
804                 ptc = ptn;
805                 ptn = ptn->next;
806                 ps_presentity_free(ptc, mtype);
807         }
808 }
809
810 #define PS_PRESENTITY_FIELD_SHIFT(field) do { \
811                 if (pt->field.s) { \
812                         ptn->field.s = p; \
813                 } \
814                 p += pt->field.len + 1; \
815         } while(0)
816
817 /**
818  *
819  */
820 ps_presentity_t *ps_presentity_dup(ps_presentity_t *pt, int mtype)
821 {
822         ps_presentity_t *ptn = NULL;
823         char *p = NULL;
824
825         if(pt==NULL) {
826                 return NULL;
827         }
828         if(mtype==0) {
829                 ptn = (ps_presentity_t*)shm_malloc(pt->bsize);
830         } else {
831                 ptn = (ps_presentity_t*)pkg_malloc(pt->bsize);
832         }
833         if(ptn==NULL) {
834                 if(mtype==0) {
835                         SHM_MEM_ERROR;
836                 } else {
837                         PKG_MEM_ERROR;
838                 }
839                 return NULL;
840         }
841
842         memcpy((void*)ptn, pt, pt->bsize);
843
844         p = (char*)ptn + sizeof(ps_presentity_t);
845         PS_PRESENTITY_FIELD_SHIFT(user);
846         PS_PRESENTITY_FIELD_SHIFT(domain);
847         PS_PRESENTITY_FIELD_SHIFT(etag);
848         PS_PRESENTITY_FIELD_SHIFT(event);
849         PS_PRESENTITY_FIELD_SHIFT(ruid);
850         PS_PRESENTITY_FIELD_SHIFT(sender);
851         PS_PRESENTITY_FIELD_SHIFT(body);
852
853         ptn->next = NULL;
854         ptn->prev = NULL;
855
856         return ptn;
857 }
858
859 /**
860  *
861  */
862 int ps_presentity_match(ps_presentity_t *pta, ps_presentity_t *ptb, int mmode)
863 {
864         if(pta->hashid != ptb->hashid) {
865                 return 0;
866         }
867
868         if(pta->user.len != ptb->user.len || pta->domain.len != ptb->domain.len) {
869                 return 0;
870         }
871
872         if(mmode == 0) {
873                 if(pta->etag.len != ptb->etag.len || pta->event.len != ptb->event.len) {
874                         return 0;
875                 }
876         }
877
878         if(strncmp(pta->user.s, ptb->user.s, pta->user.len)!=0) {
879                 return 0;
880         }
881
882         if(strncmp(pta->domain.s, ptb->domain.s, pta->domain.len)!=0) {
883                 return 0;
884         }
885
886         if(mmode==0) {
887                 if(strncmp(pta->etag.s, ptb->etag.s, pta->etag.len)!=0) {
888                         return 0;
889                 }
890
891                 if(strncmp(pta->event.s, ptb->event.s, pta->event.len)!=0) {
892                         return 0;
893                 }
894         }
895
896         return 1;
897 }
898
899 /**
900  *
901  */
902 int ps_ptable_init(int ssize)
903 {
904         size_t tsize = 0;
905         int i = 0;
906
907         if(_ps_ptable!=NULL) {
908                 return 0;
909         }
910         tsize = sizeof(ps_ptable_t) + (ssize * sizeof(ps_pslot_t));
911         _ps_ptable = (ps_ptable_t*)shm_malloc(tsize);
912         if(_ps_ptable==NULL) {
913                 SHM_MEM_ERROR;
914                 return -1;
915         }
916         memset(_ps_ptable, 0, tsize);
917         _ps_ptable->ssize = ssize;
918         _ps_ptable->slots = (ps_pslot_t*)((char*)_ps_ptable + sizeof(ps_ptable_t));
919         for(i=0; i<ssize; i++) {
920                 if(lock_init(&_ps_ptable->slots[i].lock) == 0) {
921                         LM_ERR("initializing lock on slot [%d]\n", i);
922                         goto error;
923                 }
924         }
925
926         return 0;
927
928 error:
929         i--;
930         while(i>=0) {
931                 lock_destroy(&_ps_ptable->slots[i].lock);
932                 i--;
933         }
934         shm_free(_ps_ptable);
935         _ps_ptable = NULL;
936         return -1;
937 }
938
939 /**
940  *
941  */
942 void ps_ptable_destroy(void)
943 {
944         int i = 0;
945         ps_presentity_t *pt = NULL;
946         ps_presentity_t *ptn = NULL;
947
948         if(_ps_ptable==NULL) {
949                 return;
950         }
951         for(i=0; i<_ps_ptable->ssize; i++) {
952                 lock_destroy(&_ps_ptable->slots[i].lock);
953                 pt = _ps_ptable->slots[i].plist;
954                 while(pt!=NULL) {
955                         ptn = pt->next;
956                         ps_presentity_free(pt, 0);
957                         pt = ptn;
958                 }
959         }
960         shm_free(_ps_ptable);
961         _ps_ptable = NULL;
962         return;
963 }
964
965 /**
966  *
967  */
968 int ps_ptable_insert(ps_presentity_t *pt)
969 {
970         ps_presentity_t ptc;
971         ps_presentity_t *ptn = NULL;
972         int idx = 0;
973
974         /* copy struct to fill in missing fields */
975         memcpy(&ptc, pt, sizeof(ps_presentity_t));
976
977         ptc.hashid = core_case_hash(&pt->user, &pt->domain, 0);
978
979         if(ptc.ruid.s == NULL) {
980                 if(sruid_next(&pres_sruid) < 0) {
981                         return -1;
982                 }
983                 ptc.ruid = pres_sruid.uid;
984         }
985
986         ptn = ps_presentity_new(&ptc, 0);
987         if(ptn==NULL) {
988                 return -1;
989         }
990
991         idx = ptn->hashid % _ps_ptable->ssize;
992
993         lock_get(&_ps_ptable->slots[idx].lock);
994         if(_ps_ptable->slots[idx].plist == NULL) {
995                 _ps_ptable->slots[idx].plist = ptn;
996         } else {
997                 _ps_ptable->slots[idx].plist->prev = ptn;
998                 ptn->next = _ps_ptable->slots[idx].plist;
999                 _ps_ptable->slots[idx].plist = ptn;
1000         }
1001         lock_release(&_ps_ptable->slots[idx].lock);
1002
1003         return 0;
1004 }
1005
1006 /**
1007  *
1008  */
1009 int ps_ptable_replace(ps_presentity_t *pt)
1010 {
1011         ps_presentity_t ptc;
1012         ps_presentity_t *ptn = NULL;
1013         int idx = 0;
1014
1015         /* copy struct to fill in missing fields */
1016         memcpy(&ptc, pt, sizeof(ps_presentity_t));
1017
1018         ptc.hashid = core_case_hash(&pt->user, &pt->domain, 0);
1019
1020         if(ptc.ruid.s == NULL) {
1021                 if(sruid_next(&pres_sruid) < 0) {
1022                         return -1;
1023                 }
1024                 ptc.ruid = pres_sruid.uid;
1025         }
1026
1027         idx = ptc.hashid % _ps_ptable->ssize;
1028
1029         lock_get(&_ps_ptable->slots[idx].lock);
1030         ptn = _ps_ptable->slots[idx].plist;
1031         while(ptn!=NULL) {
1032                 if(ps_presentity_match(ptn, &ptc, 0)==1) {
1033                         if(ptn->next) {
1034                                 ptn->next->prev = ptn->prev;
1035                         }
1036                         if(ptn->prev) {
1037                                 ptn->prev->next = ptn->next;
1038                         } else {
1039                                 _ps_ptable->slots[idx].plist = ptn->next;
1040                         }
1041                         break;
1042                 }
1043                 ptn = ptn->next;
1044         }
1045
1046         ptn = ps_presentity_new(&ptc, 0);
1047         if(ptn==NULL) {
1048                 lock_release(&_ps_ptable->slots[idx].lock);
1049                 return -1;
1050         }
1051
1052         if(_ps_ptable->slots[idx].plist == NULL) {
1053                 _ps_ptable->slots[idx].plist = ptn;
1054         } else {
1055                 _ps_ptable->slots[idx].plist->prev = ptn;
1056                 ptn->next = _ps_ptable->slots[idx].plist;
1057                 _ps_ptable->slots[idx].plist = ptn;
1058         }
1059         lock_release(&_ps_ptable->slots[idx].lock);
1060
1061         return 0;
1062 }
1063
1064 /**
1065  *
1066  */
1067 int ps_ptable_update(ps_presentity_t *pt)
1068 {
1069         ps_presentity_t ptc;
1070         ps_presentity_t *ptn = NULL;
1071         int idx = 0;
1072
1073         /* copy struct to fill in missing fields */
1074         memcpy(&ptc, pt, sizeof(ps_presentity_t));
1075
1076         ptc.hashid = core_case_hash(&pt->user, &pt->domain, 0);
1077
1078         if(ptc.ruid.s == NULL) {
1079                 if(sruid_next(&pres_sruid) < 0) {
1080                         return -1;
1081                 }
1082                 ptc.ruid = pres_sruid.uid;
1083         }
1084
1085         idx = ptc.hashid % _ps_ptable->ssize;
1086
1087         lock_get(&_ps_ptable->slots[idx].lock);
1088         ptn = _ps_ptable->slots[idx].plist;
1089         while(ptn!=NULL) {
1090                 if(ps_presentity_match(ptn, &ptc, 0)==1) {
1091                         if(ptn->next) {
1092                                 ptn->next->prev = ptn->prev;
1093                         }
1094                         if(ptn->prev) {
1095                                 ptn->prev->next = ptn->next;
1096                         } else {
1097                                 _ps_ptable->slots[idx].plist = ptn->next;
1098                         }
1099                         break;
1100                 }
1101                 ptn = ptn->next;
1102         }
1103
1104         if(ptn == NULL) {
1105                 lock_release(&_ps_ptable->slots[idx].lock);
1106                 return 0; /* affected items */
1107         }
1108         ps_presentity_free(ptn, 0);
1109
1110         ptn = ps_presentity_new(&ptc, 0);
1111         if(ptn==NULL) {
1112                 lock_release(&_ps_ptable->slots[idx].lock);
1113                 return -1;
1114         }
1115
1116         if(_ps_ptable->slots[idx].plist == NULL) {
1117                 _ps_ptable->slots[idx].plist = ptn;
1118         } else {
1119                 _ps_ptable->slots[idx].plist->prev = ptn;
1120                 ptn->next = _ps_ptable->slots[idx].plist;
1121                 _ps_ptable->slots[idx].plist = ptn;
1122         }
1123         lock_release(&_ps_ptable->slots[idx].lock);
1124
1125         return 1; /* affected items */
1126 }
1127
1128 /**
1129  *
1130  */
1131 int ps_ptable_remove(ps_presentity_t *pt)
1132 {
1133         ps_presentity_t ptc;
1134         ps_presentity_t *ptn = NULL;
1135         int idx = 0;
1136
1137         /* copy struct to fill in missing fields */
1138         memcpy(&ptc, pt, sizeof(ps_presentity_t));
1139
1140         ptc.hashid = core_case_hash(&pt->user, &pt->domain, 0);
1141         idx = ptc.hashid % _ps_ptable->ssize;
1142
1143         lock_get(&_ps_ptable->slots[idx].lock);
1144         ptn = _ps_ptable->slots[idx].plist;
1145         while(ptn!=NULL) {
1146                 if(ps_presentity_match(ptn, &ptc, 0)==1) {
1147                         if(ptn->next) {
1148                                 ptn->next->prev = ptn->prev;
1149                         }
1150                         if(ptn->prev) {
1151                                 ptn->prev->next = ptn->next;
1152                         } else {
1153                                 _ps_ptable->slots[idx].plist = ptn->next;
1154                         }
1155                         break;
1156                 }
1157                 ptn = ptn->next;
1158         }
1159         lock_release(&_ps_ptable->slots[idx].lock);
1160
1161         if(ptn != NULL) {
1162                 ps_presentity_free(ptn, 0);
1163         }
1164         return 0;
1165 }
1166
1167 /**
1168  *
1169  */
1170 ps_presentity_t *ps_ptable_get_list(str *user, str *domain)
1171 {
1172         ps_presentity_t ptc;
1173         ps_presentity_t *ptn = NULL;
1174         ps_presentity_t *ptl = NULL;
1175         ps_presentity_t *ptd = NULL;
1176         ps_presentity_t *pte = NULL;
1177         int idx = 0;
1178
1179         memset(&ptc, 0, sizeof(ps_presentity_t));
1180
1181         ptc.user = *user;
1182         ptc.domain = *domain;
1183         ptc.hashid = core_case_hash(&ptc.user, &ptc.domain, 0);
1184         idx = ptc.hashid % _ps_ptable->ssize;
1185
1186         lock_get(&_ps_ptable->slots[idx].lock);
1187         ptn = _ps_ptable->slots[idx].plist;
1188         while(ptn!=NULL) {
1189                 if(ps_presentity_match(ptn, &ptc, 1)==1) {
1190                         ptd = ps_presentity_dup(ptn, 1);
1191                         if(ptd == NULL) {
1192                                 break;
1193                         }
1194                         if(pte==NULL) {
1195                                 ptl = ptd;
1196                         } else {
1197                                 pte->next = ptd;
1198                                 ptd->prev = pte;
1199                         }
1200                         pte = ptd;
1201                 }
1202                 ptn = ptn->next;
1203         }
1204         lock_release(&_ps_ptable->slots[idx].lock);
1205
1206         if(ptd==NULL && ptl != NULL) {
1207                 ps_presentity_list_free(ptl, 1);
1208                 return NULL;
1209         }
1210
1211         return ptl;
1212 }
1213
1214 /**
1215  *
1216  */
1217 ps_presentity_t *ps_ptable_get_item(str *user, str *domain, str *event, str *etag)
1218 {
1219         ps_presentity_t ptc;
1220         ps_presentity_t *ptn = NULL;
1221         ps_presentity_t *ptd = NULL;
1222         int idx = 0;
1223
1224         memset(&ptc, 0, sizeof(ps_presentity_t));
1225
1226         ptc.user = *user;
1227         ptc.domain = *domain;
1228         ptc.event = *event;
1229         ptc.etag = *etag;
1230         ptc.hashid = core_case_hash(&ptc.user, &ptc.domain, 0);
1231         idx = ptc.hashid % _ps_ptable->ssize;
1232
1233         lock_get(&_ps_ptable->slots[idx].lock);
1234         ptn = _ps_ptable->slots[idx].plist;
1235         while(ptn!=NULL) {
1236                 if(ps_presentity_match(ptn, &ptc, 0)==1) {
1237                         ptd = ps_presentity_dup(ptn, 1);
1238                         break;
1239                 }
1240                 ptn = ptn->next;
1241         }
1242         lock_release(&_ps_ptable->slots[idx].lock);
1243
1244         return ptd;
1245 }
1246
1247 /**
1248  *
1249  */
1250 ps_presentity_t *ps_ptable_get_expired(int eval)
1251 {
1252         ps_presentity_t *ptn = NULL;
1253         ps_presentity_t *ptl = NULL;
1254         ps_presentity_t *ptd = NULL;
1255         ps_presentity_t *pte = NULL;
1256         int i = 0;
1257
1258         for(i=0; i<_ps_ptable->ssize; i++) {
1259                 lock_get(&_ps_ptable->slots[i].lock);
1260                 ptn = _ps_ptable->slots[i].plist;
1261                 while(ptn!=NULL) {
1262                         if(ptn->expires > 0 && ptn->expires <= eval) {
1263                                 ptd = ps_presentity_dup(ptn, 1);
1264                                 if(ptd == NULL) {
1265                                         break;
1266                                 }
1267                                 if(pte==NULL) {
1268                                         ptl = ptd;
1269                                 } else {
1270                                         pte->next = ptd;
1271                                         ptd->prev = pte;
1272                                 }
1273                                 pte = ptd;
1274                         }
1275                         ptn = ptn->next;
1276                 }
1277                 lock_release(&_ps_ptable->slots[i].lock);
1278         }
1279
1280         if(ptd==NULL && ptl != NULL) {
1281                 ps_presentity_list_free(ptl, 1);
1282                 return NULL;
1283         }
1284
1285         return ptl;
1286 }