static void send_mwi_notify(struct mwi_subscription *sub) { struct ast_sip_message_accumulator counter = { .old_msgs = 0, .new_msgs = 0, }; struct ast_sip_body_data data = { .body_type = AST_SIP_MESSAGE_ACCUMULATOR, .body_data = &counter, }; ao2_callback(sub->stasis_subs, OBJ_NODATA, get_message_count, &counter); if (sub->is_solicited) { ast_sip_subscription_notify(sub->sip_sub, &data, 0); return; } send_unsolicited_mwi_notify(sub, &counter); } static int unsubscribe_stasis(void *obj, void *arg, int flags) { struct mwi_stasis_subscription *mwi_stasis = obj; if (mwi_stasis->stasis_sub) { ast_debug(3, "Removing stasis subscription to mailbox %s\n", mwi_stasis->mailbox); mwi_stasis->stasis_sub = stasis_unsubscribe(mwi_stasis->stasis_sub); } return CMP_MATCH; } static void mwi_subscription_shutdown(struct ast_sip_subscription *sub) { struct mwi_subscription *mwi_sub; RAII_VAR(struct ast_datastore *, mwi_datastore, ast_sip_subscription_get_datastore(sub, MWI_DATASTORE), ao2_cleanup); if (!mwi_datastore) { return; } mwi_sub = mwi_datastore->data; ao2_callback(mwi_sub->stasis_subs, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe_stasis, NULL); } static struct ast_datastore_info mwi_ds_info = { }; static int add_mwi_datastore(struct mwi_subscription *sub) { RAII_VAR(struct ast_datastore *, mwi_datastore, NULL, ao2_cleanup); mwi_datastore = ast_sip_subscription_alloc_datastore(&mwi_ds_info, MWI_DATASTORE); if (!mwi_datastore) { return -1; } mwi_datastore->data = sub; ast_sip_subscription_add_datastore(sub->sip_sub, mwi_datastore); return 0; }
static void send_mwi_notify(struct mwi_subscription *sub) { struct ast_sip_message_accumulator counter = { .old_msgs = 0, .new_msgs = 0, }; struct ast_sip_body_data data = { .body_type = AST_SIP_MESSAGE_ACCUMULATOR, .body_data = &counter, }; ao2_callback(sub->stasis_subs, OBJ_NODATA, get_message_count, &counter); if (sub->is_solicited) { ast_sip_subscription_notify(sub->sip_sub, &data, 0); return; } send_unsolicited_mwi_notify(sub, &counter); } static int unsubscribe_stasis(void *obj, void *arg, int flags) { struct mwi_stasis_subscription *mwi_stasis = obj; if (mwi_stasis->stasis_sub) { ast_debug(3, "Removing stasis subscription to mailbox %s\n", mwi_stasis->mailbox); mwi_stasis->stasis_sub = stasis_unsubscribe_and_join(mwi_stasis->stasis_sub); } return CMP_MATCH; } static void mwi_subscription_shutdown(struct ast_sip_subscription *sub) { struct mwi_subscription *mwi_sub; struct ast_datastore *mwi_datastore; mwi_datastore = ast_sip_subscription_get_datastore(sub, MWI_DATASTORE); if (!mwi_datastore) { return; } mwi_sub = mwi_datastore->data; ao2_callback(mwi_sub->stasis_subs, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe_stasis, NULL); ast_sip_subscription_remove_datastore(sub, MWI_DATASTORE); ao2_ref(mwi_datastore, -1); } static void mwi_ds_destroy(void *data) { struct mwi_subscription *sub = data; ao2_ref(sub, -1); } static struct ast_datastore_info mwi_ds_info = { .destroy = mwi_ds_destroy, }; static int add_mwi_datastore(struct mwi_subscription *sub) { struct ast_datastore *mwi_datastore; int res; mwi_datastore = ast_sip_subscription_alloc_datastore(&mwi_ds_info, MWI_DATASTORE); if (!mwi_datastore) { return -1; } ao2_ref(sub, +1); mwi_datastore->data = sub; /* * NOTE: Adding the datastore to the subscription creates a ref loop * that must be manually broken. */ res = ast_sip_subscription_add_datastore(sub->sip_sub, mwi_datastore); ao2_ref(mwi_datastore, -1); return res; } /*! * \brief Determines if an endpoint is receiving unsolicited MWI for a particular mailbox. * * \param endpoint The endpoint to check * \param mailbox The candidate mailbox * \retval 0 The endpoint does not receive unsolicited MWI for this mailbox * \retval 1 The endpoint receives unsolicited MWI for this mailbox */ static int endpoint_receives_unsolicited_mwi_for_mailbox(struct ast_sip_endpoint *endpoint, const char *mailbox) { struct ao2_iterator *mwi_subs; struct mwi_subscription *mwi_sub; const char *endpoint_id = ast_sorcery_object_get_id(endpoint); int ret = 0; mwi_subs = ao2_find(unsolicited_mwi, endpoint_id, OBJ_SEARCH_KEY | OBJ_MULTIPLE); if (!mwi_subs) { return 0; } for (; (mwi_sub = ao2_iterator_next(mwi_subs)) && !ret; ao2_cleanup(mwi_sub)) { struct mwi_stasis_subscription *mwi_stasis; mwi_stasis = ao2_find(mwi_sub->stasis_subs, mailbox, OBJ_SEARCH_KEY); if (mwi_stasis) { ret = 1; ao2_cleanup(mwi_stasis); } } ao2_iterator_destroy(mwi_subs); return ret; } /*! * \brief Determine if an endpoint is a candidate to be able to subscribe for MWI * * Currently, this just makes sure that the endpoint is not already receiving unsolicted * MWI for any of an AOR's configured mailboxes. * * \param obj The AOR to which the endpoint is subscribing. * \param arg The endpoint that is attempting to subscribe. * \param flags Unused. * \retval 0 Endpoint is a candidate to subscribe to MWI on the AOR. * \retval -1 The endpoint cannot subscribe to MWI on the AOR. */ static int mwi_validate_for_aor(void *obj, void *arg, int flags) { struct ast_sip_aor *aor = obj; struct ast_sip_endpoint *endpoint = arg; char *mailboxes; char *mailbox; if (ast_strlen_zero(aor->mailboxes)) { return 0; } mailboxes = ast_strdupa(aor->mailboxes); while ((mailbox = strsep(&mailboxes, ","))) { if (endpoint_receives_unsolicited_mwi_for_mailbox(endpoint, mailbox)) { ast_log(LOG_NOTICE, "Endpoint '%s' already configured for unsolicited MWI for mailbox '%s'. " "Denying MWI subscription to %s\n", ast_sorcery_object_get_id(endpoint), mailbox, ast_sorcery_object_get_id(aor)); return -1; } } return 0; } static int mwi_on_aor(void *obj, void *arg, int flags) { struct ast_sip_aor *aor = obj; struct mwi_subscription *sub = arg; char *mailboxes; char *mailbox; if (ast_strlen_zero(aor->mailboxes)) { return 0; } mailboxes = ast_strdupa(aor->mailboxes); while ((mailbox = strsep(&mailboxes, ","))) { struct mwi_stasis_subscription *mwi_stasis_sub; mwi_stasis_sub = mwi_stasis_subscription_alloc(mailbox, sub); if (!mwi_stasis_sub) { continue; } ao2_link(sub->stasis_subs, mwi_stasis_sub); ao2_ref(mwi_stasis_sub, -1); } return 0; } static struct mwi_subscription *mwi_create_subscription( struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub) { struct mwi_subscription *sub = mwi_subscription_alloc(endpoint, 1, sip_sub); if (!sub) { return NULL; } if (add_mwi_datastore(sub)) { ast_log(LOG_WARNING, "Unable to add datastore for MWI subscription to %s\n", sub->id); ao2_ref(sub, -1); return NULL; } return sub; } static struct mwi_subscription *mwi_subscribe_single( struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub, const char *name) { struct ast_sip_aor *aor; struct mwi_subscription *sub; aor = ast_sip_location_retrieve_aor(name); if (!aor) { /*! I suppose it's possible for the AOR to disappear on us * between accepting the subscription and sending the first * NOTIFY... */ ast_log(LOG_WARNING, "Unable to locate aor %s. MWI subscription failed.\n", name); return NULL; } sub = mwi_create_subscription(endpoint, sip_sub); if (sub) { mwi_on_aor(aor, sub, 0); } ao2_ref(aor, -1); return sub; } static struct mwi_subscription *mwi_subscribe_all( struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub) { struct mwi_subscription *sub; sub = mwi_create_subscription(endpoint, sip_sub); if (!sub) { return NULL; } ast_sip_for_each_aor(endpoint->aors, mwi_on_aor, sub); return sub; } static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint, const char *resource) { RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup); if (ast_strlen_zero(resource)) { if (ast_sip_for_each_aor(endpoint->aors, mwi_validate_for_aor, endpoint)) { return 500; } return 200; } aor = ast_sip_location_retrieve_aor(resource); if (!aor) { ast_log(LOG_WARNING, "Unable to locate aor %s. MWI subscription failed.\n", resource); return 404; } if (ast_strlen_zero(aor->mailboxes)) { ast_log(LOG_NOTICE, "AOR %s has no configured mailboxes. MWI subscription failed.\n", resource); return 404; } if (mwi_validate_for_aor(aor, endpoint, 0)) { return 500; } return 200; }