[svn-commits] kharwell: branch kharwell/pimp_sip_qualify r390173 - in /team/kharwell/pimp_s...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu May 30 18:14:38 CDT 2013


Author: kharwell
Date: Thu May 30 18:14:36 2013
New Revision: 390173

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=390173
Log:
most of the code is in and somewhat tested. currently crashing though on scheduler remove

Modified:
    team/kharwell/pimp_sip_qualify/include/asterisk/res_sip.h
    team/kharwell/pimp_sip_qualify/res/res_sip.c
    team/kharwell/pimp_sip_qualify/res/res_sip/location.c
    team/kharwell/pimp_sip_qualify/res/res_sip/sip_configuration.c
    team/kharwell/pimp_sip_qualify/res/res_sip/sip_options.c

Modified: team/kharwell/pimp_sip_qualify/include/asterisk/res_sip.h
URL: http://svnview.digium.com/svn/asterisk/team/kharwell/pimp_sip_qualify/include/asterisk/res_sip.h?view=diff&rev=390173&r1=390172&r2=390173
==============================================================================
--- team/kharwell/pimp_sip_qualify/include/asterisk/res_sip.h (original)
+++ team/kharwell/pimp_sip_qualify/include/asterisk/res_sip.h Thu May 30 18:14:36 2013
@@ -137,6 +137,8 @@
 	);
 	/*! Absolute time that this contact is no longer valid after */
 	struct timeval expiration_time;
+	/*! Frequency to send OPTIONS requests to contact. 0 is disabled. */
+	unsigned int qualify_frequency;
 };
 
 /*!
@@ -166,6 +168,8 @@
 	unsigned int maximum_expiration;
 	/*! Default contact expiration if one is not provided in the contact */
 	unsigned int default_expiration;
+	/*! Frequency to send OPTIONS requests to AOR contacts. 0 is disabled. */
+	unsigned int qualify_frequency;
 	/*! Maximum number of external contacts, 0 to disable */
 	unsigned int max_contacts;
 	/*! Whether to remove any existing contacts not related to an incoming REGISTER when it comes in */
@@ -326,8 +330,6 @@
 	unsigned int sess_expires;
 	/*! List of outbound registrations */
 	AST_LIST_HEAD_NOLOCK(, ast_sip_registration) registrations;
-	/*! Frequency to send OPTIONS requests to endpoint. 0 is disabled. */
-	unsigned int qualify_frequency;
 	/*! Method(s) by which the endpoint should be identified. */
 	enum ast_sip_endpoint_identifier_type ident_method;
 	/*! Boolean indicating if direct_media is permissible */
