2 * Copyright (C) 2005 iptelorg GmbH
4 * This file is part of ser, a free SIP server.
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
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:
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.
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
26 #include <cds/memory.h>
27 #include <cds/logger.h>
30 #include <presence/notifier_domain.h>
31 #include <presence/notifier.h>
32 #include <presence/subscriber.h>
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);*/
40 static void lock_subscription_data(qsa_subscription_t *s)
42 /* is function due to debugging */
43 if (s->mutex) cds_mutex_lock(s->mutex);
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);
51 static void free_notifier(notifier_t *info);
52 static void free_subscription(qsa_subscription_t *s);
54 /* -------- package functions -------- */
56 static notifier_package_t *find_package(notifier_domain_t *d, const str_t *name)
58 notifier_package_t *p;
63 if (str_case_equals(name, &p->name) == 0) return p;
69 static void add_package(notifier_domain_t *d, notifier_package_t *p)
72 DOUBLE_LINKED_LIST_ADD(d->first_package, d->last_package, p);
75 static notifier_package_t *create_package(const str_t *name)
77 notifier_package_t *p = (notifier_package_t*)cds_malloc(sizeof(notifier_package_t));
79 p->first_subscription = NULL;
80 p->last_subscription = NULL;
81 p->first_notifier = NULL;
82 p->last_notifier = NULL;
86 if (str_dup(&p->name, name) < 0) {
88 ERROR_LOG("can't allocate memory\n");
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)
98 notifier_package_t *p = NULL;
100 if (is_str_empty(name)) return NULL;
102 p = find_package(d, name);
104 p = create_package(name);
105 if (p) add_package(d, p);
110 static void destroy_package(notifier_package_t *p)
112 /* notifier_t *e, *n; */
113 qsa_subscription_t *s, *ns;
115 /* release all subscriptions ??? */
116 s = p->first_subscription;
119 /* CAN NOT be called !!!! : unsubscribe(p->domain, s) */
120 release_subscription(s);
124 /* !!! don't release notifiers - its their job !!! */
125 /* it may lead to errors there */
126 /* e = p->first_notifier;
133 p->first_notifier = NULL;
134 p->last_notifier = NULL;
135 str_free_content(&p->name);
140 /* -------- content type functions -------- */
142 static qsa_content_type_t *find_content_type(notifier_domain_t *d, const str_t *name)
144 qsa_content_type_t *p;
147 p = d->first_content_type;
149 if (str_case_equals(name, &p->name) == 0) return p;
155 static void add_content_type(notifier_domain_t *d, qsa_content_type_t *p)
157 DOUBLE_LINKED_LIST_ADD(d->first_content_type, d->last_content_type, p);
160 static qsa_content_type_t *create_content_type(const str_t *name,
161 destroy_function_f destroy_func)
163 qsa_content_type_t *p = (qsa_content_type_t*)cds_malloc(sizeof(qsa_content_type_t) + str_len(name));
168 if (str_len(name) > 0) {
169 memcpy(p->name.s, name->s, name->len);
170 p->name.len = name->len;
172 else p->name.len = 0;
173 p->destroy_func = destroy_func;
178 /** finds existing package or adds new if not exists */
179 qsa_content_type_t *register_content_type(notifier_domain_t *d,
181 destroy_function_f destroy_func)
183 qsa_content_type_t *p = NULL;
185 if (is_str_empty(name)) return NULL;
187 p = find_content_type(d, name);
189 p = create_content_type(name, destroy_func);
190 if (p) add_content_type(d, p);
195 static void destroy_content_type(qsa_content_type_t *p)
200 /* -------- Helper functions -------- */
202 static void free_notifier(notifier_t *info)
207 static void free_subscription(qsa_subscription_t *s)
209 DEBUG_LOG("freeing subscription to %p\n", s);
213 /*static void add_server_subscription(notifier_t *n, qsa_subscription_t *s)
215 server_subscription_t server_s;
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);
222 else ERROR_LOG("subscription not accepted by notifier %p\n", n);
225 static void remove_notifier_from_subscription(qsa_subscription_t *s, notifier_t *n)
229 cnt = vector_size(&s->server_subscriptions);
230 for (i = 0; i < cnt; i++) {
231 ss = vector_get_ptr(&s->server_subscriptions, i);
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" * /
241 /* -------- Domain initialization/destruction functions -------- */
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)
246 notifier_domain_t *d = (notifier_domain_t*)cds_malloc(sizeof(notifier_domain_t));
248 d->first_package = NULL;
249 d->last_package = NULL;
250 d->first_content_type = NULL;
251 d->last_content_type = NULL;
253 if (str_dup(&d->name, name) < 0) {
255 ERROR_LOG("can't allocate memory\n");
258 cds_mutex_init(&d->mutex);
259 cds_mutex_init(&d->data_mutex);
260 init_reference_counter(g, &d->ref);
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)
270 notifier_package_t *p, *n;
271 qsa_content_type_t *c, *tmp;
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 */
277 lock_notifier_domain(domain);
279 /* remove packages */
280 p = domain->first_package;
286 domain->first_package = NULL;
287 domain->last_package = NULL;
289 c = domain->first_content_type;
293 destroy_content_type(tmp);
295 domain->first_content_type = NULL;
296 domain->last_content_type = NULL;
298 unlock_notifier_domain(domain);
300 str_free_content(&domain->name);
301 cds_mutex_destroy(&domain->mutex);
302 cds_mutex_init(&domain->data_mutex);
306 /* -------- Notifier functions -------- */
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,
317 notifier_package_t *p;
318 qsa_subscription_t *s;
320 lock_notifier_domain(domain);
321 p = get_package(domain, package);
323 unlock_notifier_domain(domain);
327 info = cds_malloc(sizeof(notifier_t));
328 if (!info) return info;
330 info->subscribe = subscribe;
331 info->unsubscribe = unsubscribe;
332 info->user_data = user_data;
334 DEBUG_LOG("registered notifier for %.*s\n", FMT_STR(*package));
336 DOUBLE_LINKED_LIST_ADD(p->first_notifier, p->last_notifier, info);
338 /* go through all subscriptions for package and
339 * add them to this notifier */
340 s = p->first_subscription;
342 info->subscribe(info, s);
346 unlock_notifier_domain(domain);
351 void unregister_notifier(notifier_domain_t *domain, notifier_t *info)
353 notifier_package_t *p;
355 if ((!info) || (!domain)) return;
357 /* maybe: test if the NOTIFIER is registered before unregistration */
359 lock_notifier_domain(domain);
363 /* accepted subscriptions MUST be removed by the notifier
364 * how to solve this ? */
366 /* qsa_subscription_t *s;
367 s = p->first_subscription;
369 CAN NOT be called !!!!! info->unsubscribe(info, s);
373 DOUBLE_LINKED_LIST_REMOVE(p->first_notifier, p->last_notifier, info);
374 /* DEBUG_LOG("UNregistered notifier for %.*s\n", FMT_STR(p->name)); */
377 unlock_notifier_domain(domain);
380 /* -------- Subscriber functions -------- */
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,
386 qsa_subscription_data_t *data)
388 qsa_subscription_t *s;
390 notifier_package_t *p;
393 lock_notifier_domain(domain);
394 p = get_package(domain, package);
396 ERROR_LOG("can't find/add package for subscription\n");
397 unlock_notifier_domain(domain);
401 s = cds_malloc(sizeof(qsa_subscription_t));
403 ERROR_LOG("can't allocate memory\n");
404 unlock_notifier_domain(domain);
409 s->mutex = &domain->data_mutex;
411 s->allow_notifications = 1;
412 init_reference_counter(domain->rc_grp, &s->ref);
414 DOUBLE_LINKED_LIST_ADD(p->first_subscription, p->last_subscription, s);
416 /* add a reference for calling subscriber */
417 add_reference(&s->ref);
419 /* browse all notifiers in given package and subscribe to them
420 * and add them to notifiers list */
422 e = p->first_notifier;
425 /* each notifier MUST add its own reference if
426 * it wants to accept the subscription !!! */
430 unlock_notifier_domain(domain);
431 DEBUG_LOG("subscribed to %d notifier(s)\n", cnt);
436 void release_subscription(qsa_subscription_t *s)
439 if (remove_reference(&s->ref)) free_subscription(s);
442 void accept_subscription(qsa_subscription_t *s)
445 add_reference(&s->ref);
448 /** Destroys an existing subscription - can be called ONLY by client !!! */
449 void unsubscribe(notifier_domain_t *domain, qsa_subscription_t *s)
451 notifier_package_t *p;
454 /* mark subscription as un-notifyable */
455 lock_subscription_data(s);
456 s->allow_notifications = 0;
457 unlock_subscription_data(s);
459 lock_notifier_domain(domain);
461 /* maybe: test if the SUBSCRIBER is subscribed before unsubsc. */
464 unlock_notifier_domain(domain);
468 DOUBLE_LINKED_LIST_REMOVE(p->first_subscription, p->last_subscription, s);
470 e = p->first_notifier;
472 e->unsubscribe(e, s);
476 unlock_notifier_domain(domain);
478 /* mark subscription data as invalid */
479 lock_subscription_data(s);
481 unlock_subscription_data(s);
483 /* remove clients reference (dont give references to client?) */
484 remove_reference(&s->ref);
486 release_subscription(s);
489 /* void notify_subscriber(qsa_subscription_t *s, mq_message_t *msg) */
490 int notify_subscriber(qsa_subscription_t *s,
492 qsa_content_type_t *content_type,
494 qsa_subscription_status_t status)
498 mq_message_t *msg = NULL;
499 client_notify_info_t* info = NULL;
502 ERROR_LOG("BUG: sending notify for <null> subscription\n");
507 ERROR_LOG("BUG: content type not given! Possible memory leaks!\n");
512 msg = create_message_ex(sizeof(client_notify_info_t));
514 ERROR_LOG("can't create notify message!\n");
520 set_data_destroy_function(msg, (destroy_function_f)free_client_notify_info_content);
521 info = (client_notify_info_t*)msg->data;
523 info->subscription = s;
524 info->content_type = content_type;
526 info->status = status;
528 lock_subscription_data(s);
529 if ((s->allow_notifications) && (s->data)) {
531 if (push_message(s->data->dst, msg) < 0) ok = 0;
535 unlock_subscription_data(s);
539 /* free unsent messages */
540 if (msg) free_message(msg);
541 else if (data) content_type->destroy_func(data);
545 else return 1; /* !!! Warning: data are destroyed !!! */
548 void free_client_notify_info_content(client_notify_info_t *info)
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);
558 else ERROR_LOG("BUG: content-type not given! Possible memory leaks!\n");
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)
566 if (!s->data) return NULL;
567 return &s->data->subscriber_id;
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)
575 if (!s->data) return NULL;
576 return &s->data->record_id;
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)
584 if (!s->data) return NULL;
585 return s->data->subscriber_data;
588 void clear_subscription_data(qsa_subscription_data_t *data)
590 if (data) memset(data, 0, sizeof(*data));