core, lib, modules: restructured source code tree
[sip-router] / src / lib / presence / notifier_domain.c
1 /* 
2  * Copyright (C) 2005 iptelorg GmbH
3  *
4  * This file is part of ser, a free SIP server.
5  *
6  * ser is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version
10  *
11  * For a license to use the ser software under conditions
12  * other than those described here, or to purchase support for this
13  * software, please contact iptel.org by e-mail at the following addresses:
14  *    info@iptel.org
15  *
16  * ser is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
24  */
25
26 #include <cds/memory.h>
27 #include <cds/logger.h>
28 #include <cds/sync.h>
29 #include <stdio.h>
30 #include <presence/notifier_domain.h>
31 #include <presence/notifier.h>
32 #include <presence/subscriber.h>
33 #include <cds/list.h>
34 #include <cds/cds.h>
35 #include <string.h>
36
37 /*#define lock_subscription_data(s) if (s->mutex) cds_mutex_lock(s->mutex);
38 #define unlock_subscription_data(s) if (s->mutex) cds_mutex_unlock(s->mutex);*/
39
40 static void lock_subscription_data(qsa_subscription_t *s)
41 {
42         /* is function due to debugging */
43         if (s->mutex) cds_mutex_lock(s->mutex);
44 }
45
46 static void unlock_subscription_data(qsa_subscription_t *s) {
47         /* is function due to debugging */
48         if (s->mutex) cds_mutex_unlock(s->mutex);
49 }
50
51 static void free_notifier(notifier_t *info);
52 static void free_subscription(qsa_subscription_t *s);
53
54 /* -------- package functions -------- */
55
56 static notifier_package_t *find_package(notifier_domain_t *d, const str_t *name)
57 {
58         notifier_package_t *p;
59
60         if (!d) return NULL;
61         p = d->first_package;
62         while (p) {
63                 if (str_case_equals(name, &p->name) == 0) return p;
64                 p = p->next;
65         }
66         return NULL;
67 }
68
69 static void add_package(notifier_domain_t *d, notifier_package_t *p)
70 {
71         p->domain = d;
72         DOUBLE_LINKED_LIST_ADD(d->first_package, d->last_package, p);
73 }
74
75 static notifier_package_t *create_package(const str_t *name)
76 {
77         notifier_package_t *p = (notifier_package_t*)cds_malloc(sizeof(notifier_package_t));
78         if (p) {
79                 p->first_subscription = NULL;
80                 p->last_subscription = NULL;
81                 p->first_notifier = NULL;
82                 p->last_notifier = NULL;
83                 p->next = NULL;
84                 p->prev = NULL;
85                 p->domain = NULL;
86                 if (str_dup(&p->name, name) < 0) {
87                         cds_free(p);
88                         ERROR_LOG("can't allocate memory\n");
89                         return NULL;
90                 }
91         }
92         return p;
93 }
94
95 /** finds existing package or adds new if not exists */
96 static notifier_package_t *get_package(notifier_domain_t *d, const str_t *name)
97 {
98         notifier_package_t *p = NULL;
99         
100         if (is_str_empty(name)) return NULL;
101         
102         p = find_package(d, name);
103         if (!p) {
104                 p = create_package(name);
105                 if (p) add_package(d, p);
106         }
107         return p;
108 }
109         
110 static void destroy_package(notifier_package_t *p) 
111 {
112         /* notifier_t *e, *n; */
113         qsa_subscription_t *s, *ns;
114         
115         /* release all subscriptions ???  */
116         s = p->first_subscription;
117         while (s) {
118                 ns = s->next;
119                 /* CAN NOT be called !!!! : unsubscribe(p->domain, s) */
120                 release_subscription(s);
121                 s = ns;
122         }
123         
124         /* !!! don't release notifiers - its their job !!! */
125         /* it may lead to errors there */
126         /* e = p->first_notifier;
127         while (e) {
128                 n = e->next;
129                 free_notifier(e);
130                 e = n;
131         } */
132         
133         p->first_notifier = NULL;
134         p->last_notifier = NULL;
135         str_free_content(&p->name);
136
137         cds_free(p);
138 }
139
140 /* -------- content type functions -------- */
141
142 static qsa_content_type_t *find_content_type(notifier_domain_t *d, const str_t *name)
143 {
144         qsa_content_type_t *p;
145
146         if (!d) return NULL;
147         p = d->first_content_type;
148         while (p) {
149                 if (str_case_equals(name, &p->name) == 0) return p;
150                 p = p->next;
151         }
152         return NULL;
153 }
154
155 static void add_content_type(notifier_domain_t *d, qsa_content_type_t *p)
156 {
157         DOUBLE_LINKED_LIST_ADD(d->first_content_type, d->last_content_type, p);
158 }
159
160 static qsa_content_type_t *create_content_type(const str_t *name, 
161                 destroy_function_f destroy_func)
162 {
163         qsa_content_type_t *p = (qsa_content_type_t*)cds_malloc(sizeof(qsa_content_type_t) + str_len(name));
164         if (p) {
165                 p->next = NULL;
166                 p->prev = NULL;
167                 p->name.s = p->buf;
168                 if (str_len(name) > 0) {
169                         memcpy(p->name.s, name->s, name->len);
170                         p->name.len = name->len;
171                 }
172                 else p->name.len = 0;
173                 p->destroy_func = destroy_func;
174         }
175         return p;
176 }
177
178 /** finds existing package or adds new if not exists */
179 qsa_content_type_t *register_content_type(notifier_domain_t *d, 
180                 const str_t *name,
181                 destroy_function_f destroy_func)
182 {
183         qsa_content_type_t *p = NULL;
184         
185         if (is_str_empty(name)) return NULL;
186         
187         p = find_content_type(d, name);
188         if (!p) {
189                 p = create_content_type(name, destroy_func);
190                 if (p) add_content_type(d, p);
191         }
192         return p;
193 }
194         
195 static void destroy_content_type(qsa_content_type_t *p) 
196 {
197         cds_free(p);
198 }
199
200 /* -------- Helper functions -------- */
201
202 static void free_notifier(notifier_t *info)
203 {
204         cds_free(info);
205 }
206
207 static void free_subscription(qsa_subscription_t *s)
208 {
209         DEBUG_LOG("freeing subscription to %p\n", s);
210         cds_free(s);
211 }
212
213 /*static void add_server_subscription(notifier_t *n, qsa_subscription_t *s)
214 {
215         server_subscription_t server_s;
216         
217         server_s.notifier_data = NULL;
218         if (n->subscribe(n, &s->record_id, s, &server_s.notifier_data) == 0) {
219                 server_s.notifier = n;
220                 vector_add(&s->server_subscriptions, &server_s);
221         }
222         else ERROR_LOG("subscription not accepted by notifier %p\n", n);
223 }
224                         
225 static void remove_notifier_from_subscription(qsa_subscription_t *s, notifier_t *n)
226 {
227         int cnt,i;
228
229         cnt = vector_size(&s->server_subscriptions);
230         for (i = 0; i < cnt; i++) {
231                 ss = vector_get_ptr(&s->server_subscriptions, i);
232                 if (!ss) continue;
233                 / * FIXME: call n->unsubsribe ??? 
234                  * NO this is called from unregister which is initiated
235                  * by the notifier (may be synchronized there!) * /
236                 if (ss->notifier == n) ss->notifier = NULL; / * "zombie" * /
237         }
238 }
239 */
240
241 /* -------- Domain initialization/destruction functions -------- */
242
243 /** Creates a new domain using cds memory functions. */
244 notifier_domain_t *create_notifier_domain(reference_counter_group_t *g, const str_t *name)
245 {
246         notifier_domain_t *d = (notifier_domain_t*)cds_malloc(sizeof(notifier_domain_t));
247         if (d) {
248                 d->first_package = NULL;
249                 d->last_package = NULL;
250                 d->first_content_type = NULL;
251                 d->last_content_type = NULL;
252                 d->rc_grp = g;
253                 if (str_dup(&d->name, name) < 0) {
254                         cds_free(d);
255                         ERROR_LOG("can't allocate memory\n");
256                         return NULL;
257                 }
258                 cds_mutex_init(&d->mutex);
259                 cds_mutex_init(&d->data_mutex);
260                 init_reference_counter(g, &d->ref);
261         }
262         return d;
263 }
264
265 /** Destroys domain and whole information stored in internal
266  * structures. If there are any subscribers, they are unsubscribed,
267  * if there are any notifiers, they are unregistered. */
268 void destroy_notifier_domain(notifier_domain_t *domain)
269 {
270         notifier_package_t *p, *n;
271         qsa_content_type_t *c, *tmp;
272
273         /* this function is always called only if no only one reference
274          * to domain exists (see domain maintainer), this should mean, that 
275          * all subscribers freed their subscriptions */
276         
277         lock_notifier_domain(domain);
278         
279         /* remove packages */
280         p = domain->first_package;
281         while (p) {
282                 n = p->next;
283                 destroy_package(p);
284                 p = n;
285         }
286         domain->first_package = NULL;
287         domain->last_package = NULL;
288         
289         c = domain->first_content_type;
290         while (c) {
291                 tmp = c;
292                 c = c->next;
293                 destroy_content_type(tmp);
294         }
295         domain->first_content_type = NULL;
296         domain->last_content_type = NULL;
297         
298         unlock_notifier_domain(domain);
299         
300         str_free_content(&domain->name);
301         cds_mutex_destroy(&domain->mutex);
302         cds_mutex_init(&domain->data_mutex);
303         cds_free(domain);
304 }
305
306 /* -------- Notifier functions -------- */
307
308 /* Returns the id of created notifier. Indicates error if less than 0 */
309 notifier_t *register_notifier(
310         notifier_domain_t *domain,
311         const str_t *package,
312         server_subscribe_func subscribe,
313         server_unsubscribe_func unsubscribe,
314         void *user_data)
315 {
316         notifier_t *info;
317         notifier_package_t *p;
318         qsa_subscription_t *s;
319
320         lock_notifier_domain(domain);
321         p = get_package(domain, package);
322         if (!p) {
323                 unlock_notifier_domain(domain);
324                 return NULL;
325         }
326                 
327         info = cds_malloc(sizeof(notifier_t));
328         if (!info) return info;
329
330         info->subscribe = subscribe;
331         info->unsubscribe = unsubscribe;
332         info->user_data = user_data;
333         info->package = p;
334         DEBUG_LOG("registered notifier for %.*s\n", FMT_STR(*package));
335
336         DOUBLE_LINKED_LIST_ADD(p->first_notifier, p->last_notifier, info);
337         
338         /* go through all subscriptions for package and 
339          * add them to this notifier */
340         s = p->first_subscription;
341         while (s) {
342                 info->subscribe(info, s);
343                 s = s->next;
344         }
345         
346         unlock_notifier_domain(domain);
347         
348         return info;
349 }
350
351 void unregister_notifier(notifier_domain_t *domain, notifier_t *info)
352 {
353         notifier_package_t *p;
354
355         if ((!info) || (!domain)) return;
356
357         /* maybe: test if the NOTIFIER is registered before unregistration */
358
359         lock_notifier_domain(domain);
360         
361         p = info->package;
362         if (p) {
363                 /* accepted subscriptions MUST be removed by the notifier 
364                  * how to solve this ? */
365                 
366                 /* qsa_subscription_t *s;
367                 s = p->first_subscription;
368                 while (s) {
369                         CAN NOT be called !!!!! info->unsubscribe(info, s);
370                         s = s->next;
371                 }*/
372
373                 DOUBLE_LINKED_LIST_REMOVE(p->first_notifier, p->last_notifier, info);
374                 /* DEBUG_LOG("UNregistered notifier for %.*s\n", FMT_STR(p->name)); */
375                 free_notifier(info);
376         }
377         unlock_notifier_domain(domain);
378 }
379
380 /* -------- Subscriber functions -------- */
381
382 /* If a notifier publishing watched state registeres after subscibe
383  * call, it receives the subscription automaticaly too! */
384 qsa_subscription_t *subscribe(notifier_domain_t *domain, 
385                 str_t *package,
386                 qsa_subscription_data_t *data)
387 {
388         qsa_subscription_t *s;
389         notifier_t *e;
390         notifier_package_t *p;
391         int cnt = 0;
392
393         lock_notifier_domain(domain);
394         p = get_package(domain, package);
395         if (!p) {
396                 ERROR_LOG("can't find/add package for subscription\n");
397                 unlock_notifier_domain(domain);
398                 return NULL;
399         }
400         
401         s = cds_malloc(sizeof(qsa_subscription_t));
402         if (!s) {
403                 ERROR_LOG("can't allocate memory\n");
404                 unlock_notifier_domain(domain);
405                 return s;
406         }
407
408         s->package = p;
409         s->mutex = &domain->data_mutex;
410         s->data = data;
411         s->allow_notifications = 1;
412         init_reference_counter(domain->rc_grp, &s->ref);
413
414         DOUBLE_LINKED_LIST_ADD(p->first_subscription, p->last_subscription, s);
415
416         /* add a reference for calling subscriber */
417         add_reference(&s->ref);
418         
419         /* browse all notifiers in given package and subscribe to them
420          * and add them to notifiers list */
421         cnt = 0;
422         e = p->first_notifier;
423         while (e) {
424                 cnt++;
425                 /* each notifier MUST add its own reference if
426                  * it wants to accept the subscription !!! */
427                 e->subscribe(e, s);
428                 e = e->next;
429         }
430         unlock_notifier_domain(domain);
431         DEBUG_LOG("subscribed to %d notifier(s)\n", cnt);
432         
433         return s;
434 }
435         
436 void release_subscription(qsa_subscription_t *s)
437 {
438         if (!s) return;
439         if (remove_reference(&s->ref)) free_subscription(s);
440 }
441
442 void accept_subscription(qsa_subscription_t *s)
443 {
444         if (!s) return;
445         add_reference(&s->ref);
446 }
447
448 /** Destroys an existing subscription - can be called ONLY by client !!! */
449 void unsubscribe(notifier_domain_t *domain, qsa_subscription_t *s)
450 {
451         notifier_package_t *p;
452         notifier_t *e;
453
454         /* mark subscription as un-notifyable */
455         lock_subscription_data(s);
456         s->allow_notifications = 0;
457         unlock_subscription_data(s);
458
459         lock_notifier_domain(domain);
460         
461         /* maybe: test if the SUBSCRIBER is subscribed before unsubsc. */
462         p = s->package;
463         if (!p) {
464                 unlock_notifier_domain(domain);
465                 return;
466         }
467
468         DOUBLE_LINKED_LIST_REMOVE(p->first_subscription, p->last_subscription, s);
469         
470         e = p->first_notifier;
471         while (e) {
472                 e->unsubscribe(e, s);
473                 e = e->next;
474         }
475         
476         unlock_notifier_domain(domain);
477         
478         /* mark subscription data as invalid */
479         lock_subscription_data(s);
480         s->data = NULL;
481         unlock_subscription_data(s);
482         
483         /* remove clients reference (dont give references to client?) */
484         remove_reference(&s->ref);
485         
486         release_subscription(s); 
487 }
488
489 /* void notify_subscriber(qsa_subscription_t *s, mq_message_t *msg) */
490 int notify_subscriber(qsa_subscription_t *s, 
491                 notifier_t *n,
492                 qsa_content_type_t *content_type, 
493                 void *data, 
494                 qsa_subscription_status_t status)
495 {
496         int ok = 1;
497         int sent = 0;
498         mq_message_t *msg = NULL;
499         client_notify_info_t* info = NULL;
500
501         if (!s) {
502                 ERROR_LOG("BUG: sending notify for <null> subscription\n");
503                 ok = 0;
504         }
505         
506         if (!content_type) {
507                 ERROR_LOG("BUG: content type not given! Possible memory leaks!\n");
508                 return -1;
509         }
510         
511         if (ok) {
512                 msg = create_message_ex(sizeof(client_notify_info_t));
513                 if (!msg) {
514                         ERROR_LOG("can't create notify message!\n");
515                         ok = 0; 
516                 }
517         }
518         
519         if (ok) {
520                 set_data_destroy_function(msg, (destroy_function_f)free_client_notify_info_content);
521                 info = (client_notify_info_t*)msg->data;
522                 
523                 info->subscription = s;
524                 info->content_type = content_type;
525                 info->data = data;
526                 info->status = status;
527                 
528                 lock_subscription_data(s);
529                 if ((s->allow_notifications) && (s->data)) {
530                         if (s->data->dst) {
531                                 if (push_message(s->data->dst, msg) < 0) ok = 0;
532                                 else sent = 1;
533                         }
534                 }
535                 unlock_subscription_data(s);
536         }
537         
538         if (!sent) {
539                 /* free unsent messages */
540                 if (msg) free_message(msg);
541                 else if (data) content_type->destroy_func(data);
542         }
543
544         if (ok) return 0;
545         else return 1; /* !!! Warning: data are destroyed !!! */
546 }
547
548 void free_client_notify_info_content(client_notify_info_t *info)
549 {
550         DEBUG_LOG(" ... freeing notify info content\n");
551 /*      str_free_content(&info->package);
552         str_free_content(&info->record_id);
553         str_free_content(&info->notifier); */
554         DEBUG_LOG(" ... calling destroy func on data\n");
555         if (info->content_type) {
556                 if (info->data) info->content_type->destroy_func(info->data);
557         }
558         else ERROR_LOG("BUG: content-type not given! Possible memory leaks!\n");
559 }
560
561 /* this can be called in notifier and the returned value is valid
562  * before finishes "unsubscribe" processing */
563 str_t *get_subscriber_id(qsa_subscription_t *s)
564 {
565         if (!s) return NULL;
566         if (!s->data) return NULL;
567         return &s->data->subscriber_id;
568 }
569
570 /* this can be called in notifier and the returned value is valid
571  * before finishes "unsubscribe" processing */
572 str_t *get_record_id(qsa_subscription_t *s)
573 {
574         if (!s) return NULL;
575         if (!s->data) return NULL;
576         return &s->data->record_id;
577 }
578
579 /* this can be called in notifier and the returned value is valid
580  * before finishes "unsubscribe" processing */
581 void *get_subscriber_data(qsa_subscription_t *s)
582 {
583         if (!s) return NULL;
584         if (!s->data) return NULL;
585         return s->data->subscriber_data;
586 }
587
588 void clear_subscription_data(qsa_subscription_data_t *data)
589 {
590         if (data) memset(data, 0, sizeof(*data));
591 }
592