Asterisk - The Open Source Telephony Project  GIT-master-4a4f1a5
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"
38 #include "asterisk/taskprocessor.h"
39 #include "asterisk/serializer.h"
40 #include "asterisk/sorcery.h"
41 #include "asterisk/stasis.h"
42 #include "asterisk/mwi.h"
43 
44 struct mwi_subscription;
45 
46 AO2_GLOBAL_OBJ_STATIC(mwi_unsolicited);
47 AO2_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 
69 static void mwi_to_ami(struct ast_sip_subscription *sub, struct ast_str **buf);
70 static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
71  const char *resource);
74 
75 static struct ast_sip_notifier mwi_notifier = {
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 
137 static 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 
172 static 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;
181  case OBJ_SEARCH_OBJECT:
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 
192 static 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) {
200  case OBJ_SEARCH_OBJECT:
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 
219 static 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) {
261  ao2_cleanup(sub);
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 
279 static 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;
288  case OBJ_SEARCH_OBJECT:
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 
299 static 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) {
307  case OBJ_SEARCH_OBJECT:
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 
326 static 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 
345 static 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)) {
352  vm_exten = default_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 
371 static 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;
387  struct ast_sip_body_data body_data = {
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 
450 static 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 
570  send_unsolicited_mwi_notify(sub, &counter);
571 }
572 
573 static 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 
584 static int create_unsolicited_mwi_subscriptions(struct ast_sip_endpoint *endpoint,
585  int recreate, int send_now, struct ao2_container *unsolicited_mwi, struct ao2_container *solicited_mwi);
586 
588 {
589  struct mwi_subscription *mwi_sub;
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 
629 static void mwi_ds_destroy(void *data)
630 {
631  struct mwi_subscription *sub = data;
632 
633  ao2_ref(sub, -1);
634 }
635 
636 static struct ast_datastore_info mwi_ds_info = {
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 mwi_sub [out] May contain the located mwi_subscription
678  * \param mwi_stasis [out] 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  */
729 static int allow_and_or_replace_unsolicited(struct ast_sip_endpoint *endpoint, const char *mailbox,
730  struct ao2_container *unsolicited_mwi)
731 {
732  struct mwi_subscription *mwi_sub;
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 
769 static 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  */
783 static int is_unsolicited_allowed(struct ast_sip_endpoint *endpoint, const char *mailbox,
784  struct ao2_container *unsolicited_mwi, struct ao2_container *solicited_mwi)
785 {
786  struct mwi_subscription *mwi_sub;
787  struct mwi_stasis_subscription *mwi_stasis;
788 
789  if (ast_strlen_zero(mailbox)) {
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;
824  send_notify(mwi_sub, NULL, 0);
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  */
844 static 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, ",")))) {
864  if (ast_strlen_zero(mailbox)) {
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 
889 static 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 
904  if (ast_strlen_zero(mailbox)) {
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 
951  sub = mwi_create_subscription(endpoint, sip_sub);
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 
965  sub = mwi_create_subscription(endpoint, sip_sub);
966  if (!sub) {
967  return NULL;
968  }
969 
971  return sub;
972 }
973 
974 static 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 
1037  ao2_cleanup(sub);
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 
1078 static void mwi_subscription_mailboxes_str(struct ao2_container *stasis_subs,
1079  struct ast_str **str)
1080 {
1081  int is_first = 1;
1082  struct mwi_stasis_subscription *node;
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 
1118 static 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 
1127 static 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 
1142 static 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 
1156 static 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 
1169  if (ast_mwi_state_type() == stasis_message_type(msg)) {
1170  send_notify(mwi_sub, NULL, 0);
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 
1287 static 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 
1292 static int unsubscribe(void *obj, void *arg, int flags)
1293 {
1294  struct mwi_subscription *mwi_sub = obj;
1295 
1297 
1298  return CMP_MATCH;
1299 }
1300 
1301 static void create_mwi_subscriptions(void)
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 */
1343 static 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 */
1361 static void mwi_contact_changed(const struct ast_sip_contact *contact)
1362 {
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 */
1402 static void mwi_contact_updated(const void *object)
1403 {
1404  mwi_contact_changed(object);
1405 }
1406 
1407 /*! \brief Function called when a contact is added */
1408 static void mwi_contact_added(const void *object)
1409 {
1410  mwi_contact_changed(object);
1411 }
1412 
1413 /*! \brief Function called when a contact is deleted */
1414 static 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)) {
1454  unsubscribe(mwi_sub, NULL, 0);
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 */
1463 static const struct ast_sorcery_observer mwi_contact_observer = {
1465  .updated = mwi_contact_updated,
1466  .deleted = mwi_contact_deleted,
1467 };
1468 
1469 /*! \brief Task invoked to send initial MWI NOTIFY for unsolicited */
1470 static 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 */
1483 static 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 
1505 static void global_loaded(const char *object_type)
1506 {
1511 }
1512 
1513 static struct ast_sorcery_observer global_observer = {
1514  .loaded = global_loaded,
1515 };
1516 
1517 static int reload(void)
1518 {
1521  }
1522  return 0;
1523 }
1524 
1525 static int unload_module(void)
1526 {
1527  struct ao2_container *unsolicited_mwi;
1528 
1531 
1532  unsolicited_mwi = ao2_global_obj_replace(mwi_unsolicited, NULL);
1533  if (unsolicited_mwi) {
1535  ao2_ref(unsolicited_mwi, -1);
1536  }
1537 
1538  ao2_global_obj_release(mwi_solicited);
1539 
1541  ast_log(LOG_WARNING, "Unload incomplete. Try again later\n");
1542  return -1;
1543  }
1545 
1547 
1550  return 0;
1551 }
1552 
1553 static int load_module(void)
1554 {
1555  struct ao2_container *mwi_container;
1556 
1558  return AST_MODULE_LOAD_DECLINE;
1559  }
1560 
1563  if (!mwi_serializer_pool) {
1564  ast_log(AST_LOG_WARNING, "Failed to create MWI serializer pool. The default SIP pool will be used for MWI\n");
1565  }
1566 
1569  if (!mwi_container) {
1570  unload_module();
1571  return AST_MODULE_LOAD_DECLINE;
1572  }
1573  ao2_global_obj_replace_unref(mwi_solicited, mwi_container);
1574  ao2_ref(mwi_container, -1);
1575 
1578  if (!mwi_container) {
1579  unload_module();
1580  return AST_MODULE_LOAD_DECLINE;
1581  }
1582  ao2_global_obj_replace_unref(mwi_unsolicited, mwi_container);
1583  ao2_ref(mwi_container, -1);
1584 
1588 
1594  } else {
1595  struct stasis_subscription *sub;
1596 
1600  }
1601  }
1602 
1603  if (!mwi_serializer_pool) {
1604  /*
1605  * If the mwi serializer pool was unable to be established then the module will
1606  * use the default serializer pool. If this happens prevent manual unloading
1607  * since there would now exist the potential for a crash on unload.
1608  */
1610  }
1611 
1612  return AST_MODULE_LOAD_SUCCESS;
1613 }
1614 
1616  .support_level = AST_MODULE_SUPPORT_CORE,
1617  .load = load_module,
1618  .unload = unload_module,
1619  .reload = reload,
1620  .load_pri = AST_MODPRI_CHANNEL_DEPEND + 5,
1621  .requires = "res_pjsip,res_pjsip_pubsub",
1622 );
const char * str
Definition: app_jack.c:147
#define var
Definition: ast_expr2f.c:614
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:182
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
#define ao2_link(container, obj)
Definition: astobj2.h:1549
@ CMP_MATCH
Definition: astobj2.h:1031
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:365
#define ao2_global_obj_replace_unref(holder, obj)
Definition: astobj2.h:908
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
#define ao2_callback_data(container, flags, cb_fn, arg, data)
Definition: astobj2.h:1743
#define ao2_unlink(container, obj)
Definition: astobj2.h:1598
#define ao2_link_flags(container, obj, flags)
Definition: astobj2.h:1572
#define ao2_global_obj_ref(holder)
Definition: astobj2.h:925
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
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:730
#define ao2_lock(a)
Definition: astobj2.h:718
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_global_obj_replace(holder, obj)
Definition: astobj2.h:885
#define ao2_global_obj_release(holder)
Definition: astobj2.h:865
#define ao2_bump(obj)
Definition: astobj2.h:491
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:1120
@ OBJ_SEARCH_OBJECT
The arg parameter is an object of the same type.
Definition: astobj2.h:1091
@ OBJ_NOLOCK
Assume that the ao2_container is already locked.
Definition: astobj2.h:1067
@ OBJ_NODATA
Definition: astobj2.h:1048
@ OBJ_SEARCH_MASK
Search option field mask.
Definition: astobj2.h:1076
@ OBJ_MULTIPLE
Definition: astobj2.h:1053
@ OBJ_UNLINK
Definition: astobj2.h:1043
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1105
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
static char mailbox[AST_MAX_MAILBOX_UNIQUEID]
Definition: chan_mgcp.c:207
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:1720
int old_msgs
Definition: mwi.h:462
int new_msgs
Definition: mwi.h:461
struct stasis_message_type * ast_mwi_state_type(void)
Get the Stasis Message Bus API message type for MWI messages.
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:5175
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:1263
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:273
Support for logging to various files, console and syslog Configuration in file logger....
#define AST_LOG_WARNING
Definition: logger.h:280
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:453
#define LOG_WARNING
Definition: logger.h:275
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_subscription * ast_mwi_subscriber_subscription(struct ast_mwi_subscriber *sub)
Retrieve the stasis MWI topic subscription if available.
Definition: mwi.c:272
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:254
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:230
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:264
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:502
static struct stasis_subscription * mwi_sub
struct ast_threadpool * ast_sip_threadpool(void)
Retrieve the SIP threadpool object.
Definition: res_pjsip.c:5667
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
char * ast_sip_get_default_voicemail_extension(void)
Retrieve the default voicemail extension.
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
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:684
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
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:5071
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:5128
unsigned int ast_sip_get_mwi_disable_initial_unsolicited(void)
Retrieve the global setting 'disable sending unsolicited mwi on startup'.
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:4527
int ast_sip_get_mwi_tps_queue_low(void)
Retrieve the global MWI taskprocessor low water clear alert level.
unsigned int ast_sip_get_mwi_tps_queue_high(void)
Retrieve the global MWI taskprocessor high water alert trigger level.
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 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 struct mwi_subscription * mwi_create_subscription(struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub)
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 ast_datastore_info mwi_ds_info
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 struct mwi_subscription * mwi_subscription_alloc(struct ast_sip_endpoint *endpoint, unsigned int is_solicited, struct ast_sip_subscription *sip_sub)
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 void * mwi_get_notify_data(struct ast_sip_subscription *sub)
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 struct ast_sip_aor * find_aor_for_resource(struct ast_sip_endpoint *endpoint, const char *resource)
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 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 struct mwi_stasis_subscription * mwi_stasis_subscription_alloc(const char *mailbox, struct mwi_subscription *mwi_sub)
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
static struct mwi_subscription * mwi_subscribe_all(struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub)
#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
static struct mwi_subscription * mwi_subscribe_single(struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub, const char *name)
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.
const char * ast_sip_subscription_get_resource_name(struct ast_sip_subscription *sub)
Get the name of the subscribed resource.
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_get_datastore(struct ast_sip_subscription *subscription, const char *name)
Retrieve a subscription datastore.
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.
struct ast_datastore * ast_sip_subscription_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Alternative for ast_datastore_alloc()
int ast_sip_subscription_add_datastore(struct ast_sip_subscription *subscription, struct ast_datastore *datastore)
Add a datastore to a SIP subscription.
pjsip_sip_uri * ast_sip_subscription_get_sip_uri(struct ast_sip_subscription *sub)
Retrieve the local sip uri for this subscription.
struct ast_taskprocessor * ast_sip_subscription_get_serializer(struct ast_sip_subscription *sub)
Get the serializer for the 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_sip_endpoint * ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub)
Get the endpoint that is associated with this subscription.
#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
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_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
int ast_serializer_pool_destroy(struct ast_serializer_pool *pool)
Destroy the serializer pool.
Definition: serializer.c:39
Sorcery Data Access Layer API.
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
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
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
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_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
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
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:972
@ 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:1024
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:1078
#define stasis_subscribe_pool(topic, callback, data)
Definition: stasis.h:680
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:1175
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
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:1104
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:727
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1224
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:633
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
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:1841
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:68
void * data
Definition: datastore.h:70
struct ast_json * json
Definition: json.h:1042
struct ast_module * self
Definition: module.h:342
The structure that contains MWI state.
Definition: mwi.h:457
A SIP address of record.
Definition: res_pjsip.h:376
char * voicemail_extension
Definition: res_pjsip.h:406
const ast_string_field mailboxes
Definition: res_pjsip.h:384
Data used to create bodies for NOTIFY/PUBLISH requests.
const char * body_type
SIP body description.
Definition: res_pjsip.h:2072
const char * type
Definition: res_pjsip.h:2074
const char * body_text
Definition: res_pjsip.h:2078
const char * subtype
Definition: res_pjsip.h:2076
Contact associated with an address of record.
Definition: res_pjsip.h:297
struct ast_sip_mwi_configuration mwi
Definition: res_pjsip.h:642
An entity with which Asterisk communicates.
Definition: res_pjsip.h:854
const ast_string_field aors
Definition: res_pjsip.h:881
struct ast_sip_endpoint_subscription_configuration subscription
Definition: res_pjsip.h:887
Message counter used for message-summary XML bodies.
const ast_string_field mailboxes
Definition: res_pjsip.h:624
unsigned int aggregate
Definition: res_pjsip.h:626
const ast_string_field fromuser
Definition: res_pjsip.h:624
unsigned int subscribe_replaces_unsolicited
Definition: res_pjsip.h:628
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
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:597
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:911
#define ast_assert(a)
Definition: utils.h:710