Asterisk - The Open Source Telephony Project GIT-master-a358458
res_pjsip_mwi.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2013, Digium, Inc.
5 *
6 * Mark Michelson <mmichelson@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*** MODULEINFO
20 <depend>pjproject</depend>
21 <depend>res_pjsip</depend>
22 <depend>res_pjsip_pubsub</depend>
23 <support_level>core</support_level>
24 ***/
25
26#include "asterisk.h"
27
28#include <pjsip.h>
29#include <pjsip_simple.h>
30#include <pjlib.h>
31
32#include "asterisk/res_pjsip.h"
35#include "asterisk/module.h"
36#include "asterisk/logger.h"
37#include "asterisk/astobj2.h"
39#include "asterisk/serializer.h"
40#include "asterisk/sorcery.h"
41#include "asterisk/stasis.h"
42#include "asterisk/mwi.h"
43
44struct mwi_subscription;
45
46AO2_GLOBAL_OBJ_STATIC(mwi_unsolicited);
47AO2_GLOBAL_OBJ_STATIC(mwi_solicited);
48
50
51#define STASIS_BUCKETS 13
52#define MWI_BUCKETS 53
53
54#define MWI_TYPE "application"
55#define MWI_SUBTYPE "simple-message-summary"
56
57#define MWI_DATASTORE "MWI datastore"
58
59/*! Number of serializers in pool if one not supplied. */
60#define MWI_SERIALIZER_POOL_SIZE 8
61
62/*! Max timeout for all threads to join during an unload. */
63#define MAX_UNLOAD_TIMEOUT_TIME 10 /* Seconds */
64
65/*! Pool of serializers to use if not supplied. */
67
69static void mwi_to_ami(struct ast_sip_subscription *sub, struct ast_str **buf);
70static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
71 const char *resource);
73static void *mwi_get_notify_data(struct ast_sip_subscription *sub);
74
77 .new_subscribe = mwi_new_subscribe,
78 .subscription_established = mwi_subscription_established,
79 .get_notify_data = mwi_get_notify_data,
80};
81
83 .event_name = "message-summary",
84 .body_type = AST_SIP_MESSAGE_ACCUMULATOR,
85 .accept = { MWI_TYPE"/"MWI_SUBTYPE, },
86 .subscription_shutdown = mwi_subscription_shutdown,
87 .to_ami = mwi_to_ami,
88 .notifier = &mwi_notifier,
89};
90
91/*!
92 * \brief Wrapper for stasis subscription
93 *
94 * An MWI subscription has a container of these. This
95 * represents a stasis subscription for MWI state.
96 */
98 /*! The MWI stasis subscription */
100 /*! The mailbox corresponding with the MWI subscription. Used as a hash key */
101 char mailbox[1];
102};
103
104/*!
105 * \brief A subscription for MWI
106 *
107 * This subscription is the basis for MWI for an endpoint. Each
108 * endpoint that uses MWI will have a corresponding mwi_subscription.
109 *
110 * This structure acts as the owner for the underlying SIP subscription.
111 * When the mwi_subscription is destroyed, the SIP subscription dies, too.
112 * The mwi_subscription's lifetime is governed by its underlying stasis
113 * subscriptions. When all stasis subscriptions are destroyed, the
114 * mwi_subscription is destroyed as well.
115 */
117 /*! Container of \ref mwi_stasis_subscription structures.
118 * A single MWI subscription may be for multiple mailboxes, thus
119 * requiring multiple stasis subscriptions
120 */
122 /*! The SIP subscription. Unsolicited MWI does not use this */
124 /*! AORs we should react to for unsolicited MWI NOTIFY */
125 char *aors;
126 /*! Is the MWI solicited (i.e. Initiated with an external SUBSCRIBE) ? */
127 unsigned int is_solicited;
128 /*! True if this subscription is to be terminated */
129 unsigned int terminate;
130 /*! Identifier for the subscription.
131 * The identifier is the same as the corresponding endpoint's stasis ID.
132 * Used as a hash key
133 */
134 char id[1];
135};
136
137static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub,
138 struct stasis_message *msg);
139
141{
142 struct mwi_stasis_subscription *mwi_stasis_sub;
143
144 if (!mwi_sub) {
145 return NULL;
146 }
147
148 mwi_stasis_sub = ao2_alloc(sizeof(*mwi_stasis_sub) + strlen(mailbox), NULL);
149 if (!mwi_stasis_sub) {
150 return NULL;
151 }
152
153 /* Safe strcpy */
154 strcpy(mwi_stasis_sub->mailbox, mailbox);
155
156 ao2_ref(mwi_sub, +1);
158 if (!mwi_stasis_sub->mwi_subscriber) {
159 /* Failed to subscribe. */
160 ao2_ref(mwi_stasis_sub, -1);
161 ao2_ref(mwi_sub, -1);
162 return NULL;
163 }
164
168
169 return mwi_stasis_sub;
170}
171
172static int stasis_sub_hash(const void *obj, const int flags)
173{
174 const struct mwi_stasis_subscription *object;
175 const char *key;
176
177 switch (flags & OBJ_SEARCH_MASK) {
178 case OBJ_SEARCH_KEY:
179 key = obj;
180 break;
182 object = obj;
183 key = object->mailbox;
184 break;
185 default:
186 ast_assert(0);
187 return 0;
188 }
189 return ast_str_hash(key);
190}
191
192static int stasis_sub_cmp(void *obj, void *arg, int flags)
193{
194 const struct mwi_stasis_subscription *sub_left = obj;
195 const struct mwi_stasis_subscription *sub_right = arg;
196 const char *right_key = arg;
197 int cmp;
198
199 switch (flags & OBJ_SEARCH_MASK) {
201 right_key = sub_right->mailbox;
202 /* Fall through */
203 case OBJ_SEARCH_KEY:
204 cmp = strcmp(sub_left->mailbox, right_key);
205 break;
207 cmp = strncmp(sub_left->mailbox, right_key, strlen(right_key));
208 break;
209 default:
210 cmp = 0;
211 break;
212 }
213 if (cmp) {
214 return 0;
215 }
216 return CMP_MATCH;
217}
218
219static void mwi_subscription_destructor(void *obj)
220{
221 struct mwi_subscription *sub = obj;
222
223 ast_debug(3, "Destroying MWI subscription for endpoint %s\n", sub->id);
224 if (sub->is_solicited) {
226 }
227 ao2_cleanup(sub->stasis_subs);
228 ast_free(sub->aors);
229}
230
232 unsigned int is_solicited, struct ast_sip_subscription *sip_sub)
233{
234 struct mwi_subscription *sub;
235 const char *endpoint_id = ast_sorcery_object_get_id(endpoint);
236
237 sub = ao2_alloc(sizeof(*sub) + strlen(endpoint_id),
239
240 if (!sub) {
241 return NULL;
242 }
243
244 /* Safe strcpy */
245 strcpy(sub->id, endpoint_id);
246
247 /* Unsolicited MWI doesn't actually result in a SIP subscription being
248 * created. This is because a SIP subscription associates with a dialog.
249 * Most devices expect unsolicited MWI NOTIFYs to appear out of dialog. If
250 * they receive an in-dialog MWI NOTIFY (i.e. with a to-tag), then they
251 * will reject the NOTIFY with a 481, thus resulting in message-waiting
252 * state not being updated on the device
253 */
254 if (is_solicited) {
255 sub->sip_sub = sip_sub;
256 }
257
260 if (!sub->stasis_subs) {
262 return NULL;
263 }
264 sub->is_solicited = is_solicited;
265
266 if (!is_solicited && !ast_strlen_zero(endpoint->aors)) {
267 sub->aors = ast_strdup(endpoint->aors);
268 if (!sub->aors) {
269 ao2_ref(sub, -1);
270 return NULL;
271 }
272 }
273
274 ast_debug(3, "Created %s MWI subscription for endpoint %s\n", is_solicited ? "solicited" : "unsolicited", sub->id);
275
276 return sub;
277}
278
279static int mwi_sub_hash(const void *obj, const int flags)
280{
281 const struct mwi_subscription *object;
282 const char *key;
283
284 switch (flags & OBJ_SEARCH_MASK) {
285 case OBJ_SEARCH_KEY:
286 key = obj;
287 break;
289 object = obj;
290 key = object->id;
291 break;
292 default:
293 ast_assert(0);
294 return 0;
295 }
296 return ast_str_hash(key);
297}
298
299static int mwi_sub_cmp(void *obj, void *arg, int flags)
300{
301 const struct mwi_subscription *sub_left = obj;
302 const struct mwi_subscription *sub_right = arg;
303 const char *right_key = arg;
304 int cmp;
305
306 switch (flags & OBJ_SEARCH_MASK) {
308 right_key = sub_right->id;
309 /* Fall through */
310 case OBJ_SEARCH_KEY:
311 cmp = strcmp(sub_left->id, right_key);
312 break;
314 cmp = strncmp(sub_left->id, right_key, strlen(right_key));
315 break;
316 default:
317 cmp = 0;
318 break;
319 }
320 if (cmp) {
321 return 0;
322 }
323 return CMP_MATCH;
324}
325
326static int get_message_count(void *obj, void *arg, int flags)
327{
328 struct mwi_stasis_subscription *mwi_stasis = obj;
329 struct ast_sip_message_accumulator *counter = arg;
330 struct ast_mwi_state *mwi_state;
331
332 mwi_state = ast_mwi_subscriber_data(mwi_stasis->mwi_subscriber);
333 if (!mwi_state) {
334 return 0;
335 }
336
337 counter->old_msgs += mwi_state->old_msgs;
338 counter->new_msgs += mwi_state->new_msgs;
339
340 ao2_ref(mwi_state, -1);
341
342 return 0;
343}
344
345static void set_voicemail_extension(pj_pool_t *pool, pjsip_sip_uri *local_uri,
346 struct ast_sip_message_accumulator *counter, const char *voicemail_extension)
347{
348 pjsip_sip_uri *account_uri;
349 const char *vm_exten;
350
351 if (ast_strlen_zero(voicemail_extension)) {
353 } else {
354 vm_exten = voicemail_extension;
355 }
356
357 if (!ast_strlen_zero(vm_exten)) {
358 account_uri = pjsip_uri_clone(pool, local_uri);
359 pj_strdup2(pool, &account_uri->user, vm_exten);
360 pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, account_uri, counter->message_account, sizeof(counter->message_account));
361 }
362}
363
367 pjsip_evsub_state state;
369};
370
371static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flags)
372{
373 struct unsolicited_mwi_data *mwi_data = arg;
374 struct mwi_subscription *sub = mwi_data->sub;
375 struct ast_sip_endpoint *endpoint = mwi_data->endpoint;
376 pjsip_evsub_state state = mwi_data->state;
377 struct ast_sip_contact *contact = obj;
378 const char *state_name;
379 pjsip_tx_data *tdata;
380 pjsip_sub_state_hdr *sub_state;
381 pjsip_event_hdr *event;
382 pjsip_from_hdr *from;
383 pjsip_sip_uri *from_uri;
384 const pjsip_hdr *allow_events = pjsip_evsub_get_allow_events_hdr(NULL);
385 struct ast_sip_body body;
386 struct ast_str *body_text;
388 .body_type = AST_SIP_MESSAGE_ACCUMULATOR,
389 .body_data = mwi_data->counter,
390 };
391
392 if (ast_sip_create_request("NOTIFY", NULL, endpoint, NULL, contact, &tdata)) {
393 ast_log(LOG_WARNING, "Unable to create unsolicited NOTIFY request to endpoint %s URI %s\n", sub->id, contact->uri);
394 return 0;
395 }
396
397 body.type = MWI_TYPE;
398 body.subtype = MWI_SUBTYPE;
399 body_text = ast_str_create(64);
400 if (!body_text) {
401 pjsip_tx_data_dec_ref(tdata);
402 return 0;
403 }
404
405 from = PJSIP_MSG_FROM_HDR(tdata->msg);
406 from_uri = pjsip_uri_get_uri(from->uri);
407
408 if (!ast_strlen_zero(endpoint->subscription.mwi.fromuser)) {
409 pj_strdup2(tdata->pool, &from_uri->user, endpoint->subscription.mwi.fromuser);
410 }
411
412 set_voicemail_extension(tdata->pool, from_uri, mwi_data->counter, endpoint->subscription.mwi.voicemail_extension);
413
414 if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, &body_data, &body_text)) {
415 ast_log(LOG_WARNING, "Unable to generate SIP MWI NOTIFY body.\n");
416 ast_free(body_text);
417 pjsip_tx_data_dec_ref(tdata);
418 return 0;
419 }
420
421 body.body_text = ast_str_buffer(body_text);
422
423 switch (state) {
424 case PJSIP_EVSUB_STATE_ACTIVE:
425 state_name = "active";
426 break;
427 case PJSIP_EVSUB_STATE_TERMINATED:
428 default:
429 state_name = "terminated";
430 break;
431 }
432
433 sub_state = pjsip_sub_state_hdr_create(tdata->pool);
434 pj_cstr(&sub_state->sub_state, state_name);
435 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) sub_state);
436
437 event = pjsip_event_hdr_create(tdata->pool);
438 pj_cstr(&event->event_type, "message-summary");
439 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) event);
440
441 pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, allow_events));
442 ast_sip_add_body(tdata, &body);
443 ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL);
444
445 ast_free(body_text);
446
447 return 0;
448}
449
450static struct ast_sip_aor *find_aor_for_resource(struct ast_sip_endpoint *endpoint, const char *resource)
451{
452 struct ast_sip_aor *aor;
453 char *aor_name;
454 char *aors_copy;
455
456 /* Direct match */
457 if ((aor = ast_sip_location_retrieve_aor(resource))) {
458 return aor;
459 }
460
461 if (!endpoint) {
462 return NULL;
463 }
464
465 /*
466 * This may be a subscribe to the voicemail_extension. If so,
467 * look for an aor belonging to this endpoint that has a matching
468 * voicemail_extension.
469 */
470 aors_copy = ast_strdupa(endpoint->aors);
471 while ((aor_name = ast_strip(strsep(&aors_copy, ",")))) {
472 struct ast_sip_aor *check_aor = ast_sip_location_retrieve_aor(aor_name);
473
474 if (!check_aor) {
475 continue;
476 }
477
478 if (!ast_strlen_zero(check_aor->voicemail_extension)
479 && !strcasecmp(check_aor->voicemail_extension, resource)) {
480 ast_debug(1, "Found an aor (%s) that matches voicemail_extension %s\n", aor_name, resource);
481 return check_aor;
482 }
483
484 ao2_ref(check_aor, -1);
485 }
486
487 return NULL;
488}
489
491 struct ast_sip_message_accumulator *counter)
492{
494 "endpoint", sub->id), ao2_cleanup);
495 char *endpoint_aors;
496 char *aor_name;
497
498 if (!endpoint) {
499 ast_log(LOG_WARNING, "Unable to send unsolicited MWI to %s because endpoint does not exist\n",
500 sub->id);
501 return;
502 }
503 if (ast_strlen_zero(endpoint->aors)) {
504 ast_log(LOG_WARNING, "Unable to send unsolicited MWI to %s because the endpoint has no"
505 " configured AORs\n", sub->id);
506 return;
507 }
508
509 endpoint_aors = ast_strdupa(endpoint->aors);
510
511 ast_debug(5, "Sending unsolicited MWI NOTIFY to endpoint %s, new messages: %d, old messages: %d\n",
512 sub->id, counter->new_msgs, counter->old_msgs);
513
514 while ((aor_name = ast_strip(strsep(&endpoint_aors, ",")))) {
516 RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
517 struct unsolicited_mwi_data mwi_data = {
518 .sub = sub,
519 .endpoint = endpoint,
520 .counter = counter,
521 };
522
523 if (!aor) {
524 ast_log(LOG_WARNING, "Unable to locate AOR %s for unsolicited MWI\n", aor_name);
525 continue;
526 }
527
529 if (!contacts || (ao2_container_count(contacts) == 0)) {
530 ast_debug(1, "No contacts bound to AOR %s. Cannot send unsolicited MWI until a contact registers.\n", aor_name);
531 continue;
532 }
533
535 }
536}
537
539{
540 struct ast_sip_message_accumulator counter = {
541 .old_msgs = 0,
542 .new_msgs = 0,
543 .message_account[0] = '\0',
544 };
545 struct ast_sip_body_data data = {
547 .body_data = &counter,
548 };
549
550 ao2_callback(sub->stasis_subs, OBJ_NODATA, get_message_count, &counter);
551
552 if (sub->is_solicited) {
553 const char *resource = ast_sip_subscription_get_resource_name(sub->sip_sub);
554 struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sub->sip_sub);
555 struct ast_sip_aor *aor = find_aor_for_resource(endpoint, resource);
556 pjsip_dialog *dlg = ast_sip_subscription_get_dialog(sub->sip_sub);
557 pjsip_sip_uri *sip_uri = ast_sip_subscription_get_sip_uri(sub->sip_sub);
558
559 if (aor && dlg && sip_uri) {
560 set_voicemail_extension(dlg->pool, sip_uri, &counter, aor->voicemail_extension);
561 }
562
563 ao2_cleanup(aor);
564 ao2_cleanup(endpoint);
565 ast_sip_subscription_notify(sub->sip_sub, &data, sub->terminate);
566
567 return;
568 }
569
571}
572
573static int unsubscribe_stasis(void *obj, void *arg, int flags)
574{
575 struct mwi_stasis_subscription *mwi_stasis = obj;
576
577 if (mwi_stasis->mwi_subscriber) {
578 ast_debug(3, "Removing stasis subscription to mailbox %s\n", mwi_stasis->mailbox);
580 }
581 return CMP_MATCH;
582}
583
585 int recreate, int send_now, struct ao2_container *unsolicited_mwi, struct ao2_container *solicited_mwi);
586
588{
590 struct ast_datastore *mwi_datastore;
591 struct ast_sip_endpoint *endpoint = NULL;
592 struct ao2_container *unsolicited_mwi;
593 struct ao2_container *solicited_mwi;
594
596 if (!mwi_datastore) {
597 return;
598 }
599
600 mwi_sub = mwi_datastore->data;
601
604 endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", mwi_sub->id);
605
606 ao2_ref(mwi_datastore, -1);
607
608 solicited_mwi = ao2_global_obj_ref(mwi_solicited);
609 if (solicited_mwi) {
610 ao2_unlink(solicited_mwi, mwi_sub);
611 }
612
613 /*
614 * When a solicited subscription is removed it's possible an unsolicited one
615 * needs to be [re-]created. Attempt to establish unsolicited MWI.
616 */
617 unsolicited_mwi = ao2_global_obj_ref(mwi_unsolicited);
618 if (unsolicited_mwi && endpoint) {
619 ao2_lock(unsolicited_mwi);
620 create_unsolicited_mwi_subscriptions(endpoint, 1, 1, unsolicited_mwi, solicited_mwi);
621 ao2_unlock(unsolicited_mwi);
622 ao2_ref(unsolicited_mwi, -1);
623 }
624
625 ao2_cleanup(solicited_mwi);
626 ao2_cleanup(endpoint);
627}
628
629static void mwi_ds_destroy(void *data)
630{
631 struct mwi_subscription *sub = data;
632
633 ao2_ref(sub, -1);
634}
635
638};
639
641{
642 struct ast_datastore *mwi_datastore;
643 int res;
644
646 if (!mwi_datastore) {
647 return -1;
648 }
649 ao2_ref(sub, +1);
650 mwi_datastore->data = sub;
651
652 /*
653 * NOTE: Adding the datastore to the subscription creates a ref loop
654 * that must be manually broken.
655 */
656 res = ast_sip_subscription_add_datastore(sub->sip_sub, mwi_datastore);
657 ao2_ref(mwi_datastore, -1);
658 return res;
659}
660
661/*!
662 * \internal
663 * \brief Determine if an MWI subscription already exists for the given endpoint/mailbox
664 *
665 * Search the given container, and attempt to find out if the given endpoint has a
666 * current subscription within. If so pass back the associated mwi_subscription and
667 * mwi_stasis_subscription objects.
668 *
669 * \note If a subscription is located then the caller is responsible for removing the
670 * references to the passed back mwi_subscription and mwi_stasis_subscription objects.
671 *
672 * \note Must be called with the given container already locked.
673 *
674 * \param container The ao2_container to search
675 * \param endpoint The endpoint to find
676 * \param mailbox The mailbox potentially subscribed
677 * \param[out] mwi_sub May contain the located mwi_subscription
678 * \param[out] mwi_stasis May contain the located mwi_stasis_subscription
679 *
680 * \retval 1 if a subscription was located, 0 otherwise
681 */
683 struct ast_sip_endpoint *endpoint, const char *mailbox,
684 struct mwi_subscription **mwi_sub, struct mwi_stasis_subscription **mwi_stasis)
685{
686 struct ao2_iterator *mwi_subs;
687
688 *mwi_sub = NULL;
689 *mwi_stasis = NULL;
690
691 if (!container) {
692 return 0;
693 }
694
695 mwi_subs = ao2_find(container, ast_sorcery_object_get_id(endpoint),
697 if (!mwi_subs) {
698 return 0;
699 }
700
701 while ((*mwi_sub = ao2_iterator_next(mwi_subs))) {
702 *mwi_stasis = ao2_find((*mwi_sub)->stasis_subs, mailbox, OBJ_SEARCH_KEY);
703 if (*mwi_stasis) {
704 /* If found then caller is responsible for unrefs of passed back objects */
705 break;
706 }
707 ao2_ref(*mwi_sub, -1);
708 }
709
710 ao2_iterator_destroy(mwi_subs);
711 return *mwi_stasis ? 1 : 0;
712}
713
714/*!
715 * \internal
716 * \brief Allow and/or replace the unsolicited subscription
717 *
718 * Checks to see if solicited subscription is allowed. If allowed, and an
719 * unsolicited one exists then prepare for replacement by removing the
720 * current unsolicited subscription.
721 *
722 * \param endpoint The endpoint
723 * \param mailbox The mailbox
724 * \param unsolicited_mwi A container of unsolicited mwi objects
725 *
726 * \retval 1 if a solicited subscription is allowed for the endpoint/mailbox
727 * 0 otherwise
728 */
729static int allow_and_or_replace_unsolicited(struct ast_sip_endpoint *endpoint, const char *mailbox,
730 struct ao2_container *unsolicited_mwi)
731{
733 struct mwi_stasis_subscription *mwi_stasis;
734
735 if (!has_mwi_subscription(unsolicited_mwi, endpoint, mailbox, &mwi_sub, &mwi_stasis)) {
736 /* If no unsolicited subscription then allow the solicited one */
737 return 1;
738 }
739
741 /* Has unsolicited subscription and can't replace, so disallow */
742 ao2_ref(mwi_stasis, -1);
743 ao2_ref(mwi_sub, -1);
744 return 0;
745 }
746
747 /*
748 * The unsolicited subscription exists, and it is allowed to be replaced.
749 * So, first remove the unsolicited stasis subscription, and if aggregation
750 * is not enabled then also remove the mwi_subscription object as well.
751 */
752 ast_debug(1, "Unsolicited subscription being replaced by solicited for "
753 "endpoint '%s' mailbox '%s'\n", ast_sorcery_object_get_id(endpoint), mailbox);
754
755 unsubscribe_stasis(mwi_stasis, NULL, 0);
756 ao2_unlink(mwi_sub->stasis_subs, mwi_stasis);
757
758 if (!endpoint->subscription.mwi.aggregate) {
759 ao2_unlink(unsolicited_mwi, mwi_sub);
760 }
761
762 ao2_ref(mwi_stasis, -1);
763 ao2_ref(mwi_sub, -1);
764
765 /* This solicited subscription is replacing an unsolicited one, so allow */
766 return 1;
767}
768
769static int send_notify(void *obj, void *arg, int flags);
770
771/*!
772 * \internal
773 * \brief Determine if an unsolicited MWI subscription is allowed
774 *
775 * \param endpoint The endpoint
776 * \param mailbox The mailbox
777 * \param unsolicited_mwi A container of unsolicited mwi objects
778 * \param solicited_mwi A container of solicited mwi objects
779 *
780 * \retval 1 if an unsolicited subscription is allowed for the endpoint/mailbox
781 * 0 otherwise
782 */
783static int is_unsolicited_allowed(struct ast_sip_endpoint *endpoint, const char *mailbox,
784 struct ao2_container *unsolicited_mwi, struct ao2_container *solicited_mwi)
785{
787 struct mwi_stasis_subscription *mwi_stasis;
788
790 return 0;
791 }
792
793 /*
794 * First check if an unsolicited subscription exists. If it does then we don't
795 * want to add another one.
796 */
797 if (has_mwi_subscription(unsolicited_mwi, endpoint, mailbox, &mwi_sub, &mwi_stasis)) {
798 ao2_ref(mwi_stasis, -1);
799 ao2_ref(mwi_sub, -1);
800 return 0;
801 }
802
803 /*
804 * If there is no unsolicited subscription, next check to see if a solicited
805 * subscription exists for the endpoint/mailbox. If not, then allow.
806 */
807 if (!has_mwi_subscription(solicited_mwi, endpoint, mailbox, &mwi_sub, &mwi_stasis)) {
808 return 1;
809 }
810
811 /*
812 * If however, a solicited subscription does exist then we'll need to see if that
813 * subscription is allowed to replace the unsolicited one. If is allowed to replace
814 * then disallow the unsolicited one.
815 */
817 ao2_ref(mwi_stasis, -1);
818 ao2_ref(mwi_sub, -1);
819 return 0;
820 }
821
822 /* Otherwise, shutdown the solicited subscription and allow the unsolicited */
823 mwi_sub->terminate = 1;
825
826 ao2_ref(mwi_stasis, -1);
827 ao2_ref(mwi_sub, -1);
828
829 return 1;
830}
831
832/*!
833 * \brief Determine if an endpoint is a candidate to be able to subscribe for MWI
834 *
835 * Currently, this just makes sure that the endpoint is not already receiving unsolicted
836 * MWI for any of an AOR's configured mailboxes.
837 *
838 * \param obj The AOR to which the endpoint is subscribing.
839 * \param arg The endpoint that is attempting to subscribe.
840 * \param flags Unused.
841 * \retval 0 Endpoint is a candidate to subscribe to MWI on the AOR.
842 * \retval -1 The endpoint cannot subscribe to MWI on the AOR.
843 */
844static int mwi_validate_for_aor(void *obj, void *arg, int flags)
845{
846 struct ast_sip_aor *aor = obj;
847 struct ast_sip_endpoint *endpoint = arg;
848 char *mailboxes;
849 char *mailbox;
850 struct ao2_container *unsolicited_mwi;
851
852 if (ast_strlen_zero(aor->mailboxes)) {
853 return 0;
854 }
855
856 /* A reload could be taking place so lock while checking if allowed */
857 unsolicited_mwi = ao2_global_obj_ref(mwi_unsolicited);
858 if (unsolicited_mwi) {
859 ao2_lock(unsolicited_mwi);
860 }
861
863 while ((mailbox = ast_strip(strsep(&mailboxes, ",")))) {
865 continue;
866 }
867
868 if (!allow_and_or_replace_unsolicited(endpoint, mailbox, unsolicited_mwi)) {
869 ast_debug(1, "Endpoint '%s' already configured for unsolicited MWI for mailbox '%s'. "
870 "Denying MWI subscription to %s\n", ast_sorcery_object_get_id(endpoint), mailbox,
872
873 if (unsolicited_mwi) {
874 ao2_unlock(unsolicited_mwi);
875 ao2_ref(unsolicited_mwi, -1);
876 }
877 return -1;
878 }
879 }
880
881 if (unsolicited_mwi) {
882 ao2_unlock(unsolicited_mwi);
883 ao2_ref(unsolicited_mwi, -1);
884 }
885
886 return 0;
887}
888
889static int mwi_on_aor(void *obj, void *arg, int flags)
890{
891 struct ast_sip_aor *aor = obj;
892 struct mwi_subscription *sub = arg;
893 char *mailboxes;
894 char *mailbox;
895
896 if (ast_strlen_zero(aor->mailboxes)) {
897 return 0;
898 }
899
901 while ((mailbox = ast_strip(strsep(&mailboxes, ",")))) {
902 struct mwi_stasis_subscription *mwi_stasis_sub;
903
905 continue;
906 }
907
908 mwi_stasis_sub = mwi_stasis_subscription_alloc(mailbox, sub);
909 if (!mwi_stasis_sub) {
910 continue;
911 }
912
913 ao2_link(sub->stasis_subs, mwi_stasis_sub);
914 ao2_ref(mwi_stasis_sub, -1);
915 }
916
917 return 0;
918}
919
921 struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub)
922{
923 struct mwi_subscription *sub = mwi_subscription_alloc(endpoint, 1, sip_sub);
924
925 if (!sub) {
926 return NULL;
927 }
928
929 if (add_mwi_datastore(sub)) {
930 ast_log(LOG_WARNING, "Unable to add datastore for MWI subscription to %s\n",
931 sub->id);
932 ao2_ref(sub, -1);
933 return NULL;
934 }
935
936 return sub;
937}
938
940 struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub, const char *name)
941{
942 struct ast_sip_aor *aor;
943 struct mwi_subscription *sub;
944
945 aor = find_aor_for_resource(endpoint, name);
946 if (!aor) {
947 ast_log(LOG_WARNING, "Unable to locate aor %s. MWI subscription failed.\n", name);
948 return NULL;
949 }
950
952 if (sub) {
953 mwi_on_aor(aor, sub, 0);
954 }
955
956 ao2_ref(aor, -1);
957 return sub;
958}
959
961 struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub)
962{
963 struct mwi_subscription *sub;
964
966 if (!sub) {
967 return NULL;
968 }
969
971 return sub;
972}
973
974static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
975 const char *resource)
976{
977 RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
978
979 if (ast_strlen_zero(resource)) {
980 if (ast_sip_for_each_aor(endpoint->aors, mwi_validate_for_aor, endpoint)) {
981 return 500;
982 }
983 return 200;
984 }
985
986 aor = find_aor_for_resource(endpoint, resource);
987 if (!aor) {
988 ast_debug(1, "Unable to locate aor %s. MWI subscription failed.\n", resource);
989 return 404;
990 }
991
992 if (ast_strlen_zero(aor->mailboxes)) {
993 ast_debug(1, "AOR %s has no configured mailboxes. MWI subscription failed.\n",
994 resource);
995 return 404;
996 }
997
998 if (mwi_validate_for_aor(aor, endpoint, 0)) {
999 return 500;
1000 }
1001
1002 return 200;
1003}
1004
1006{
1007 const char *resource = ast_sip_subscription_get_resource_name(sip_sub);
1008 struct mwi_subscription *sub;
1009 struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sip_sub);
1010 struct ao2_container *solicited_mwi;
1011
1012 /* no aor in uri? subscribe to all on endpoint */
1013 if (ast_strlen_zero(resource)) {
1014 sub = mwi_subscribe_all(endpoint, sip_sub);
1015 } else {
1016 sub = mwi_subscribe_single(endpoint, sip_sub, resource);
1017 }
1018 if (!sub) {
1019 ao2_cleanup(endpoint);
1020 return -1;
1021 }
1022
1023 if (!ao2_container_count(sub->stasis_subs)) {
1024 /*
1025 * We setup no MWI subscriptions so remove the MWI datastore
1026 * to break the ref loop.
1027 */
1029 }
1030
1031 solicited_mwi = ao2_global_obj_ref(mwi_solicited);
1032 if (solicited_mwi) {
1033 ao2_link(solicited_mwi, sub);
1034 ao2_ref(solicited_mwi, -1);
1035 }
1036
1038 ao2_cleanup(endpoint);
1039 return 0;
1040}
1041
1043{
1044 struct ast_sip_message_accumulator *counter;
1045 struct mwi_subscription *mwi_sub;
1046 struct ast_datastore *mwi_datastore;
1047 struct ast_sip_aor *aor;
1049
1051 if (!mwi_datastore) {
1052 return NULL;
1053 }
1054 mwi_sub = mwi_datastore->data;
1055
1056 counter = ao2_alloc(sizeof(*counter), NULL);
1057 if (!counter) {
1058 ao2_cleanup(mwi_datastore);
1059 return NULL;
1060 }
1061
1063 pjsip_dialog *dlg = ast_sip_subscription_get_dialog(sub);
1064 pjsip_sip_uri *sip_uri = ast_sip_subscription_get_sip_uri(sub);
1065
1066 if (dlg && sip_uri) {
1067 set_voicemail_extension(dlg->pool, sip_uri, counter, aor->voicemail_extension);
1068 }
1069 ao2_ref(aor, -1);
1070 }
1071 ao2_cleanup(endpoint);
1072
1073 ao2_callback(mwi_sub->stasis_subs, OBJ_NODATA, get_message_count, counter);
1074 ao2_cleanup(mwi_datastore);
1075 return counter;
1076}
1077
1078static void mwi_subscription_mailboxes_str(struct ao2_container *stasis_subs,
1079 struct ast_str **str)
1080{
1081 int is_first = 1;
1083 struct ao2_iterator i = ao2_iterator_init(stasis_subs, 0);
1084
1085 while ((node = ao2_iterator_next(&i))) {
1086 if (is_first) {
1087 is_first = 0;
1088 ast_str_append(str, 0, "%s", node->mailbox);
1089 } else {
1090 ast_str_append(str, 0, ",%s", node->mailbox);
1091 }
1092 ao2_ref(node, -1);
1093 }
1095}
1096
1098 struct ast_str **buf)
1099{
1100 struct mwi_subscription *mwi_sub;
1101 struct ast_datastore *mwi_datastore;
1102
1104 if (!mwi_datastore) {
1105 return;
1106 }
1107
1108 mwi_sub = mwi_datastore->data;
1109
1110 ast_str_append(buf, 0, "SubscriptionType: mwi\r\n");
1111 ast_str_append(buf, 0, "Mailboxes: ");
1113 ast_str_append(buf, 0, "\r\n");
1114
1115 ao2_ref(mwi_datastore, -1);
1116}
1117
1118static int serialized_notify(void *userdata)
1119{
1120 struct mwi_subscription *mwi_sub = userdata;
1121
1123 ao2_ref(mwi_sub, -1);
1124 return 0;
1125}
1126
1127static int serialized_cleanup(void *userdata)
1128{
1129 struct mwi_subscription *mwi_sub = userdata;
1130
1131 /* This is getting rid of the reference that was added
1132 * just before this serialized task was pushed.
1133 */
1135 /* This is getting rid of the reference held by the
1136 * stasis subscription
1137 */
1139 return 0;
1140}
1141
1142static int send_notify(void *obj, void *arg, int flags)
1143{
1144 struct mwi_subscription *mwi_sub = obj;
1145 struct ast_taskprocessor *serializer = mwi_sub->is_solicited
1148
1150 ao2_ref(mwi_sub, -1);
1151 }
1152
1153 return 0;
1154}
1155
1156static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub,
1157 struct stasis_message *msg)
1158{
1159 struct mwi_subscription *mwi_sub = userdata;
1160
1164 ao2_ref(mwi_sub, -1);
1165 }
1166 return;
1167 }
1168
1171 }
1172}
1173
1174/*!
1175 * \internal
1176 * \brief Create unsolicited MWI subscriptions for an endpoint
1177 *
1178 * \note Call with the unsolicited_mwi container lock held.
1179 *
1180 * \param endpoint An endpoint object
1181 * \param recreate Whether or not unsolicited subscriptions are potentially being recreated
1182 * \param send_now Whether or not to send a notify once the subscription is created
1183 * \param unsolicited_mwi A container of unsolicited mwi objects
1184 * \param solicited_mwi A container of solicited mwi objects
1185 *
1186 * \retval 0
1187 */
1189 int recreate, int send_now, struct ao2_container *unsolicited_mwi, struct ao2_container *solicited_mwi)
1190{
1191 RAII_VAR(struct mwi_subscription *, aggregate_sub, NULL, ao2_cleanup);
1192 char *mailboxes;
1193 char *mailbox;
1194 int sub_added = 0;
1195
1196 if (ast_strlen_zero(endpoint->subscription.mwi.mailboxes)) {
1197 return 0;
1198 }
1199
1200 if (endpoint->subscription.mwi.aggregate) {
1201 const char *endpoint_id = ast_sorcery_object_get_id(endpoint);
1202
1203 /* Check if aggregate subscription exists */
1204 aggregate_sub = ao2_find(unsolicited_mwi, endpoint_id, OBJ_SEARCH_KEY | OBJ_NOLOCK);
1205
1206 /*
1207 * If enabled there should only ever exist a single aggregate subscription object
1208 * for an endpoint. So if it exists just return unless subscriptions are potentially
1209 * being added back in. If that's the case then continue.
1210 */
1211 if (aggregate_sub && !recreate) {
1212 return 0;
1213 }
1214
1215 if (!aggregate_sub) {
1216 aggregate_sub = mwi_subscription_alloc(endpoint, 0, NULL);
1217 if (!aggregate_sub) {
1218 return 0; /* No MWI aggregation for you */
1219 }
1220
1221 /*
1222 * Just in case we somehow get in the position of recreating with no previous
1223 * aggregate object, set recreate to false here in order to allow the new
1224 * object to be linked into the container below
1225 */
1226 recreate = 0;
1227 }
1228 }
1229
1230 /* Lock solicited so we don't potentially add to both containers */
1231 if (solicited_mwi) {
1232 ao2_lock(solicited_mwi);
1233 }
1234
1236 while ((mailbox = ast_strip(strsep(&mailboxes, ",")))) {
1237 struct mwi_subscription *sub;
1238 struct mwi_stasis_subscription *mwi_stasis_sub;
1239
1240 if (!is_unsolicited_allowed(endpoint, mailbox, unsolicited_mwi, solicited_mwi)) {
1241 continue;
1242 }
1243
1244 sub = aggregate_sub ?: mwi_subscription_alloc(endpoint, 0, NULL);
1245 if (!sub) {
1246 continue;
1247 }
1248
1249 mwi_stasis_sub = mwi_stasis_subscription_alloc(mailbox, sub);
1250 if (mwi_stasis_sub) {
1251 ao2_link(sub->stasis_subs, mwi_stasis_sub);
1252 ao2_ref(mwi_stasis_sub, -1);
1253 }
1254 if (!aggregate_sub) {
1255 ao2_link_flags(unsolicited_mwi, sub, OBJ_NOLOCK);
1256 if (send_now) {
1257 send_notify(sub, NULL, 0);
1258 }
1259 ao2_ref(sub, -1);
1260 }
1261
1262 if (aggregate_sub && !sub_added) {
1263 /* If aggregation track if at least one subscription has been added */
1264 sub_added = 1;
1265 }
1266 }
1267
1268 if (aggregate_sub) {
1269 if (ao2_container_count(aggregate_sub->stasis_subs)) {
1270 /* Only link if we're dealing with a new aggregate object */
1271 if (!recreate) {
1272 ao2_link_flags(unsolicited_mwi, aggregate_sub, OBJ_NOLOCK);
1273 }
1274 if (send_now && sub_added) {
1275 send_notify(aggregate_sub, NULL, 0);
1276 }
1277 }
1278 }
1279
1280 if (solicited_mwi) {
1281 ao2_unlock(solicited_mwi);
1282 }
1283
1284 return 0;
1285}
1286
1287static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, void *data, int flags)
1288{
1289 return create_unsolicited_mwi_subscriptions(obj, 0, 0, arg, data);
1290}
1291
1292static int unsubscribe(void *obj, void *arg, int flags)
1293{
1294 struct mwi_subscription *mwi_sub = obj;
1295
1297
1298 return CMP_MATCH;
1299}
1300
1302{
1303 struct ao2_container *unsolicited_mwi;
1304 struct ao2_container *solicited_mwi;
1305 struct ao2_container *endpoints;
1306 struct ast_variable *var;
1307
1308 unsolicited_mwi = ao2_global_obj_ref(mwi_unsolicited);
1309 if (!unsolicited_mwi) {
1310 return;
1311 }
1312
1313 var = ast_variable_new("mailboxes !=", "", "");
1314
1317
1319 if (!endpoints) {
1320 ao2_ref(unsolicited_mwi, -1);
1321 return;
1322 }
1323
1324 solicited_mwi = ao2_global_obj_ref(mwi_solicited);
1325
1326 /* We remove all the old stasis subscriptions first before applying the new configuration. This
1327 * prevents a situation where there might be multiple overlapping stasis subscriptions for an
1328 * endpoint for mailboxes. Though there may be mailbox changes during the gap between unsubscribing
1329 * and resubscribing, up-to-date mailbox state will be sent out to the endpoint when the
1330 * new stasis subscription is established
1331 */
1332 ao2_lock(unsolicited_mwi);
1335 ao2_unlock(unsolicited_mwi);
1336
1337 ao2_ref(endpoints, -1);
1338 ao2_cleanup(solicited_mwi);
1339 ao2_ref(unsolicited_mwi, -1);
1340}
1341
1342/*! \brief Function called to send MWI NOTIFY on any unsolicited mailboxes relating to this AOR */
1343static int send_contact_notify(void *obj, void *arg, int flags)
1344{
1345 struct mwi_subscription *mwi_sub = obj;
1346 const char *aor = arg;
1347
1348 if (!mwi_sub->aors || !strstr(mwi_sub->aors, aor)) {
1349 return 0;
1350 }
1351
1354 ao2_ref(mwi_sub, -1);
1355 }
1356
1357 return 0;
1358}
1359
1360/*! \brief Create mwi subscriptions and notify */
1361static void mwi_contact_changed(const struct ast_sip_contact *contact)
1362{
1363 char *id = ast_strdupa(ast_sorcery_object_get_id(contact));
1364 char *aor = NULL;
1365 struct ast_sip_endpoint *endpoint = NULL;
1366 struct ao2_container *unsolicited_mwi;
1367 struct ao2_container *solicited_mwi;
1368
1369 if (contact->endpoint) {
1370 endpoint = ao2_bump(contact->endpoint);
1371 } else {
1372 if (!ast_strlen_zero(contact->endpoint_name)) {
1373 endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", contact->endpoint_name);
1374 }
1375 }
1376
1377 if (!endpoint || ast_strlen_zero(endpoint->subscription.mwi.mailboxes)) {
1378 ao2_cleanup(endpoint);
1379 return;
1380 }
1381
1382 unsolicited_mwi = ao2_global_obj_ref(mwi_unsolicited);
1383 if (!unsolicited_mwi) {
1384 ao2_cleanup(endpoint);
1385 return;
1386 }
1387
1388 solicited_mwi = ao2_global_obj_ref(mwi_solicited);
1389
1390 ao2_lock(unsolicited_mwi);
1391 create_unsolicited_mwi_subscriptions(endpoint, 0, 0, unsolicited_mwi, solicited_mwi);
1392 ao2_unlock(unsolicited_mwi);
1393 ao2_cleanup(endpoint);
1394 ao2_cleanup(solicited_mwi);
1395 ao2_ref(unsolicited_mwi, -1);
1396
1397 aor = strsep(&id, ";@");
1398 ao2_callback(unsolicited_mwi, OBJ_NODATA, send_contact_notify, aor);
1399}
1400
1401/*! \brief Function called when a contact is updated */
1402static void mwi_contact_updated(const void *object)
1403{
1404 mwi_contact_changed(object);
1405}
1406
1407/*! \brief Function called when a contact is added */
1408static void mwi_contact_added(const void *object)
1409{
1410 mwi_contact_changed(object);
1411}
1412
1413/*! \brief Function called when a contact is deleted */
1414static void mwi_contact_deleted(const void *object)
1415{
1416 const struct ast_sip_contact *contact = object;
1417 struct ao2_iterator *mwi_subs;
1418 struct mwi_subscription *mwi_sub;
1419 struct ast_sip_endpoint *endpoint = NULL;
1420 struct ast_sip_contact *found_contact;
1421 struct ao2_container *unsolicited_mwi;
1422
1423 if (contact->endpoint) {
1424 endpoint = ao2_bump(contact->endpoint);
1425 } else {
1426 if (!ast_strlen_zero(contact->endpoint_name)) {
1427 endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", contact->endpoint_name);
1428 }
1429 }
1430
1431 if (!endpoint || ast_strlen_zero(endpoint->subscription.mwi.mailboxes)) {
1432 ao2_cleanup(endpoint);
1433 return;
1434 }
1435
1436 /* Check if there is another contact */
1437 found_contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
1438 ao2_cleanup(endpoint);
1439 if (found_contact) {
1440 ao2_cleanup(found_contact);
1441 return;
1442 }
1443
1444 unsolicited_mwi = ao2_global_obj_ref(mwi_unsolicited);
1445 if (!unsolicited_mwi) {
1446 return;
1447 }
1448
1449 ao2_lock(unsolicited_mwi);
1450 mwi_subs = ao2_find(unsolicited_mwi, contact->endpoint_name,
1452 if (mwi_subs) {
1453 for (; (mwi_sub = ao2_iterator_next(mwi_subs)); ao2_cleanup(mwi_sub)) {
1455 }
1456 ao2_iterator_destroy(mwi_subs);
1457 }
1458 ao2_unlock(unsolicited_mwi);
1459 ao2_ref(unsolicited_mwi, -1);
1460}
1461
1462/*! \brief Observer for contacts so unsolicited MWI is sent when a contact changes */
1465 .updated = mwi_contact_updated,
1466 .deleted = mwi_contact_deleted,
1467};
1468
1469/*! \brief Task invoked to send initial MWI NOTIFY for unsolicited */
1470static int send_initial_notify_all(void *obj)
1471{
1472 struct ao2_container *unsolicited_mwi = ao2_global_obj_ref(mwi_unsolicited);
1473
1474 if (unsolicited_mwi) {
1475 ao2_callback(unsolicited_mwi, OBJ_NODATA, send_notify, NULL);
1476 ao2_ref(unsolicited_mwi, -1);
1477 }
1478
1479 return 0;
1480}
1481
1482/*! \brief Event callback which fires initial unsolicited MWI NOTIFY messages when we're fully booted */
1483static void mwi_startup_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
1484{
1485 struct ast_json_payload *payload;
1486 const char *type;
1487
1489 return;
1490 }
1491
1492 payload = stasis_message_data(message);
1493 type = ast_json_string_get(ast_json_object_get(payload->json, "type"));
1494
1495 if (strcmp(type, "FullyBooted")) {
1496 return;
1497 }
1498
1501
1503}
1504
1505static void global_loaded(const char *object_type)
1506{
1511}
1512
1515};
1516
1517static int reload(void)
1518{
1521 }
1522 return 0;
1523}
1524
1525static int unload_module(void)
1526{
1527 /*
1528 * pjsip_evsub_register_pkg is called by ast_sip_register_subscription_handler
1529 * but there is no corresponding unregister function, so unloading
1530 * a module does not remove the event package. If this module is ever
1531 * loaded again, then pjproject will assert and cause a crash.
1532 * For that reason, we must only be allowed to unload when
1533 * asterisk is shutting down. If a pjsip_evsub_unregister_pkg
1534 * API is added in the future then we should go back to unloading
1535 * the module as intended.
1536 */
1537
1538 if (ast_shutdown_final()) {
1539 struct ao2_container *unsolicited_mwi;
1540
1543
1544 unsolicited_mwi = ao2_global_obj_replace(mwi_unsolicited, NULL);
1545 if (unsolicited_mwi) {
1547 ao2_ref(unsolicited_mwi, -1);
1548 }
1549
1550 ao2_global_obj_release(mwi_solicited);
1551
1553 ast_log(LOG_WARNING, "Unload incomplete. Try again later\n");
1554 return -1;
1555 }
1557
1559
1562 return 0;
1563 } else {
1564 return -1;
1565 }
1566}
1567
1568static int load_module(void)
1569{
1570 struct ao2_container *mwi_container;
1571
1574 }
1575
1578 if (!mwi_serializer_pool) {
1579 ast_log(AST_LOG_WARNING, "Failed to create MWI serializer pool. The default SIP pool will be used for MWI\n");
1580 }
1581
1584 if (!mwi_container) {
1585 unload_module();
1587 }
1588 ao2_global_obj_replace_unref(mwi_solicited, mwi_container);
1589 ao2_ref(mwi_container, -1);
1590
1593 if (!mwi_container) {
1594 unload_module();
1596 }
1597 ao2_global_obj_replace_unref(mwi_unsolicited, mwi_container);
1598 ao2_ref(mwi_container, -1);
1599
1603
1609 } else {
1610 struct stasis_subscription *sub;
1611
1615 }
1616 }
1617
1618 if (!mwi_serializer_pool) {
1619 /*
1620 * If the mwi serializer pool was unable to be established then the module will
1621 * use the default serializer pool. If this happens prevent manual unloading
1622 * since there would now exist the potential for a crash on unload.
1623 */
1625 }
1626
1628}
1629
1631 .support_level = AST_MODULE_SUPPORT_CORE,
1632 .load = load_module,
1633 .unload = unload_module,
1634 .reload = reload,
1635 .load_pri = AST_MODPRI_CHANNEL_DEPEND + 5,
1636 .requires = "res_pjsip,res_pjsip_pubsub",
const char * str
Definition: app_jack.c:147
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
int ast_shutdown_final(void)
Definition: asterisk.c:1867
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_global_obj_replace_unref(holder, obj)
Replace an ao2 object in the global holder, throwing away any old object.
Definition: astobj2.h:901
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_callback_data(container, flags, cb_fn, arg, data)
Definition: astobj2.h:1723
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
Definition: astobj2.h:1554
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
Definition: astobj2.h:918
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_global_obj_replace(holder, obj)
Replace an ao2 object in the global holder.
Definition: astobj2.h:878
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
Definition: astobj2.h:859
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_SEARCH_PARTIAL_KEY
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
Definition: astobj2.h:1116
@ OBJ_SEARCH_OBJECT
The arg parameter is an object of the same type.
Definition: astobj2.h:1087
@ OBJ_NOLOCK
Assume that the ao2_container is already locked.
Definition: astobj2.h:1063
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_SEARCH_MASK
Search option field mask.
Definition: astobj2.h:1072
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
@ OBJ_UNLINK
Definition: astobj2.h:1039
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
static const char type[]
Definition: chan_ooh323.c:109
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static const char name[]
Definition: format_mp3.c:68
struct stasis_topic * ast_manager_get_topic(void)
Get the Stasis Message Bus API topic for AMI.
Definition: manager.c:1872
struct stasis_message_type * stasis_subscription_change_type(void)
Gets the message type for subscription change notices.
struct ast_flags ast_options
Definition: options.c:61
@ AST_OPT_FLAG_FULLY_BOOTED
Definition: options.h:58
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:2099
char * strsep(char **str, const char *delims)
#define ast_variable_new(name, value, filename)
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
Support for logging to various files, console and syslog Configuration in file logger....
#define AST_LOG_WARNING
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_WARNING
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:283
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:407
static struct ao2_container * endpoints
struct stasis_message_type * ast_manager_get_generic_type(void)
Get the stasis_message_type for generic messages.
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
Definition: module.h:464
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:326
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Asterisk MWI API.
struct stasis_message_type * ast_mwi_state_type(void)
Get the Stasis Message Bus API message type for MWI messages.
struct stasis_subscription * ast_mwi_subscriber_subscription(struct ast_mwi_subscriber *sub)
Retrieve the stasis MWI topic subscription if available.
Definition: mwi.c:277
void * ast_mwi_unsubscribe_and_join(struct ast_mwi_subscriber *sub)
Unsubscribe from the stasis topic, block until the final message is received, and then unsubscribe fr...
Definition: mwi.c:259
struct ast_mwi_subscriber * ast_mwi_subscribe_pool(const char *mailbox, stasis_subscription_cb callback, void *data)
Add an MWI state subscriber, and stasis subscription to the mailbox.
Definition: mwi.c:235
struct ast_mwi_state * ast_mwi_subscriber_data(struct ast_mwi_subscriber *sub)
Retrieves the state data object associated with the MWI subscriber.
Definition: mwi.c:269
static struct stasis_rest_handlers mailboxes
REST handler for /api-docs/mailboxes.json.
struct stasis_forward * sub
Definition: res_corosync.c:240
struct ao2_container * container
Definition: res_fax.c:501
static struct stasis_subscription * mwi_sub
struct ast_sip_contact * ast_sip_location_retrieve_contact_from_aor_list(const char *aor_list)
Retrieve the first bound contact from a list of AORs.
Definition: location.c:304
struct ast_sip_aor * ast_sip_location_retrieve_aor(const char *aor_name)
Retrieve a named AOR.
Definition: location.c:147
int ast_sip_for_each_aor(const char *aors, ao2_callback_fn on_aor, void *arg)
For every aor in the comma separated aors string call the given 'on_aor' handler.
Definition: location.c:687
char * ast_sip_get_default_voicemail_extension(void)
Retrieve the default voicemail extension.
int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint, void *token, void(*callback)(void *token, pjsip_event *e))
General purpose method for sending a SIP request.
Definition: res_pjsip.c:1979
int ast_sip_add_body(pjsip_tx_data *tdata, const struct ast_sip_body *body)
Add a body to an outbound SIP message.
Definition: res_pjsip.c:2052
unsigned int ast_sip_get_mwi_disable_initial_unsolicited(void)
Retrieve the global setting 'disable sending unsolicited mwi on startup'.
struct ast_threadpool * ast_sip_threadpool(void)
Retrieve the SIP threadpool object.
Definition: res_pjsip.c:3462
int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint, const char *uri, struct ast_sip_contact *contact, pjsip_tx_data **tdata)
General purpose method for creating a SIP request.
Definition: res_pjsip.c:1435
int ast_sip_get_mwi_tps_queue_low(void)
Retrieve the global MWI taskprocessor low water clear alert level.
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
unsigned int ast_sip_get_mwi_tps_queue_high(void)
Retrieve the global MWI taskprocessor high water alert trigger level.
struct ao2_container * ast_sip_location_retrieve_aor_contacts(const struct ast_sip_aor *aor)
Retrieve all contacts currently available for an AOR.
Definition: location.c:247
static void mwi_contact_deleted(const void *object)
Function called when a contact is deleted.
static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint, const char *resource)
static void send_unsolicited_mwi_notify(struct mwi_subscription *sub, struct ast_sip_message_accumulator *counter)
static void mwi_subscription_destructor(void *obj)
static struct ast_sorcery_observer global_observer
static struct mwi_subscription * mwi_create_subscription(struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub)
static int send_notify(void *obj, void *arg, int flags)
static void mwi_to_ami(struct ast_sip_subscription *sub, struct ast_str **buf)
static void global_loaded(const char *object_type)
static int stasis_sub_hash(const void *obj, const int flags)
static void mwi_contact_added(const void *object)
Function called when a contact is added.
static int unsubscribe(void *obj, void *arg, int flags)
static void send_mwi_notify(struct mwi_subscription *sub)
static void set_voicemail_extension(pj_pool_t *pool, pjsip_sip_uri *local_uri, struct ast_sip_message_accumulator *counter, const char *voicemail_extension)
static int mwi_on_aor(void *obj, void *arg, int flags)
static int allow_and_or_replace_unsolicited(struct ast_sip_endpoint *endpoint, const char *mailbox, struct ao2_container *unsolicited_mwi)
#define MAX_UNLOAD_TIMEOUT_TIME
Definition: res_pjsip_mwi.c:63
static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flags)
static int get_message_count(void *obj, void *arg, int flags)
static int has_mwi_subscription(struct ao2_container *container, struct ast_sip_endpoint *endpoint, const char *mailbox, struct mwi_subscription **mwi_sub, struct mwi_stasis_subscription **mwi_stasis)
static int add_mwi_datastore(struct mwi_subscription *sub)
static void mwi_contact_updated(const void *object)
Function called when a contact is updated.
static void mwi_subscription_shutdown(struct ast_sip_subscription *sub)
static int create_unsolicited_mwi_subscriptions(struct ast_sip_endpoint *endpoint, int recreate, int send_now, struct ao2_container *unsolicited_mwi, struct ao2_container *solicited_mwi)
static struct mwi_subscription * mwi_subscription_alloc(struct ast_sip_endpoint *endpoint, unsigned int is_solicited, struct ast_sip_subscription *sip_sub)
static struct ast_datastore_info mwi_ds_info
static struct mwi_stasis_subscription * mwi_stasis_subscription_alloc(const char *mailbox, struct mwi_subscription *mwi_sub)
static int mwi_subscription_established(struct ast_sip_subscription *sub)
static struct ast_serializer_pool * mwi_serializer_pool
Definition: res_pjsip_mwi.c:66
static void mwi_ds_destroy(void *data)
static char * default_voicemail_extension
Definition: res_pjsip_mwi.c:49
static int is_unsolicited_allowed(struct ast_sip_endpoint *endpoint, const char *mailbox, struct ao2_container *unsolicited_mwi, struct ao2_container *solicited_mwi)
static const struct ast_sorcery_observer mwi_contact_observer
Observer for contacts so unsolicited MWI is sent when a contact changes.
static void create_mwi_subscriptions(void)
static struct mwi_subscription * mwi_subscribe_all(struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub)
static void * mwi_get_notify_data(struct ast_sip_subscription *sub)
static struct mwi_subscription * mwi_subscribe_single(struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub, const char *name)
static struct ast_sip_notifier mwi_notifier
Definition: res_pjsip_mwi.c:75
static void mwi_startup_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Event callback which fires initial unsolicited MWI NOTIFY messages when we're fully booted.
static int mwi_validate_for_aor(void *obj, void *arg, int flags)
Determine if an endpoint is a candidate to be able to subscribe for MWI.
static int send_contact_notify(void *obj, void *arg, int flags)
Function called to send MWI NOTIFY on any unsolicited mailboxes relating to this AOR.
static void mwi_contact_changed(const struct ast_sip_contact *contact)
Create mwi subscriptions and notify.
static int serialized_notify(void *userdata)
static int mwi_sub_cmp(void *obj, void *arg, int flags)
static struct ast_sip_aor * find_aor_for_resource(struct ast_sip_endpoint *endpoint, const char *resource)
static int serialized_cleanup(void *userdata)
static int unsubscribe_stasis(void *obj, void *arg, int flags)
static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
static int load_module(void)
static void mwi_subscription_mailboxes_str(struct ao2_container *stasis_subs, struct ast_str **str)
static int stasis_sub_cmp(void *obj, void *arg, int flags)
#define STASIS_BUCKETS
Definition: res_pjsip_mwi.c:51
static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, void *data, int flags)
static int unload_module(void)
static int reload(void)
AO2_GLOBAL_OBJ_STATIC(mwi_unsolicited)
#define MWI_BUCKETS
Definition: res_pjsip_mwi.c:52
static int mwi_sub_hash(const void *obj, const int flags)
static int send_initial_notify_all(void *obj)
Task invoked to send initial MWI NOTIFY for unsolicited.
#define MWI_DATASTORE
Definition: res_pjsip_mwi.c:57
#define MWI_TYPE
Definition: res_pjsip_mwi.c:54
#define MWI_SUBTYPE
Definition: res_pjsip_mwi.c:55
#define MWI_SERIALIZER_POOL_SIZE
Definition: res_pjsip_mwi.c:60
static struct ast_sip_subscription_handler mwi_handler
Definition: res_pjsip_mwi.c:82
struct ast_sip_endpoint * ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub)
Get the endpoint that is associated with this subscription.
void ast_sip_unregister_subscription_handler(struct ast_sip_subscription_handler *handler)
Unregister a subscription handler.
int ast_sip_register_subscription_handler(struct ast_sip_subscription_handler *handler)
Register a subscription handler.
void ast_sip_subscription_destroy(struct ast_sip_subscription *sub)
Alert the pubsub core that the subscription is ready for destruction.
void ast_sip_subscription_remove_datastore(struct ast_sip_subscription *subscription, const char *name)
Remove a subscription datastore from the subscription.
int ast_sip_pubsub_generate_body_content(const char *content_type, const char *content_subtype, struct ast_sip_body_data *data, struct ast_str **str)
Generate body content for a PUBLISH or NOTIFY.
struct ast_datastore * ast_sip_subscription_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Alternative for ast_datastore_alloc()
struct ast_taskprocessor * ast_sip_subscription_get_serializer(struct ast_sip_subscription *sub)
Get the serializer for the subscription.
int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip_body_data *notify_data, int terminate)
Notify a SIP subscription of a state change.
int ast_sip_subscription_add_datastore(struct ast_sip_subscription *subscription, struct ast_datastore *datastore)
Add a datastore to a SIP subscription.
const char * ast_sip_subscription_get_resource_name(struct ast_sip_subscription *sub)
Get the name of the subscribed resource.
pjsip_sip_uri * ast_sip_subscription_get_sip_uri(struct ast_sip_subscription *sub)
Retrieve the local sip uri for this subscription.
#define AST_SIP_MESSAGE_ACCUMULATOR
pjsip_dialog * ast_sip_subscription_get_dialog(struct ast_sip_subscription *sub)
Get the pjsip dialog that is associated with this subscription.
struct ast_datastore * ast_sip_subscription_get_datastore(struct ast_sip_subscription *subscription, const char *name)
Retrieve a subscription datastore.
#define NULL
Definition: resample.c:96
struct ast_taskprocessor * ast_serializer_pool_get(struct ast_serializer_pool *pool)
Retrieve a serializer from the pool.
Definition: serializer.c:127
int ast_serializer_pool_set_alerts(struct ast_serializer_pool *pool, long high, long low)
Set taskprocessor alert levels for the serializers in the pool.
Definition: serializer.c:156
struct ast_serializer_pool * ast_serializer_pool_create(const char *name, unsigned int size, struct ast_threadpool *threadpool, int timeout)
Create a serializer pool.
Definition: serializer.c:76
int ast_serializer_pool_destroy(struct ast_serializer_pool *pool)
Destroy the serializer pool.
Definition: serializer.c:39
Sorcery Data Access Layer API.
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Remove an observer from a specific object type.
Definition: sorcery.c:2418
@ AST_RETRIEVE_FLAG_MULTIPLE
Return all matching objects.
Definition: sorcery.h:120
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition: sorcery.c:1853
int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Add an observer to a specific object type.
Definition: sorcery.c:2386
void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type)
Inform any wizards of a specific object type to reload persistent objects.
Definition: sorcery.c:1442
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Definition: sorcery.c:1897
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
@ STASIS_SUBSCRIPTION_FILTER_SELECTIVE
Definition: stasis.h:297
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
Definition: stasis.c:1023
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
Definition: stasis.c:1077
#define stasis_subscribe_pool(topic, callback, data)
Definition: stasis.h:680
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
int stasis_subscription_final_message(struct stasis_subscription *sub, struct stasis_message *msg)
Determine whether a message is the final message to be received on a subscription.
Definition: stasis.c:1174
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:971
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1259
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Structure for a data store type.
Definition: datastore.h:31
void(* destroy)(void *data)
Definition: datastore.h:34
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
struct ast_json * json
Definition: json.h:1083
struct ast_module * self
Definition: module.h:342
The structure that contains MWI state.
Definition: mwi.h:455
int old_msgs
Definition: mwi.h:460
int new_msgs
Definition: mwi.h:459
A SIP address of record.
Definition: res_pjsip.h:478
char * voicemail_extension
Definition: res_pjsip.h:508
const ast_string_field mailboxes
Definition: res_pjsip.h:486
Data used to create bodies for NOTIFY/PUBLISH requests.
const char * body_type
SIP body description.
Definition: res_pjsip.h:2325
const char * type
Definition: res_pjsip.h:2327
const char * body_text
Definition: res_pjsip.h:2331
const char * subtype
Definition: res_pjsip.h:2329
Contact associated with an address of record.
Definition: res_pjsip.h:392
const ast_string_field uri
Definition: res_pjsip.h:414
struct ast_sip_endpoint * endpoint
Definition: res_pjsip.h:424
const ast_string_field endpoint_name
Definition: res_pjsip.h:414
struct ast_sip_mwi_configuration mwi
Definition: res_pjsip.h:751
An entity with which Asterisk communicates.
Definition: res_pjsip.h:963
const ast_string_field aors
Definition: res_pjsip.h:992
struct ast_sip_endpoint_subscription_configuration subscription
Definition: res_pjsip.h:998
Message counter used for message-summary XML bodies.
const ast_string_field mailboxes
Definition: res_pjsip.h:733
unsigned int aggregate
Definition: res_pjsip.h:735
const ast_string_field fromuser
Definition: res_pjsip.h:733
unsigned int subscribe_replaces_unsolicited
Definition: res_pjsip.h:737
const char * default_accept
Default body type defined for the event package this notifier handles.
Structure representing a "virtual" SIP subscription.
Interface for a sorcery object type observer.
Definition: sorcery.h:332
void(* loaded)(const char *object_type)
Callback for when an object type is loaded/reloaded.
Definition: sorcery.h:343
void(* created)(const void *object)
Callback for when an object is created.
Definition: sorcery.h:334
Support for dynamic strings.
Definition: strings.h:623
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:69
Structure for variables, used for configurations and for channel variables.
Definition: astman.c:222
Wrapper for stasis subscription.
Definition: res_pjsip_mwi.c:97
struct ast_mwi_subscriber * mwi_subscriber
Definition: res_pjsip_mwi.c:99
A subscription for MWI.
struct ast_sip_subscription * sip_sub
unsigned int terminate
unsigned int is_solicited
struct ao2_container * stasis_subs
Definition: test_heap.c:38
pjsip_evsub_state state
struct ast_sip_endpoint * endpoint
struct ast_sip_message_accumulator * counter
struct mwi_subscription * sub
An API for managing task processing threads that can be shared across modules.
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739