presence: implemented more functions to work with in-memory presentity records
[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 /**
705  * ==============================
706  *  in-memory presentity records
707  * ==============================
708  */
709
710 static ps_ptable_t *_ps_ptable = NULL;
711
712 ps_ptable_t *ps_ptable_get(void)
713 {
714         return _ps_ptable;
715 }
716
717 #define PS_PRESENTITY_FIELD_COPY(field) do { \
718                 if (pt->field.s) { \
719                         ptn->field.s = p; \
720                         memcpy(ptn->field.s, pt->field.s, pt->field.len); \
721                 } \
722                 ptn->field.len = pt->field.len; \
723                 p += pt->field.len + 1; \
724         } while(0)
725
726 /**
727  *
728  */
729 ps_presentity_t *ps_presentity_new(ps_presentity_t *pt, int mtype)
730 {
731         uint32_t bsize = 0;
732         ps_presentity_t *ptn = NULL;
733         char *p = NULL;
734
735         if(pt==NULL) {
736                 return NULL;
737         }
738         bsize = sizeof(ps_presentity_t)
739                         + pt->user.len + 1
740                         + pt->domain.len + 1
741                         + pt->etag.len + 1
742                         + pt->event.len + 1
743                         + pt->ruid.len + 1
744                         + pt->sender.len + 1
745                         + pt->body.len + 1;
746         if(mtype==0) {
747                 ptn = (ps_presentity_t*)shm_malloc(bsize);
748         } else {
749                 ptn = (ps_presentity_t*)pkg_malloc(bsize);
750         }
751         if(ptn==NULL) {
752                 if(mtype==0) {
753                         SHM_MEM_ERROR;
754                 } else {
755                         PKG_MEM_ERROR;
756                 }
757                 return NULL;
758         }
759         memset(ptn, 0, bsize);
760
761         ptn->bsize = bsize;
762         ptn->hashid = core_case_hash(&pt->user, &pt->domain, 0);
763         ptn->expires = pt->expires;
764         ptn->received_time = pt->received_time;
765         ptn->priority = pt->priority;
766
767         p = (char*)ptn + sizeof(ps_presentity_t);
768         PS_PRESENTITY_FIELD_COPY(user);
769         PS_PRESENTITY_FIELD_COPY(domain);
770         PS_PRESENTITY_FIELD_COPY(etag);
771         PS_PRESENTITY_FIELD_COPY(event);
772         PS_PRESENTITY_FIELD_COPY(ruid);
773         PS_PRESENTITY_FIELD_COPY(sender);
774         PS_PRESENTITY_FIELD_COPY(body);
775
776         return ptn;
777 }
778
779 /**
780  *
781  */
782 void ps_presentity_free(ps_presentity_t *pt, int mtype)
783 {
784         if(pt==NULL) {
785                 return;
786         }
787         if(mtype==0) {
788                 shm_free(pt);
789         } else {
790                 pkg_free(pt);
791         }
792 }
793
794 /**
795  *
796  */
797 void ps_presentity_list_free(ps_presentity_t *pt, int mtype)
798 {
799         ps_presentity_t *ptc = NULL;
800         ps_presentity_t *ptn = NULL;
801
802         if(pt==NULL) {
803                 return;
804         }
805
806         ptn = pt;
807         while(ptn!=NULL) {
808                 ptc = ptn;
809                 ptn = ptn->next;
810                 ps_presentity_free(ptc, mtype);
811         }
812 }
813
814 #define PS_PRESENTITY_FIELD_SHIFT(field) do { \
815                 if (pt->field.s) { \
816                         ptn->field.s = p; \
817                 } \
818                 p += pt->field.len + 1; \
819         } while(0)
820
821 /**
822  *
823  */
824 ps_presentity_t *ps_presentity_dup(ps_presentity_t *pt, int mtype)
825 {
826         ps_presentity_t *ptn = NULL;
827         char *p = NULL;
828
829         if(pt==NULL) {
830                 return NULL;
831         }
832         if(mtype==0) {
833                 ptn = (ps_presentity_t*)shm_malloc(pt->bsize);
834         } else {
835                 ptn = (ps_presentity_t*)pkg_malloc(pt->bsize);
836         }
837         if(ptn==NULL) {
838                 if(mtype==0) {
839                         SHM_MEM_ERROR;
840                 } else {
841                         PKG_MEM_ERROR;
842                 }
843                 return NULL;
844         }
845
846         memcpy((void*)ptn, pt, pt->bsize);
847
848         p = (char*)ptn + sizeof(ps_presentity_t);
849         PS_PRESENTITY_FIELD_SHIFT(user);
850         PS_PRESENTITY_FIELD_SHIFT(domain);
851         PS_PRESENTITY_FIELD_SHIFT(etag);
852         PS_PRESENTITY_FIELD_SHIFT(event);
853         PS_PRESENTITY_FIELD_SHIFT(ruid);
854         PS_PRESENTITY_FIELD_SHIFT(sender);
855         PS_PRESENTITY_FIELD_SHIFT(body);
856
857         ptn->next = NULL;
858         ptn->prev = NULL;
859
860         return ptn;
861 }
862
863 /**
864  * match presentity with various conditions
865  *   0 - only user and domain
866  *   1 - match also event
867  *   2 - match also etag
868  */
869 int ps_presentity_match(ps_presentity_t *pta, ps_presentity_t *ptb, int mmode)
870 {
871         if(pta->hashid != ptb->hashid) {
872                 return 0;
873         }
874
875         if(pta->user.len != ptb->user.len || pta->domain.len != ptb->domain.len) {
876                 return 0;
877         }
878
879         if(mmode > 0) {
880                 if(pta->event.len != ptb->event.len) {
881                         return 0;
882                 }
883         }
884
885         if(mmode > 1) {
886                 if(pta->etag.len != ptb->etag.len) {
887                         return 0;
888                 }
889         }
890
891         if(strncmp(pta->user.s, ptb->user.s, pta->user.len)!=0) {
892                 return 0;
893         }
894
895         if(strncmp(pta->domain.s, ptb->domain.s, pta->domain.len)!=0) {
896                 return 0;
897         }
898
899         if(mmode > 0) {
900                 if(strncmp(pta->event.s, ptb->event.s, pta->event.len)!=0) {
901                         return 0;
902                 }
903         }
904
905         if(mmode > 1) {
906                 if(strncmp(pta->etag.s, ptb->etag.s, pta->etag.len)!=0) {
907                         return 0;
908                 }
909         }
910         return 1;
911 }
912
913 /**
914  *
915  */
916 int ps_ptable_init(int ssize)
917 {
918         size_t tsize = 0;
919         int i = 0;
920
921         if(_ps_ptable!=NULL) {
922                 return 0;
923         }
924         tsize = sizeof(ps_ptable_t) + (ssize * sizeof(ps_pslot_t));
925         _ps_ptable = (ps_ptable_t*)shm_malloc(tsize);
926         if(_ps_ptable==NULL) {
927                 SHM_MEM_ERROR;
928                 return -1;
929         }
930         memset(_ps_ptable, 0, tsize);
931         _ps_ptable->ssize = ssize;
932         _ps_ptable->slots = (ps_pslot_t*)((char*)_ps_ptable + sizeof(ps_ptable_t));
933         for(i=0; i<ssize; i++) {
934                 if(lock_init(&_ps_ptable->slots[i].lock) == 0) {
935                         LM_ERR("initializing lock on slot [%d]\n", i);
936                         goto error;
937                 }
938         }
939
940         return 0;
941
942 error:
943         i--;
944         while(i>=0) {
945                 lock_destroy(&_ps_ptable->slots[i].lock);
946                 i--;
947         }
948         shm_free(_ps_ptable);
949         _ps_ptable = NULL;
950         return -1;
951 }
952
953 /**
954  *
955  */
956 void ps_ptable_destroy(void)
957 {
958         int i = 0;
959         ps_presentity_t *pt = NULL;
960         ps_presentity_t *ptn = NULL;
961
962         if(_ps_ptable==NULL) {
963                 return;
964         }
965         for(i=0; i<_ps_ptable->ssize; i++) {
966                 lock_destroy(&_ps_ptable->slots[i].lock);
967                 pt = _ps_ptable->slots[i].plist;
968                 while(pt!=NULL) {
969                         ptn = pt->next;
970                         ps_presentity_free(pt, 0);
971                         pt = ptn;
972                 }
973         }
974         shm_free(_ps_ptable);
975         _ps_ptable = NULL;
976         return;
977 }
978
979 /**
980  *
981  */
982 int ps_ptable_insert(ps_presentity_t *pt)
983 {
984         ps_presentity_t ptc;
985         ps_presentity_t *ptn = NULL;
986         uint32_t idx = 0;
987
988         /* copy struct to fill in missing fields */
989         memcpy(&ptc, pt, sizeof(ps_presentity_t));
990
991         ptc.hashid = core_case_hash(&pt->user, &pt->domain, 0);
992
993         if(ptc.ruid.s == NULL) {
994                 if(sruid_next(&pres_sruid) < 0) {
995                         return -1;
996                 }
997                 ptc.ruid = pres_sruid.uid;
998         }
999
1000         ptn = ps_presentity_new(&ptc, 0);
1001         if(ptn==NULL) {
1002                 return -1;
1003         }
1004
1005         idx = core_hash_idx(ptn->hashid, _ps_ptable->ssize);
1006
1007         lock_get(&_ps_ptable->slots[idx].lock);
1008         if(_ps_ptable->slots[idx].plist == NULL) {
1009                 _ps_ptable->slots[idx].plist = ptn;
1010         } else {
1011                 _ps_ptable->slots[idx].plist->prev = ptn;
1012                 ptn->next = _ps_ptable->slots[idx].plist;
1013                 _ps_ptable->slots[idx].plist = ptn;
1014         }
1015         lock_release(&_ps_ptable->slots[idx].lock);
1016
1017         return 0;
1018 }
1019
1020 /**
1021  *
1022  */
1023 int ps_ptable_replace(ps_presentity_t *ptm, ps_presentity_t *pt)
1024 {
1025         ps_presentity_t ptc;
1026         ps_presentity_t ptv;
1027         ps_presentity_t *ptn = NULL;
1028         uint32_t idx = 0;
1029
1030         /* copy struct to fill in missing fields */
1031         memcpy(&ptc, ptm, sizeof(ps_presentity_t));
1032         memcpy(&ptv, pt, sizeof(ps_presentity_t));
1033
1034         ptc.hashid = core_case_hash(&pt->user, &pt->domain, 0);
1035         ptv.hashid = core_case_hash(&pt->user, &pt->domain, 0);
1036
1037         if(ptv.ruid.s == NULL) {
1038                 if(sruid_next(&pres_sruid) < 0) {
1039                         return -1;
1040                 }
1041                 ptv.ruid = pres_sruid.uid;
1042         }
1043
1044         idx = core_hash_idx(ptc.hashid, _ps_ptable->ssize);
1045
1046         lock_get(&_ps_ptable->slots[idx].lock);
1047         ptn = _ps_ptable->slots[idx].plist;
1048         while(ptn!=NULL) {
1049                 if(ps_presentity_match(ptn, &ptc, 2)==1) {
1050                         if(ptn->next) {
1051                                 ptn->next->prev = ptn->prev;
1052                         }
1053                         if(ptn->prev) {
1054                                 ptn->prev->next = ptn->next;
1055                         } else {
1056                                 _ps_ptable->slots[idx].plist = ptn->next;
1057                         }
1058                         break;
1059                 }
1060                 ptn = ptn->next;
1061         }
1062
1063         if(ptn!=NULL) {
1064                 ps_presentity_free(ptn, 0);
1065         }
1066
1067         ptn = ps_presentity_new(&ptv, 0);
1068         if(ptn==NULL) {
1069                 lock_release(&_ps_ptable->slots[idx].lock);
1070                 return -1;
1071         }
1072
1073         if(_ps_ptable->slots[idx].plist == NULL) {
1074                 _ps_ptable->slots[idx].plist = ptn;
1075         } else {
1076                 _ps_ptable->slots[idx].plist->prev = ptn;
1077                 ptn->next = _ps_ptable->slots[idx].plist;
1078                 _ps_ptable->slots[idx].plist = ptn;
1079         }
1080         lock_release(&_ps_ptable->slots[idx].lock);
1081
1082         return 0;
1083 }
1084
1085 /**
1086  *
1087  */
1088 int ps_ptable_update(ps_presentity_t *ptm, ps_presentity_t *pt)
1089 {
1090         ps_presentity_t ptc;
1091         ps_presentity_t ptv;
1092         ps_presentity_t *ptn = NULL;
1093         uint32_t idx = 0;
1094
1095         /* copy struct to fill in missing fields */
1096         memcpy(&ptc, ptm, sizeof(ps_presentity_t));
1097         memcpy(&ptv, pt, sizeof(ps_presentity_t));
1098
1099         ptc.hashid = core_case_hash(&ptm->user, &ptm->domain, 0);
1100         ptv.hashid = core_case_hash(&pt->user, &pt->domain, 0);
1101
1102         if(ptv.ruid.s == NULL) {
1103                 if(sruid_next(&pres_sruid) < 0) {
1104                         return -1;
1105                 }
1106                 ptv.ruid = pres_sruid.uid;
1107         }
1108
1109         idx = core_hash_idx(ptc.hashid, _ps_ptable->ssize);
1110
1111         lock_get(&_ps_ptable->slots[idx].lock);
1112         ptn = _ps_ptable->slots[idx].plist;
1113         while(ptn!=NULL) {
1114                 if(ps_presentity_match(ptn, &ptc, 2)==1) {
1115                         if(ptn->next) {
1116                                 ptn->next->prev = ptn->prev;
1117                         }
1118                         if(ptn->prev) {
1119                                 ptn->prev->next = ptn->next;
1120                         } else {
1121                                 _ps_ptable->slots[idx].plist = ptn->next;
1122                         }
1123                         break;
1124                 }
1125                 ptn = ptn->next;
1126         }
1127
1128         if(ptn == NULL) {
1129                 lock_release(&_ps_ptable->slots[idx].lock);
1130                 return 0; /* affected items */
1131         }
1132         ps_presentity_free(ptn, 0);
1133
1134         ptn = ps_presentity_new(&ptv, 0);
1135         if(ptn==NULL) {
1136                 lock_release(&_ps_ptable->slots[idx].lock);
1137                 return -1;
1138         }
1139
1140         if(_ps_ptable->slots[idx].plist == NULL) {
1141                 _ps_ptable->slots[idx].plist = ptn;
1142         } else {
1143                 _ps_ptable->slots[idx].plist->prev = ptn;
1144                 ptn->next = _ps_ptable->slots[idx].plist;
1145                 _ps_ptable->slots[idx].plist = ptn;
1146         }
1147         lock_release(&_ps_ptable->slots[idx].lock);
1148
1149         return 1; /* affected items */
1150 }
1151
1152 /**
1153  *
1154  */
1155 int ps_ptable_remove(ps_presentity_t *pt)
1156 {
1157         ps_presentity_t ptc;
1158         ps_presentity_t *ptn = NULL;
1159         uint32_t idx = 0;
1160
1161         /* copy struct to fill in missing fields */
1162         memcpy(&ptc, pt, sizeof(ps_presentity_t));
1163
1164         ptc.hashid = core_case_hash(&pt->user, &pt->domain, 0);
1165         idx = core_hash_idx(ptc.hashid, _ps_ptable->ssize);
1166
1167         lock_get(&_ps_ptable->slots[idx].lock);
1168         ptn = _ps_ptable->slots[idx].plist;
1169         while(ptn!=NULL) {
1170                 if(ps_presentity_match(ptn, &ptc, 2)==1) {
1171                         if(ptn->next) {
1172                                 ptn->next->prev = ptn->prev;
1173                         }
1174                         if(ptn->prev) {
1175                                 ptn->prev->next = ptn->next;
1176                         } else {
1177                                 _ps_ptable->slots[idx].plist = ptn->next;
1178                         }
1179                         break;
1180                 }
1181                 ptn = ptn->next;
1182         }
1183         lock_release(&_ps_ptable->slots[idx].lock);
1184
1185         if(ptn != NULL) {
1186                 ps_presentity_free(ptn, 0);
1187         }
1188         return 0;
1189 }
1190
1191 /**
1192  *
1193  */
1194 ps_presentity_t *ps_ptable_get_list(str *user, str *domain)
1195 {
1196         ps_presentity_t ptc;
1197         ps_presentity_t *ptn = NULL;
1198         ps_presentity_t *ptl = NULL;
1199         ps_presentity_t *ptd = NULL;
1200         ps_presentity_t *pte = NULL;
1201         uint32_t idx = 0;
1202
1203         memset(&ptc, 0, sizeof(ps_presentity_t));
1204
1205         ptc.user = *user;
1206         ptc.domain = *domain;
1207         ptc.hashid = core_case_hash(&ptc.user, &ptc.domain, 0);
1208         idx = core_hash_idx(ptc.hashid, _ps_ptable->ssize);
1209
1210         lock_get(&_ps_ptable->slots[idx].lock);
1211         ptn = _ps_ptable->slots[idx].plist;
1212         while(ptn!=NULL) {
1213                 if(ps_presentity_match(ptn, &ptc, 0)==1) {
1214                         ptd = ps_presentity_dup(ptn, 1);
1215                         if(ptd == NULL) {
1216                                 break;
1217                         }
1218                         if(pte==NULL) {
1219                                 ptl = ptd;
1220                         } else {
1221                                 pte->next = ptd;
1222                                 ptd->prev = pte;
1223                         }
1224                         pte = ptd;
1225                 }
1226                 ptn = ptn->next;
1227         }
1228         lock_release(&_ps_ptable->slots[idx].lock);
1229
1230         if(ptd==NULL && ptl != NULL) {
1231                 ps_presentity_list_free(ptl, 1);
1232                 return NULL;
1233         }
1234
1235         return ptl;
1236 }
1237
1238 /**
1239  *
1240  */
1241 ps_presentity_t *ps_ptable_search(ps_presentity_t *ptm, int mmode, int rmode)
1242 {
1243         ps_presentity_t *ptn = NULL;
1244         ps_presentity_t *ptl = NULL;
1245         ps_presentity_t *ptd = NULL;
1246         ps_presentity_t *pte = NULL;
1247         uint32_t idx = 0;
1248         int pmax = 0;
1249
1250         ptm->hashid = core_case_hash(&ptm->user, &ptm->domain, 0);
1251         idx = core_hash_idx(ptm->hashid, _ps_ptable->ssize);
1252
1253         lock_get(&_ps_ptable->slots[idx].lock);
1254         ptn = _ps_ptable->slots[idx].plist;
1255         while(ptn!=NULL) {
1256                 if((ps_presentity_match(ptn, ptm, mmode)==1)
1257                                 && (ptm->expires==0 || ptn->expires > ptm->expires)) {
1258                         ptd = ps_presentity_dup(ptn, 1);
1259                         if(ptd == NULL) {
1260                                 break;
1261                         }
1262                         if(pte==NULL) {
1263                                 ptl = ptd;
1264                         } else {
1265                                 pte->next = ptd;
1266                                 ptd->prev = pte;
1267                         }
1268                         pte = ptd;
1269                 }
1270                 ptn = ptn->next;
1271         }
1272         lock_release(&_ps_ptable->slots[idx].lock);
1273
1274         if(ptd==NULL && ptl != NULL) {
1275                 ps_presentity_list_free(ptl, 1);
1276                 return NULL;
1277         }
1278
1279         if(rmode==1) {
1280                 /* order list by priority */
1281                 pte = NULL;
1282                 while(ptl!=NULL) {
1283                         pmax = 0;
1284                         ptn = ptl;
1285                         ptd = ptl;
1286                         while(ptn!=NULL) {
1287                                 if(ptn->priority >= pmax) {
1288                                         pmax = ptn->priority;
1289                                         ptd = ptn;
1290                                 }
1291                                 ptn = ptn->next;
1292                         }
1293                         if(ptd == ptl) {
1294                                 ptl = ptl->next;
1295                                 if(ptl) {
1296                                         ptl->prev = NULL;
1297                                 }
1298                                 ptd->next = pte;
1299                                 pte->prev = ptd;
1300                                 pte = ptd;
1301                         } else {
1302                                 if(ptd->prev) {
1303                                         ptd->prev->next = ptd->next;
1304                                 }
1305                                 if(ptd->next) {
1306                                         ptd->next->prev = ptd->prev;
1307                                 }
1308                                 ptd->next = pte;
1309                                 ptd->prev = NULL;
1310                                 pte->prev = ptd;
1311                                 pte = ptd;
1312                         }
1313                 }
1314                 return pte;
1315         }
1316
1317         /* default ordered by received time */
1318         return ptl;
1319 }
1320
1321 /**
1322  *
1323  */
1324 ps_presentity_t *ps_ptable_get_item(str *user, str *domain, str *event, str *etag)
1325 {
1326         ps_presentity_t ptc;
1327         ps_presentity_t *ptn = NULL;
1328         ps_presentity_t *ptd = NULL;
1329         uint32_t idx = 0;
1330
1331         memset(&ptc, 0, sizeof(ps_presentity_t));
1332
1333         ptc.user = *user;
1334         ptc.domain = *domain;
1335         ptc.event = *event;
1336         ptc.etag = *etag;
1337         ptc.hashid = core_case_hash(&ptc.user, &ptc.domain, 0);
1338         idx = core_hash_idx(ptc.hashid, _ps_ptable->ssize);
1339
1340         lock_get(&_ps_ptable->slots[idx].lock);
1341         ptn = _ps_ptable->slots[idx].plist;
1342         while(ptn!=NULL) {
1343                 if(ps_presentity_match(ptn, &ptc, 2)==1) {
1344                         ptd = ps_presentity_dup(ptn, 1);
1345                         break;
1346                 }
1347                 ptn = ptn->next;
1348         }
1349         lock_release(&_ps_ptable->slots[idx].lock);
1350
1351         return ptd;
1352 }
1353
1354 /**
1355  *
1356  */
1357 ps_presentity_t *ps_ptable_get_expired(int eval)
1358 {
1359         ps_presentity_t *ptn = NULL;
1360         ps_presentity_t *ptl = NULL;
1361         ps_presentity_t *ptd = NULL;
1362         ps_presentity_t *pte = NULL;
1363         int i = 0;
1364
1365         if(_ps_ptable == NULL) {
1366                 return NULL;
1367         }
1368
1369         for(i=0; i<_ps_ptable->ssize; i++) {
1370                 lock_get(&_ps_ptable->slots[i].lock);
1371                 ptn = _ps_ptable->slots[i].plist;
1372                 while(ptn!=NULL) {
1373                         if(ptn->expires > 0 && ptn->expires <= eval) {
1374                                 ptd = ps_presentity_dup(ptn, 1);
1375                                 if(ptd == NULL) {
1376                                         break;
1377                                 }
1378                                 if(pte==NULL) {
1379                                         ptl = ptd;
1380                                 } else {
1381                                         pte->next = ptd;
1382                                         ptd->prev = pte;
1383                                 }
1384                                 pte = ptd;
1385                         }
1386                         ptn = ptn->next;
1387                 }
1388                 lock_release(&_ps_ptable->slots[i].lock);
1389         }
1390
1391         if(ptd==NULL && ptl != NULL) {
1392                 ps_presentity_list_free(ptl, 1);
1393                 return NULL;
1394         }
1395
1396         return ptl;
1397 }