@@ -579,6 +581,16 @@
 int ast_sip_initialize_sorcery_transport(struct ast_sorcery *sorcery);
 
 /*!
+ * \brief Initialize qualify support on a sorcery instance
+ *
+ * \param sorcery The sorcery instance
+ *
+ * \retval -1 failure
+ * \retval 0 success
+ */
+int ast_sip_initialize_sorcery_qualify(struct ast_sorcery *sorcery);
+
+/*!
  * \brief Initialize location support on a sorcery instance
  *
  * \param sorcery The sorcery instance

Modified: team/kharwell/pimp_sip_qualify/res/res_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/kharwell/pimp_sip_qualify/res/res_sip.c?view=diff&rev=390173&r1=390172&r2=390173
==============================================================================
--- team/kharwell/pimp_sip_qualify/res/res_sip.c (original)
+++ team/kharwell/pimp_sip_qualify/res/res_sip.c Thu May 30 18:14:36 2013
@@ -1005,6 +1005,11 @@
 	pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
 
 	if (ast_strlen_zero(uri)) {
+		if (!endpoint) {
+			ast_log(LOG_ERROR, "An endpoint and/or uri must be specified\n");
+			return -1;
+		}
+
 		contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
 		if (!contact || ast_strlen_zero(contact->uri)) {
 			ast_log(LOG_ERROR, "Unable to retrieve contact for endpoint %s\n",
@@ -1017,10 +1022,12 @@
 		pj_cstr(&remote_uri, uri);
 	}
 
-	if (sip_get_tpselector_from_endpoint(endpoint, &selector)) {
-		ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport selector for endpoint %s\n",
+	if (endpoint) {
+		if (sip_get_tpselector_from_endpoint(endpoint, &selector)) {
+			ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport selector for endpoint %s\n",
 				ast_sorcery_object_get_id(endpoint));
-		return -1;
+			return -1;
+		}
 	}
 
 	pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Outbound request", 256, 256);

Modified: team/kharwell/pimp_sip_qualify/res/res_sip/location.c
URL: http://svnview.digium.com/svn/asterisk/team/kharwell/pimp_sip_qualify/res/res_sip/location.c?view=diff&rev=390173&r1=390172&r2=390173
==============================================================================
--- team/kharwell/pimp_sip_qualify/res/res_sip/location.c (original)
+++ team/kharwell/pimp_sip_qualify/res/res_sip/location.c Thu May 30 18:14:36 2013
@@ -235,6 +235,7 @@
 
 	ast_string_field_set(contact, uri, uri);
 	contact->expiration_time = expiration_time;
+	contact->qualify_frequency = aor->qualify_frequency;
 
 	return ast_sorcery_create(ast_sip_get_sorcery(), contact);
 }
@@ -299,6 +300,7 @@
 	ast_sorcery_object_field_register(sorcery, "aor", "minimum_expiration", "60", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, minimum_expiration));
 	ast_sorcery_object_field_register(sorcery, "aor", "maximum_expiration", "7200", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, maximum_expiration));
 	ast_sorcery_object_field_register(sorcery, "aor", "default_expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, default_expiration));
+	ast_sorcery_object_field_register(sorcery, "aor", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_aor, qualify_frequency), 0, 86400);
 	ast_sorcery_object_field_register(sorcery, "aor", "max_contacts", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, max_contacts));
 	ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing));
 	ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, NULL, 0, 0);

Modified: team/kharwell/pimp_sip_qualify/res/res_sip/sip_configuration.c
URL: http://svnview.digium.com/svn/asterisk/team/kharwell/pimp_sip_qualify/res/res_sip/sip_configuration.c?view=diff&rev=390173&r1=390172&r2=390173
==============================================================================
--- team/kharwell/pimp_sip_qualify/res/res_sip/sip_configuration.c (original)
+++ team/kharwell/pimp_sip_qualify/res/res_sip/sip_configuration.c Thu May 30 18:14:36 2013
@@ -335,7 +335,6 @@
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "context", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, context));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "disallow", "", OPT_CODEC_T, 0, FLDSET(struct ast_sip_endpoint, prefs, codecs));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow", "", OPT_CODEC_T, 1, FLDSET(struct ast_sip_endpoint, prefs, codecs));
-	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_endpoint, qualify_frequency), 0, 86400);
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtmfmode", "rfc4733", dtmf_handler, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rtp_ipv6));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_symmetric", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rtp_symmetric));
@@ -388,6 +387,13 @@
 		return -1;
 	}
 
+	if (ast_sip_initialize_sorcery_qualify(sip_sorcery)) {
+		ast_log(LOG_ERROR, "Failed to register SIP qualify support with sorcery\n");
+		ast_sorcery_unref(sip_sorcery);
+		sip_sorcery = NULL;
+		return -1;
+	}
+
 	if (ast_sip_initialize_sorcery_domain_alias(sip_sorcery)) {
 		ast_log(LOG_ERROR, "Failed to register SIP domain aliases support with sorcery\n");
 		ast_sorcery_unref(sip_sorcery);

Modified: team/kharwell/pimp_sip_qualify/res/res_sip/sip_options.c
URL: http://svnview.digium.com/svn/asterisk/team/kharwell/pimp_sip_qualify/res/res_sip/sip_options.c?view=diff&rev=390173&r1=390172&r2=390173
==============================================================================
--- team/kharwell/pimp_sip_qualify/res/res_sip/sip_options.c (original)
+++ team/kharwell/pimp_sip_qualify/res/res_sip/sip_options.c Thu May 30 18:14:36 2013
@@ -1,8 +1,19 @@
 /*
- * sip_options.c
+ * Asterisk -- An open source telephony toolkit.
  *
- *  Created on: Jan 25, 2013
- *      Author: mjordan
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Matt Jordan <mjordan at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
  */
 
 #include "asterisk.h"
@@ -16,41 +27,424 @@
 #include "asterisk/pbx.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/cli.h"
+#include "asterisk/time.h"
 #include "include/res_sip_private.h"
 
 #define DEFAULT_LANGUAGE "en"
 #define DEFAULT_ENCODING "text/plain"
 #define QUALIFIED_BUCKETS 211
