mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-29 15:36:13 +00:00 
			
		
		
		
	Merge "res_pjsip_mwi: Set up unsolicited MWI upon registration."
This commit is contained in:
		| @@ -168,6 +168,8 @@ struct ast_sip_contact { | ||||
| 	int authenticate_qualify; | ||||
| 	/*! Qualify timeout. 0 is diabled. */ | ||||
| 	double qualify_timeout; | ||||
| 	/*! Endpoint that added the contact, only available in observers */ | ||||
| 	struct ast_sip_endpoint *endpoint; | ||||
| }; | ||||
|  | ||||
| #define CONTACT_STATUS "contact_status" | ||||
| @@ -952,12 +954,14 @@ struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_na | ||||
|  * \param expiration_time Optional expiration time of the contact | ||||
|  * \param path_info Path information | ||||
|  * \param user_agent User-Agent header from REGISTER request | ||||
|  * \param endpoint The endpoint that resulted in the contact being added | ||||
|  * | ||||
|  * \retval -1 failure | ||||
|  * \retval 0 success | ||||
|  */ | ||||
| int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, | ||||
| 	struct timeval expiration_time, const char *path_info, const char *user_agent); | ||||
| 	struct timeval expiration_time, const char *path_info, const char *user_agent, | ||||
| 	struct ast_sip_endpoint *endpoint); | ||||
|  | ||||
| /*! | ||||
|  * \brief Update a contact | ||||
|   | ||||
| @@ -53,6 +53,7 @@ static void contact_destroy(void *obj) | ||||
| 	struct ast_sip_contact *contact = obj; | ||||
|  | ||||
| 	ast_string_field_free_memory(contact); | ||||
| 	ao2_cleanup(contact->endpoint); | ||||
| } | ||||
|  | ||||
| /*! \brief Allocator for contact */ | ||||
| @@ -228,7 +229,8 @@ struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_na | ||||
| } | ||||
|  | ||||
| int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, | ||||
| 		struct timeval expiration_time, const char *path_info, const char *user_agent) | ||||
| 		struct timeval expiration_time, const char *path_info, const char *user_agent, | ||||
| 		struct ast_sip_endpoint *endpoint) | ||||
| { | ||||
| 	char name[MAX_OBJECT_FIELD * 2 + 3]; | ||||
| 	RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); | ||||
| @@ -256,6 +258,8 @@ int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, | ||||
| 		ast_string_field_set(contact, user_agent, user_agent); | ||||
| 	} | ||||
|  | ||||
| 	contact->endpoint = ao2_bump(endpoint); | ||||
|  | ||||
| 	return ast_sorcery_create(ast_sip_get_sorcery(), contact); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -40,7 +40,7 @@ | ||||
| #include "asterisk/app.h" | ||||
|  | ||||
| struct mwi_subscription; | ||||
| AO2_GLOBAL_OBJ_STATIC(unsolicited_mwi); | ||||
| static struct ao2_container *unsolicited_mwi; | ||||
|  | ||||
| #define STASIS_BUCKETS 13 | ||||
| #define MWI_BUCKETS 53 | ||||
| @@ -297,7 +297,7 @@ static int mwi_sub_cmp(void *obj, void *arg, int flags) | ||||
|  | ||||
| static int get_message_count(void *obj, void *arg, int flags) | ||||
| { | ||||
| 	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); | ||||
| 	struct stasis_message *msg; | ||||
| 	struct mwi_stasis_subscription *mwi_stasis = obj; | ||||
| 	struct ast_sip_message_accumulator *counter = arg; | ||||
| 	struct ast_mwi_state *mwi_state; | ||||
| @@ -310,6 +310,9 @@ static int get_message_count(void *obj, void *arg, int flags) | ||||
| 	mwi_state = stasis_message_data(msg); | ||||
| 	counter->old_msgs += mwi_state->old_msgs; | ||||
| 	counter->new_msgs += mwi_state->new_msgs; | ||||
|  | ||||
| 	ao2_ref(msg, -1); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -479,22 +482,24 @@ static int unsubscribe_stasis(void *obj, void *arg, int flags) | ||||
| 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); | ||||
| 	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); | ||||
|  | ||||
| 	ao2_ref(mwi_datastore, -1); | ||||
| } | ||||
|  | ||||
| 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); | ||||
| 	struct ast_datastore *mwi_datastore; | ||||
|  | ||||
| 	mwi_datastore = ast_sip_subscription_alloc_datastore(&mwi_ds_info, MWI_DATASTORE); | ||||
| 	if (!mwi_datastore) { | ||||
| @@ -503,6 +508,7 @@ static int add_mwi_datastore(struct mwi_subscription *sub) | ||||
| 	mwi_datastore->data = sub; | ||||
|  | ||||
| 	ast_sip_subscription_add_datastore(sub->sip_sub, mwi_datastore); | ||||
| 	ao2_ref(mwi_datastore, -1); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -517,18 +523,12 @@ static int add_mwi_datastore(struct mwi_subscription *sub) | ||||
| static int endpoint_receives_unsolicited_mwi_for_mailbox(struct ast_sip_endpoint *endpoint, | ||||
| 		const char *mailbox) | ||||
| { | ||||
| 	struct ao2_container *unsolicited = ao2_global_obj_ref(unsolicited_mwi); | ||||
| 	struct ao2_iterator *mwi_subs; | ||||
| 	struct mwi_subscription *mwi_sub; | ||||
| 	const char *endpoint_id = ast_sorcery_object_get_id(endpoint); | ||||
| 	int ret = 0; | ||||
|  | ||||
| 	if (!unsolicited) { | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	mwi_subs = ao2_find(unsolicited, endpoint_id, OBJ_SEARCH_KEY | OBJ_MULTIPLE); | ||||
| 	ao2_cleanup(unsolicited); | ||||
| 	mwi_subs = ao2_find(unsolicited_mwi, endpoint_id, OBJ_SEARCH_KEY | OBJ_MULTIPLE); | ||||
|  | ||||
| 	if (!mwi_subs) { | ||||
| 		return 0; | ||||
| @@ -597,11 +597,15 @@ static int mwi_on_aor(void *obj, void *arg, int flags) | ||||
|  | ||||
| 	mailboxes = ast_strdupa(aor->mailboxes); | ||||
| 	while ((mailbox = strsep(&mailboxes, ","))) { | ||||
| 		RAII_VAR(struct mwi_stasis_subscription *, mwi_stasis_sub, | ||||
| 				mwi_stasis_subscription_alloc(mailbox, sub), ao2_cleanup); | ||||
| 		if (mwi_stasis_sub) { | ||||
| 			ao2_link(sub->stasis_subs, mwi_stasis_sub); | ||||
| 		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; | ||||
| @@ -768,9 +772,9 @@ static void mwi_to_ami(struct ast_sip_subscription *sub, | ||||
| 		       struct ast_str **buf) | ||||
| { | ||||
| 	struct mwi_subscription *mwi_sub; | ||||
| 	RAII_VAR(struct ast_datastore *, mwi_datastore, | ||||
| 			ast_sip_subscription_get_datastore(sub, MWI_DATASTORE), ao2_cleanup); | ||||
| 	struct ast_datastore *mwi_datastore; | ||||
|  | ||||
| 	mwi_datastore = ast_sip_subscription_get_datastore(sub, MWI_DATASTORE); | ||||
| 	if (!mwi_datastore) { | ||||
| 		return; | ||||
| 	} | ||||
| @@ -781,6 +785,8 @@ static void mwi_to_ami(struct ast_sip_subscription *sub, | ||||
| 	ast_str_append(buf, 0, "Mailboxes: "); | ||||
| 	mwi_subscription_mailboxes_str(mwi_sub->stasis_subs, buf); | ||||
| 	ast_str_append(buf, 0, "\r\n"); | ||||
|  | ||||
| 	ao2_ref(mwi_datastore, -1); | ||||
| } | ||||
|  | ||||
| static int serialized_notify(void *userdata) | ||||
| @@ -840,14 +846,38 @@ static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags | ||||
| { | ||||
| 	RAII_VAR(struct mwi_subscription *, aggregate_sub, NULL, ao2_cleanup); | ||||
| 	struct ast_sip_endpoint *endpoint = obj; | ||||
| 	struct ao2_container *mwi_subscriptions = arg; | ||||
| 	char *mailboxes; | ||||
| 	char *mailbox; | ||||
| 	char *endpoint_aors, *aor_name, *mailboxes, *mailbox; | ||||
| 	struct ao2_container *contacts = NULL; | ||||
|  | ||||
| 	if (ast_strlen_zero(endpoint->subscription.mwi.mailboxes)) { | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	endpoint_aors = ast_strdupa(endpoint->aors); | ||||
|  | ||||
| 	while ((aor_name = strsep(&endpoint_aors, ","))) { | ||||
| 		RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(aor_name), ao2_cleanup); | ||||
|  | ||||
| 		if (!aor) { | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		contacts = ast_sip_location_retrieve_aor_contacts(aor); | ||||
| 		if (!contacts || (ao2_container_count(contacts) == 0)) { | ||||
| 			ao2_cleanup(contacts); | ||||
| 			contacts = NULL; | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	if (!contacts) { | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	ao2_ref(contacts, -1); | ||||
|  | ||||
| 	if (endpoint->subscription.mwi.aggregate) { | ||||
| 		aggregate_sub = mwi_subscription_alloc(endpoint, 0, NULL); | ||||
| 		if (!aggregate_sub) { | ||||
| @@ -859,18 +889,20 @@ static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags | ||||
| 	while ((mailbox = strsep(&mailboxes, ","))) { | ||||
| 		struct mwi_subscription *sub = aggregate_sub ?: | ||||
| 			mwi_subscription_alloc(endpoint, 0, NULL); | ||||
| 		RAII_VAR(struct mwi_stasis_subscription *, mwi_stasis_sub, | ||||
| 				mwi_stasis_subscription_alloc(mailbox, sub), ao2_cleanup); | ||||
| 		struct mwi_stasis_subscription *mwi_stasis_sub; | ||||
|  | ||||
| 		mwi_stasis_sub = mwi_stasis_subscription_alloc(mailbox, sub); | ||||
| 		if (mwi_stasis_sub) { | ||||
| 			ao2_link(sub->stasis_subs, mwi_stasis_sub); | ||||
| 			ao2_ref(mwi_stasis_sub, -1); | ||||
| 		} | ||||
| 		if (!aggregate_sub) { | ||||
| 			ao2_link(mwi_subscriptions, sub); | ||||
| 			ao2_cleanup(sub); | ||||
| 		if (!aggregate_sub && sub) { | ||||
| 			ao2_link_flags(unsolicited_mwi, sub, OBJ_NOLOCK); | ||||
| 			ao2_ref(sub, -1); | ||||
| 		} | ||||
| 	} | ||||
| 	if (aggregate_sub) { | ||||
| 		ao2_link(mwi_subscriptions, aggregate_sub); | ||||
| 		ao2_link_flags(unsolicited_mwi, aggregate_sub, OBJ_NOLOCK); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| @@ -886,13 +918,11 @@ static int unsubscribe(void *obj, void *arg, int flags) | ||||
|  | ||||
| static void create_mwi_subscriptions(void) | ||||
| { | ||||
| 	struct ao2_container *mwi_subscriptions = ao2_container_alloc(MWI_BUCKETS, mwi_sub_hash, mwi_sub_cmp); | ||||
| 	RAII_VAR(struct ao2_container *, old_mwi_subscriptions, ao2_global_obj_ref(unsolicited_mwi), ao2_cleanup); | ||||
| 	RAII_VAR(struct ao2_container *, endpoints, ast_sorcery_retrieve_by_fields( | ||||
| 				ast_sip_get_sorcery(), "endpoint", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL), | ||||
| 			ao2_cleanup); | ||||
| 	struct ao2_container *endpoints; | ||||
|  | ||||
| 	if (!mwi_subscriptions) { | ||||
| 	endpoints = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "endpoint", | ||||
| 		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); | ||||
| 	if (!endpoints) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| @@ -902,12 +932,12 @@ static void create_mwi_subscriptions(void) | ||||
| 	 * and resubscribing, up-to-date mailbox state will be sent out to the endpoint when the | ||||
| 	 * new stasis subscription is established | ||||
| 	 */ | ||||
| 	if (old_mwi_subscriptions) { | ||||
| 		ao2_callback(old_mwi_subscriptions, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL); | ||||
| 	} | ||||
| 	ao2_callback(endpoints, OBJ_NODATA, create_mwi_subscriptions_for_endpoint, mwi_subscriptions); | ||||
| 	ao2_global_obj_replace_unref(unsolicited_mwi, mwi_subscriptions); | ||||
| 	ao2_ref(mwi_subscriptions, -1); | ||||
| 	ao2_lock(unsolicited_mwi); | ||||
| 	ao2_callback(unsolicited_mwi, OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL); | ||||
| 	ao2_callback(endpoints, OBJ_NODATA, create_mwi_subscriptions_for_endpoint, NULL); | ||||
| 	ao2_unlock(unsolicited_mwi); | ||||
|  | ||||
| 	ao2_ref(endpoints, -1); | ||||
| } | ||||
|  | ||||
| /*! \brief Function called to send MWI NOTIFY on any unsolicited mailboxes relating to this AOR */ | ||||
| @@ -927,40 +957,56 @@ static int send_contact_notify(void *obj, void *arg, int flags) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /*! \brief Function called when a contact is created or updated */ | ||||
| static void mwi_contact_changed_observer(const void *object) | ||||
| /*! \brief Function called when a contact is updated */ | ||||
| static void mwi_contact_updated(const void *object) | ||||
| { | ||||
| 	char *id = ast_strdupa(ast_sorcery_object_get_id(object)), *aor = NULL; | ||||
| 	struct ao2_container *mwi_subscriptions = ao2_global_obj_ref(unsolicited_mwi); | ||||
|  | ||||
| 	if (!mwi_subscriptions) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	aor = strsep(&id, ";@"); | ||||
|  | ||||
| 	ao2_callback(mwi_subscriptions, OBJ_NODATA, send_contact_notify, aor); | ||||
| 	ao2_ref(mwi_subscriptions, -1); | ||||
| 	ao2_callback(unsolicited_mwi, OBJ_NODATA, send_contact_notify, aor); | ||||
| } | ||||
|  | ||||
| /*! \brief Function called when a contact is added */ | ||||
| static void mwi_contact_added(const void *object) | ||||
| { | ||||
| 	const struct ast_sip_contact *contact = object; | ||||
| 	struct ao2_iterator *mwi_subs; | ||||
| 	struct mwi_subscription *mwi_sub; | ||||
| 	const char *endpoint_id = ast_sorcery_object_get_id(contact->endpoint); | ||||
|  | ||||
| 	if (ast_strlen_zero(contact->endpoint->subscription.mwi.mailboxes)) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	ao2_lock(unsolicited_mwi); | ||||
|  | ||||
| 	mwi_subs = ao2_find(unsolicited_mwi, endpoint_id, OBJ_SEARCH_KEY | OBJ_MULTIPLE | OBJ_NOLOCK | OBJ_UNLINK); | ||||
|  | ||||
| 	if (mwi_subs) { | ||||
| 		for (; (mwi_sub = ao2_iterator_next(mwi_subs)); ao2_cleanup(mwi_sub)) { | ||||
| 			unsubscribe(mwi_sub, NULL, 0); | ||||
| 		} | ||||
| 		ao2_iterator_destroy(mwi_subs); | ||||
| 	} | ||||
|  | ||||
| 	create_mwi_subscriptions_for_endpoint(contact->endpoint, NULL, 0); | ||||
|  | ||||
| 	ao2_unlock(unsolicited_mwi); | ||||
|  | ||||
| 	mwi_contact_updated(object); | ||||
| } | ||||
|  | ||||
| /*! \brief Observer for contacts so unsolicited MWI is sent when a contact changes */ | ||||
| static const struct ast_sorcery_observer mwi_contact_observer = { | ||||
| 	.created = mwi_contact_changed_observer, | ||||
| 	.updated = mwi_contact_changed_observer, | ||||
| 	.created = mwi_contact_added, | ||||
| 	.updated = mwi_contact_updated, | ||||
| }; | ||||
|  | ||||
| /*! \brief Task invoked to send initial MWI NOTIFY for unsolicited */ | ||||
| static int send_initial_notify_all(void *obj) | ||||
| { | ||||
| 	struct ao2_container *mwi_subscriptions = ao2_global_obj_ref(unsolicited_mwi); | ||||
|  | ||||
| 	if (!mwi_subscriptions) { | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	ao2_callback(mwi_subscriptions, OBJ_NODATA, send_notify, NULL); | ||||
| 	ao2_ref(mwi_subscriptions, -1); | ||||
| 	ao2_callback(unsolicited_mwi, OBJ_NODATA, send_notify, NULL); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
| @@ -1000,6 +1046,13 @@ static int load_module(void) | ||||
| 	if (ast_sip_register_subscription_handler(&mwi_handler)) { | ||||
| 		return AST_MODULE_LOAD_DECLINE; | ||||
| 	} | ||||
|  | ||||
| 	unsolicited_mwi = ao2_container_alloc(MWI_BUCKETS, mwi_sub_hash, mwi_sub_cmp); | ||||
| 	if (!unsolicited_mwi) { | ||||
| 		ast_sip_unregister_subscription_handler(&mwi_handler); | ||||
| 		return AST_MODULE_LOAD_DECLINE; | ||||
| 	} | ||||
|  | ||||
| 	create_mwi_subscriptions(); | ||||
| 	ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &mwi_contact_observer); | ||||
|  | ||||
| @@ -1014,12 +1067,9 @@ static int load_module(void) | ||||
|  | ||||
| static int unload_module(void) | ||||
| { | ||||
| 	RAII_VAR(struct ao2_container *, mwi_subscriptions, ao2_global_obj_ref(unsolicited_mwi), ao2_cleanup); | ||||
| 	if (mwi_subscriptions) { | ||||
| 		ao2_callback(mwi_subscriptions, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL); | ||||
| 		ao2_global_obj_release(unsolicited_mwi); | ||||
| 	ao2_callback(unsolicited_mwi, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL); | ||||
| 	ao2_ref(unsolicited_mwi, -1); | ||||
| 	ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &mwi_contact_observer); | ||||
| 	} | ||||
| 	ast_sip_unregister_subscription_handler(&mwi_handler); | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
| @@ -500,7 +500,7 @@ static int rx_task(void *data) | ||||
|  | ||||
| 			if (ast_sip_location_add_contact(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(), | ||||
| 				ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL, | ||||
| 					user_agent)) { | ||||
| 					user_agent, task_data->endpoint)) { | ||||
| 				ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n", | ||||
| 						contact_uri, aor_name); | ||||
| 				continue; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user