-
-/*! \brief Scheduling context for qualifies */
-static struct ast_sched_context *sched; /* XXX move this to registrar */
-
-struct ao2_container *scheduled_qualifies;
-
-struct qualify_info {
-	AST_DECLARE_STRING_FIELDS(
-		AST_STRING_FIELD(endpoint_id);
-	);
-	char *scheduler_data;
-	int scheduler_id;
+#define CONTACT_STATUS "contact_status"
+
+static int qualify_contact(struct ast_sip_contact *contact);
+
+/*!
+ * \internal
+ * \brief A contact's status.
+ *
+ * \detail Maintains a contact's current status and round trip time
+ *         if available.
+ */
+struct contact_status {
+	SORCERY_OBJECT(details);
+	/*! 0 = unavailable, 1 = available (default) */
+	int status;
+	/*! The start time set before sending a qualify request */
+	struct timeval start;
+	/*! The round trip time in microseconds */
+	int64_t rtt;
 };
 
-static pj_bool_t options_module_start(void);
-static pj_bool_t options_module_stop(void);
-static pj_bool_t options_module_on_rx_request(pjsip_rx_data *rdata);
-static pj_bool_t options_module_on_rx_response(pjsip_rx_data *rdata);
-
-static pjsip_module options_module = {
-	.name = {"Options Module", 14},
-	.id = -1,
-	.priority = PJSIP_MOD_PRIORITY_APPLICATION,
-	.start = options_module_start,
-	.stop = options_module_stop,
-	.on_rx_request = options_module_on_rx_request,
-	.on_rx_response = options_module_on_rx_response,
+/*!
+ * \internal
+ * \brief Create a contact_status object.
+ */
+static void *contact_status_alloc(const char *name)
+{
+	struct contact_status *status = ao2_alloc_options(
+		sizeof(*status), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+
+	if (!status) {
+		ast_log(LOG_ERROR, "Unable to allocate contact_status\n");
+		return NULL;
+	}
+
+	status->status = 0;
+	status->start = ast_tv(0, 0);
+	status->rtt = 0;
+
+	return status;
+}
+
+/*!
+ * \internal
+ * \brief Retrieve a contact_status object from sorcery creating
+ *        one if not found.
+ */
+static struct contact_status *get_contact_status(const struct ast_sip_contact *contact)
+{
+	const char *id = ast_sorcery_object_get_id(contact);
+
+	struct contact_status *status = ast_sorcery_retrieve_by_id(
+		ast_sip_get_sorcery(), CONTACT_STATUS, id);
+
+	if (status) {
+		return status;
+	}
+
+	if (!(status = ast_sorcery_alloc(
+		      ast_sip_get_sorcery(), CONTACT_STATUS,
+		      ast_sorcery_object_get_id(contact)))) {
+
+		ast_log(LOG_ERROR, "Unable to create contact_status for contact %s\n",
+			contact->uri);
+		return NULL;
+	}
+
+	if (ast_sorcery_create(ast_sip_get_sorcery(), status)) {
+		ast_log(LOG_ERROR, "Unable to persist contact_status for contact %s\n",
+			contact->uri);
+		return NULL;
+	}
+
+	return status;
+}
+
+/*!
+ * \internal
+ * \brief Set a contact_status's status to the given value.
+ */
+static void update_contact_status(const struct ast_sip_contact *contact, int value)
+{
+	RAII_VAR(struct contact_status *, status,
+		 get_contact_status(contact), ao2_cleanup);
+
+	RAII_VAR(struct contact_status *, copy, ast_sorcery_copy(
+			 ast_sip_get_sorcery(), status), ao2_cleanup);
+
+	if (!copy) {
+		ast_log(LOG_ERROR, "Unable to copy contact_status for contact %s\n",
+			contact->uri);
+		return;
+	}
+
+	copy->status = value;
+
+	if (copy->status) {
+		copy->rtt = ast_tvdiff_us(ast_tvnow(), status->start);
+		copy->start = ast_tv(0, 0);
+	} else {
+		copy->start = ast_tvnow();
+		copy->rtt = 0;
+	}
+
+	if (ast_sorcery_update(ast_sip_get_sorcery(), copy)) {
+		ast_log(LOG_ERROR, "Unable to update contact_status for contact %s\n",
+			contact->uri);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Send qualify request to the given contact.
+ */
+static int on_contact(void *obj, void *arg, int flags)
+{
+	qualify_contact(obj);
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief For an endpoint iterate over aors/contacts
+ *
+ * \detail For the given endpoint either match on a contact (if one
+ *         is given) or qualify all contacts found.
+ */
+static int on_endpoint(void *obj, void *arg, int flags)
+{
+	struct ast_sip_endpoint *endpoint = obj;
+	char *aor_name, *aors;
+
+	if (ast_strlen_zero(endpoint->aors)) {
+		return 0;
+	}
+
+	aors = ast_strdupa(endpoint->aors);
+
+	while ((aor_name = strsep(&aors, ","))) {
+		RAII_VAR(struct ast_sip_aor *, aor,
+			 ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
+		RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
+
+		if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
+			continue;
+		}
+		
+		if (arg && ao2_find(contacts, arg, OBJ_NODATA | OBJ_POINTER)) {
+			return CMP_MATCH;
+		} else {
+			ao2_callback(contacts, OBJ_NODATA, on_contact, NULL);
+		}
+	}
+
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Find endpoints associated with the given contact.
+ */
+static struct ao2_container *find_endpoints(struct ast_sip_contact *contact)
+{
+	RAII_VAR(struct ao2_container *, endpoints,
+		 ast_res_sip_get_endpoints(), ao2_cleanup);
+
+	return ao2_callback(endpoints, OBJ_MULTIPLE, on_endpoint, contact);
+}
+
+/*!
+ * \internal
+ * \brief Receive an response to the qualify contact request.
+ */
+static void qualify_contact_cb(void *token, pjsip_event *e)
+{
+	RAII_VAR(struct ast_sip_contact *, contact, token, ao2_cleanup);
+	RAII_VAR(struct ao2_container *, endpoints, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
+
+	pjsip_transaction *tsx = e->body.tsx_state.tsx;
+	pjsip_rx_data *challenge = e->body.tsx_state.src.rdata;
+	pjsip_tx_data *tdata;
+
+	ast_log(LOG_VERBOSE, "received qualify contact callback %s\n", contact->uri);
+	if (tsx->status_code != 401 && tsx->status_code != 407) {
+		ast_log(LOG_VERBOSE, "update contact callback %s\n", contact->uri);
+		update_contact_status(contact, 1);
+		return;
+	}
+
+	/* try to find endpoints that are associated with the contact */
+	if (!(endpoints = find_endpoints(contact))) {
+		ast_log(LOG_ERROR, "No endpoints found for contact %s, cannot authenticate",
+			contact->uri);
+		return;
+	}
+
+	/* find "first" endpoint in order to authenticate - actually any
+	   endpoint should do that matched on the contact */
+	endpoint = ao2_callback(endpoints, 0, NULL, NULL);
+
+	if (!ast_sip_create_request_with_auth(endpoint->sip_outbound_auths,
+					      endpoint->num_outbound_auths,
+					      challenge, tsx, &tdata)) {
+		pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata,
+					 -1, NULL, NULL);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Attempt to qualify the contact
+ *
+ * \detail Sends a SIP OPTIONS request to the given contact in order to make
+ *         sure that contact is available.
+ */
+static int qualify_contact(struct ast_sip_contact *contact)
+{
+	pjsip_tx_data *tdata;
+	ast_log(LOG_VERBOSE, "QUALIFY CONTACT %s\n", contact->uri);
+	/* assume we can't connect so if no reply comes back */
+	update_contact_status(contact, 0);
+	ast_log(LOG_VERBOSE, "updated status sending OPTIONS %s\n", contact->uri);
+
+	if (ast_sip_create_request("OPTIONS", NULL, NULL, contact->uri, &tdata)) {
+		ast_log(LOG_ERROR, "Unable to create request to qualify contact %s\n",
+			contact->uri);
+		return -1;
+	}
+
+	if (pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(),
+				     tdata, -1, contact, qualify_contact_cb) != PJ_SUCCESS) {
+		pjsip_tx_data_dec_ref(tdata);
+		ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n",
+			contact->uri);
+		return -1;
+	}
+
+	ao2_ref(contact, +1);
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Scheduling context for sending QUALIFY request at specified intervals.
+ */
+static struct ast_sched_context *sched;
+
+/*!
+ * \internal
+ * \brief Container to hold all actively scheduled qualifies.
+ */
+static struct ao2_container *sched_qualifies;
+
+/*!
+ * \internal
+ * \brief Structure to hold qualify contact scheduling information.
+ */
+struct sched_data {
+	/*! The contact being checked */
+	struct ast_sip_contact *contact;
+	/*! The scheduling id */
+	int id;
 };
 
-static pj_bool_t options_module_start(void)
+/*!
+ * \internal
+ * \brief Destroy the scheduled data and remove from scheduler.
+ */
+static void sched_data_destructor(void *obj)
+{
+	struct sched_data *data = obj;
+
+	ast_log(LOG_VERBOSE, "removing from scheduler %s\n", data->contact->uri);	
+	AST_SCHED_DEL(sched, data->id);
+	ao2_cleanup(data->contact);
+}
+
+/*!
+ * \internal
+ * \brief Send a qualify contact request within a threaded task.
+ */
+static int qualify_contact_task(void *obj)
+{
+	RAII_VAR(struct ast_sip_contact *, contact, obj, ao2_cleanup);
+	ast_log(LOG_VERBOSE, "qualify contact task\n");
+	return qualify_contact(contact);
+}
+
+/*!
+ * \internal
+ * \brief Send a scheduled qualify contact request.
+ */
+static int qualify_contact_sched(const void *obj)
+{
+	struct sched_data *data = (struct sched_data *)obj;
+	ao2_ref(data->contact, +1);
+	ast_log(LOG_VERBOSE, "qualify contact sched %s\n", data->contact->uri);
+	return !ast_sip_push_task(
+		NULL, qualify_contact_task, data->contact);
+}
+
+/*!
+ * \internal
+ * \brief Set up a scheduled qualify contact check.
+ */
+static void schedule_qualify(struct ast_sip_contact *contact)
+{
+	RAII_VAR(struct sched_data *, data,  ao2_alloc(
+			 sizeof(*data), sched_data_destructor), ao2_cleanup);
+
+	if (!data) {
+		ast_log(LOG_ERROR,
+			"Unable to create schedule qualify data\n");
+
+		return;
+	}
+
+	ast_log(LOG_VERBOSE, "schedule a qualify %s\n", contact->uri);	
+	data->contact = contact;
+	if ((data->id = ast_sched_add(
+		sched, contact->qualify_frequency * 1000,
+		qualify_contact_sched, data) <= 0)) {
+
+		ast_log(LOG_ERROR, "Unable to schedule qualify for contact %s\n",
+			contact->uri);
+		return;
+	}
+
+	ast_log(LOG_VERBOSE, "add to container %s\n", contact->uri);	
+	ao2_ref(data->contact, +1);
+	ao2_link(sched_qualifies, data);
+}
+
+/*!
+ * \internal
+ * \brief A new contact has been created make sure it is available.
+ */
+static void contact_created(const void *obj)
+{
+	struct ast_sip_contact *contact = (struct ast_sip_contact *)obj;
+
+	ast_log(LOG_VERBOSE, "creating contact %s, freq = %d\n", contact->uri, contact->qualify_frequency);
+	ao2_ref(contact, +1);
+	ast_sip_push_task(NULL, qualify_contact_task, contact);
+
+	if (contact->qualify_frequency) {
+		ast_log(LOG_VERBOSE, "schedule contact %s\n", contact->uri);
+		schedule_qualify(contact);
+	}
+}
+
+/*!
+ * \internal
+ * \brief A contact has been updated make sure it is still available.
+ */
+static void contact_updated(const void *obj)
+{
+	struct ast_sip_contact *contact = (struct ast_sip_contact *)obj;
+
+	/* see if there was a scheduled check for the contact */
+	struct sched_data *data =
+		ao2_find(sched_qualifies, contact, OBJ_UNLINK);
+
+	ast_log(LOG_VERBOSE, "Updating contact %s\n", contact->uri);
+	if (data) {
+		/* if it exists prepare for a re-schedule */
+		ast_log(LOG_VERBOSE, "found in sched about to REMOVE!!! %s\n", contact->uri);
+		ao2_ref(data, -1);
+	}
+
+	ao2_ref(contact, +1);
+	ast_sip_push_task(NULL, qualify_contact_task, contact);
+
+	if (contact->qualify_frequency) {
+		ast_log(LOG_VERBOSE, "REgister in sched!!! %s\n", contact->uri);
+		schedule_qualify(contact);
+	}
+}
+
+/*!
+ * \internal
+ * \brief A contact has been deleted remove status tracking.
+ */
+static void contact_deleted(const void *obj)
+{
+	struct ast_sip_contact *contact = (struct ast_sip_contact *)obj;
+
+	RAII_VAR(struct contact_status *, status,
+		 get_contact_status(contact), ao2_cleanup);
+
+	struct sched_data *data =
+		ao2_find(sched_qualifies, contact, OBJ_UNLINK);
+
+	if (data) {
+		/* if scheduled remove from scheduler */
+		ao2_ref(data, -1);
+	}
+
+	if (ast_sorcery_delete(ast_sip_get_sorcery(), status)) {
+		ast_log(LOG_ERROR, "Unable to delete contact_stats for contact %s\n",
+			contact->uri);
+	}
+}
+
+struct ast_sorcery_observer contact_observer = {
+	.created = contact_created,
+	.updated = contact_updated,
+	.deleted = contact_deleted,
+};
+
+static pj_bool_t options_start(void)
 {
 	if (!(sched = ast_sched_context_create()) ||
 	    ast_sched_start_thread(sched)) {
@@ -60,9 +454,11 @@
 	return PJ_SUCCESS;
 }
 
-static pj_bool_t options_module_stop(void)
-{
-	ao2_t_ref(scheduled_qualifies, -1, "Remove scheduled qualifies on module stop");
+static pj_bool_t options_stop(void)
+{
+	ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_observer);
+
+	ao2_t_ref(sched_qualifies, -1, "Remove scheduled qualifies on module stop");
 
 	if (sched) {
 		ast_sched_context_destroy(sched);
@@ -71,18 +467,20 @@
 	return PJ_SUCCESS;
 }
 
-static pj_status_t send_options_response(pjsip_rx_data *rdata, pjsip_dialog *pj_dlg, int code)
+static pj_status_t send_options_response(pjsip_rx_data *rdata, int code)
 {
 	pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
-	pjsip_transaction *pj_trans = pjsip_rdata_get_tsx(rdata);
+	pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
+	pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata);
 	pjsip_tx_data *tdata;
 	const pjsip_hdr *hdr;
 	pjsip_response_addr res_addr;
 	pj_status_t status;
 
 	/* Make the response object */
-	status = pjsip_endpt_create_response(endpt, rdata, code, NULL, &tdata);
-	if (status != PJ_SUCCESS) {
+	if ((status = pjsip_endpt_create_response(
+		     endpt, rdata, code, NULL, &tdata) != PJ_SUCCESS)) {
+		ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
 		return status;
 	}
 
@@ -106,38 +504,48 @@
 	ast_sip_add_header(tdata, "Accept-Encoding", DEFAULT_ENCODING);
 	ast_sip_add_header(tdata, "Accept-Language", DEFAULT_LANGUAGE);
 
-	if (pj_dlg && pj_trans) {
-		status = pjsip_dlg_send_response(pj_dlg, pj_trans, tdata);
+	if (dlg && trans) {
+		status = pjsip_dlg_send_response(dlg, trans, tdata);
 	} else {
 		/* Get where to send request. */
-		status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
-		if (status != PJ_SUCCESS) {
+		if ((status = pjsip_get_response_addr(
+			     tdata->pool, rdata, &res_addr)) != PJ_SUCCESS) {
+			ast_log(LOG_ERROR, "Unable to get response address (%d)\n",
+				status);
+
 			pjsip_tx_data_dec_ref(tdata);
 			return status;
 		}
-		status = pjsip_endpt_send_response(endpt, &res_addr, tdata, NULL, NULL);
+		status = pjsip_endpt_send_response(endpt, &res_addr, tdata,
+						   NULL, NULL);
+	}
+
+	if (status != PJ_SUCCESS) {
+		ast_log(LOG_ERROR, "Unable to send response (%d)\n", status);
 	}
 
 	return status;
 }
 
-static pj_bool_t options_module_on_rx_request(pjsip_rx_data *rdata)
+static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
 {
 	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
-	pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
 	pjsip_uri *ruri;
 	pjsip_sip_uri *sip_ruri;
 	char exten[AST_MAX_EXTENSION];
 
-	if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_options_method)) {
+	if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
+			     &pjsip_options_method)) {
 		return PJ_FALSE;
 	}
-	endpoint = ast_pjsip_rdata_get_endpoint(rdata);
-	ast_assert(endpoint != NULL);
+
+	if (!(endpoint = ast_pjsip_rdata_get_endpoint(rdata))) {
+		return PJ_FALSE;
+	}
 
 	ruri = rdata->msg_info.msg->line.req.uri;
 	if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) {
-		send_options_response(rdata, dlg, 416);
+		send_options_response(rdata, 416);
 		return -1;
 	}
 	
@@ -145,214 +553,112 @@
 	ast_copy_pj_str(exten, &sip_ruri->user, sizeof(exten));
 
 	if (ast_shutting_down()) {
-		send_options_response(rdata, dlg, 503);
+		send_options_response(rdata, 503);
 	} else if (!ast_exists_extension(NULL, endpoint->context, exten, 1, NULL)) {
-		send_options_response(rdata, dlg, 404);
+		send_options_response(rdata, 404);
 	} else {
-		send_options_response(rdata, dlg, 200);
+		send_options_response(rdata, 200);
 	}
 	return PJ_TRUE;
 }
 
-static pj_bool_t options_module_on_rx_response(pjsip_rx_data *rdata)
-{
-
-	return PJ_SUCCESS;
-}
-
-static int qualify_info_hash_fn(const void *obj, int flags)
-{
-	const struct qualify_info *info = obj;
-	const char *endpoint_id = flags & OBJ_KEY ? obj : info->endpoint_id;
-
-	return ast_str_hash(endpoint_id);
-}
-
-static int qualify_info_cmp_fn(void *obj, void *arg, int flags)
-{
-	struct qualify_info *left = obj;
-	struct qualify_info *right = arg;
-	const char *right_endpoint_id = flags & OBJ_KEY ? arg : right->endpoint_id;
-
-	return strcmp(left->endpoint_id, right_endpoint_id) ? 0 : CMP_MATCH | CMP_STOP;
-}
-
-
-static void qualify_info_destructor(void *obj)
-{
-	struct qualify_info *info = obj;
-	if (!info) {
-		return;
-	}
-	ast_string_field_free_memory(info);
-	/* Cancel the qualify */
-	if (!AST_SCHED_DEL(sched, info->scheduler_id)) {
-		/* If we successfully deleted the qualify, we got it before it
-		 * fired. We can safely delete the data that was passed to it.
-		 * Otherwise, we're getting deleted while this is firing - don't
-		 * touch that memory!
-		 */
-		ast_free(info->scheduler_data);
-	}
-}
-
-static struct qualify_info *create_qualify_info(struct ast_sip_endpoint *endpoint)
-{
-	struct qualify_info *info;
-
-	info = ao2_alloc(sizeof(*info), qualify_info_destructor);
-	if (!info) {
-		return NULL;
-	}
-
-	if (ast_string_field_init(info, 64)) {
-		ao2_ref(info, -1);
-		return NULL;
-	}
-	ast_string_field_set(info, endpoint_id, ast_sorcery_object_get_id(endpoint));
-
-	return info;
-}
-
-static int send_qualify_request(void *data)
-{
-	struct ast_sip_endpoint *endpoint = data;
-	pjsip_tx_data *tdata;
-	/* YAY! Send an OPTIONS request. */
-
-	ast_sip_create_request("OPTIONS", NULL, endpoint, NULL, &tdata);
-	ast_sip_send_request(tdata, NULL, endpoint);
-
-	ao2_cleanup(endpoint);
-	return 0;
-}
-
-static int qualify_endpoint_scheduler_cb(const void *data)
-{
-	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
-	struct ast_sorcery *sorcery;
-	char *endpoint_id = (char *)data;
-
-	sorcery = ast_sip_get_sorcery();
-	if (!sorcery) {
-		ast_free(endpoint_id);
-		return 0;
-	}
-
-	endpoint = ast_sorcery_retrieve_by_id(sorcery, "endpoint", endpoint_id);
-	if (!endpoint) {
-		/* Whoops, endpoint went away */
-		ast_free(endpoint_id);
-		return 0;
-	}
-
-	ast_sip_push_task(NULL, send_qualify_request, endpoint);
-
-	return 1;
-}
-
-static void schedule_qualifies(void)
-{
-	RAII_VAR(struct ao2_container *, endpoints, NULL, ao2_cleanup);
-	struct ao2_iterator it_endpoints;
-	struct ast_sip_endpoint *endpoint;
-	struct qualify_info *info;
-	char *endpoint_id;
-
-	endpoints = ast_res_sip_get_endpoints();
-	if (!endpoints) {
-		return;
-	}
-
-	it_endpoints = ao2_iterator_init(endpoints, 0);
-	while ((endpoint = ao2_iterator_next(&it_endpoints))) {
-		if (endpoint->qualify_frequency) {
-			/* XXX TODO: This really should only qualify registered peers,
-			 * which means we need a registrar. We should check the
-			 * registrar to see if this endpoint has registered and, if
-			 * not, pass on it.
-			 *
-			 * Actually, all of this should just get moved into the registrar.
-			 * Otherwise, the registar will have to kick this off when a
-			 * new endpoint registers, so it just makes sense to have it
-			 * all live there.
-			 */
-			info = create_qualify_info(endpoint);
-			if (!info) {
-				ao2_ref(endpoint, -1);
-				break;
-			}
-			endpoint_id = ast_strdup(info->endpoint_id);
-			if (!endpoint_id) {
-				ao2_t_ref(info, -1, "Dispose of info on off nominal");
-				ao2_ref(endpoint, -1);
-				break;
-			}
-			info->scheduler_data = endpoint_id;
-			info->scheduler_id = ast_sched_add_variable(sched, endpoint->qualify_frequency * 1000, qualify_endpoint_scheduler_cb, endpoint_id, 1);
-			ao2_t_link(scheduled_qualifies, info, "Link scheduled qualify information into container");
-			ao2_t_ref(info, -1, "Dispose of creation ref");
-		}
-		ao2_t_ref(endpoint, -1, "Dispose of iterator ref");
-	}
-	ao2_iterator_destroy(&it_endpoints);
-}
-
-static char *send_options(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static pjsip_module options_module = {
+	.name = {"Options Module", 14},
+	.id = -1,
+	.priority = PJSIP_MOD_PRIORITY_APPLICATION,
+	.start = options_start,
+	.stop = options_stop,
+	.on_rx_request = options_on_rx_request,
+};
+
+static char *cli_qualify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
 	const char *endpoint_name;
-	pjsip_tx_data *tdata;
 
 	switch (cmd) {
 	case CLI_INIT:
-		e->command = "sip send options";
+		e->command = "sip qualify";
 		e->usage =
-			"Usage: sip send options <endpoint>\n"
-			"       Send a SIP OPTIONS request to the specified endpoint.\n";
+			"Usage: sip qualify <endpoint>\n"
+			"       Send a SIP OPTIONS request to all contacts on the endpoint.\n";
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
 	}
 
-	if (a->argc != 4) {
+	if (a->argc != 3) {
 		return CLI_SHOWUSAGE;
 	}
 
-	endpoint_name = a->argv[3];
-
-	endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name);
-	if (!endpoint) {
+	endpoint_name = a->argv[2];
+
+	if (!(endpoint = ast_sorcery_retrieve_by_id(
+		      ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
 		ast_log(LOG_ERROR, "Unable to retrieve endpoint %s\n", endpoint_name);
 		return CLI_FAILURE;
 	}
 
-	if (ast_sip_create_request("OPTIONS", NULL, endpoint, NULL, &tdata)) {
-		ast_log(LOG_ERROR, "Unable to create OPTIONS request to endpoint %s\n", endpoint_name);
-		return CLI_FAILURE;
-	}
-
-	if (ast_sip_send_request(tdata, NULL, endpoint)) {
-		ast_log(LOG_ERROR, "Unable to send OPTIONS request to endpoint %s\n", endpoint_name);
-		return CLI_FAILURE;
-	}
+	/* send a qualify for all contacts registered with the endpoint */
+	on_endpoint(endpoint, NULL, 0);
 
 	return CLI_SUCCESS;
 }
 
 static struct ast_cli_entry cli_options[] = {
-	AST_CLI_DEFINE(send_options, "Send an OPTIONS requst to an arbitrary SIP URI"),
+	AST_CLI_DEFINE(cli_qualify, "Send an OPTIONS request to a SIP endpoint"),
 };
 
+static int sched_qualifies_hash_fn(const void *obj, int flags)
+{
+	const struct sched_data *data = obj;
+
+	return ast_str_hash(ast_sorcery_object_get_id(data->contact));
+}
+
+static int sched_qualifies_cmp_fn(void *obj, void *arg, int flags)
+{
+	struct sched_data *data = obj;
+	const char *id = flags & OBJ_KEY ? arg : ast_sorcery_object_get_id(arg);
+
+	ast_log(LOG_VERBOSE, "COMPARING!!! stored = %s, incoming = %s\n", ast_sorcery_object_get_id(data->contact), id);
+	return !strcmp(ast_sorcery_object_get_id(data->contact), id);
+}
+
+int ast_sip_initialize_sorcery_qualify(struct ast_sorcery *sorcery)
+{
+	/* initialize sorcery contact_status resource */
+	ast_sorcery_apply_default(sorcery, CONTACT_STATUS, "memory", NULL);
+
+	if (ast_sorcery_object_register(sorcery, CONTACT_STATUS,
+					contact_status_alloc, NULL, NULL)) {
+		ast_log(LOG_ERROR, "Unable to register contact_status in sorcery\n");
+		return -1;
+	}
+
+	ast_sorcery_object_field_register(sorcery, CONTACT_STATUS, "status", "no", OPT_BOOL_T,
+					  1, FLDSET(struct contact_status, status));
+
+	if (ast_sorcery_observer_add(sorcery, "contact", &contact_observer)) {
+		ast_log(LOG_WARNING, "Unable to add contact observer\n");
+		return -1;
+	}
+	ast_log(LOG_VERBOSE, "YAY added qualify stuff\n");
+	return 0;
+}
+
 int ast_res_sip_init_options_handling(int reload)
 {
 	const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
 
-	if (scheduled_qualifies) {
-		ao2_t_ref(scheduled_qualifies, -1, "Remove old scheduled qualifies");
-	}
-	scheduled_qualifies = ao2_t_container_alloc(QUALIFIED_BUCKETS, qualify_info_hash_fn, qualify_info_cmp_fn, "Create container for scheduled qualifies");
-	if (!scheduled_qualifies) {
+	if (sched_qualifies) {
+		ao2_t_ref(sched_qualifies, -1, "Remove old scheduled qualifies");
+	}
+
+	if (!(sched_qualifies = ao2_t_container_alloc(
+		QUALIFIED_BUCKETS, sched_qualifies_hash_fn, sched_qualifies_cmp_fn,
+		"Create container for scheduled qualifies"))) {
+
 		return -1;
 	}
 
@@ -361,7 +667,7 @@
 	}
 
 	if (pjsip_endpt_register_module(ast_sip_get_pjsip_endpoint(), &options_module) != PJ_SUCCESS) {
-		options_module_stop();
+		options_stop();
 		return -1;
 	}
 
@@ -372,7 +678,5 @@
 
 	ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
 
-	schedule_qualifies();
-
 	return 0;
 }




More information about the svn-commits mailing list