[asterisk-commits] gtjoseph: trunk r410288 - in /trunk: ./ include/asterisk/ main/ res/ res/res_...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sat Mar 8 10:50:40 CST 2014


Author: gtjoseph
Date: Sat Mar  8 10:50:36 2014
New Revision: 410288

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=410288
Log:
pjsip_cli: Create pjsip show channel and contact, and general cli code cleanup.

Created the 'pjsip show channel' and 'pjsip show contact' commands.
Refactored out the hated ast_hashtab.  Replaced with ao2_container.
Cleaned up function naming.  Internal only, no public name changes.
Cleaned up whitespace and brace formatting in cli code.
Changed some NULL checking from "if"s to ast_asserts.
Fixed some register/unregister ordering to reduce deadlock potential.
Fixed ast_sip_location_add_contact where the 'name' buffer was too short.
Fixed some self-assignment issues in res_pjsip_outbound_registration.

(closes issue ASTERISK-23276)
Review: http://reviewboard.asterisk.org/r/3283/
........

Merged revisions 410287 from http://svn.asterisk.org/svn/asterisk/branches/12

Modified:
    trunk/   (props changed)
    trunk/include/asterisk/res_pjsip.h
    trunk/include/asterisk/res_pjsip_cli.h
    trunk/include/asterisk/sorcery.h
    trunk/main/sorcery.c
    trunk/res/res_pjsip/config_auth.c
    trunk/res/res_pjsip/config_transport.c
    trunk/res/res_pjsip/location.c
    trunk/res/res_pjsip/pjsip_cli.c
    trunk/res/res_pjsip/pjsip_configuration.c
    trunk/res/res_pjsip_endpoint_identifier_ip.c
    trunk/res/res_pjsip_outbound_registration.c

Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-12-merged' - no diff available.

Modified: trunk/include/asterisk/res_pjsip.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/res_pjsip.h?view=diff&rev=410288&r1=410287&r2=410288
==============================================================================
--- trunk/include/asterisk/res_pjsip.h (original)
+++ trunk/include/asterisk/res_pjsip.h Sat Mar  8 10:50:36 2014
@@ -220,6 +220,19 @@
 	struct ao2_container *permanent_contacts;
 	/*! Determines whether SIP Path headers are supported */
 	unsigned int support_path;
+};
+
+/*!
+ * \brief A wrapper for contact that adds the aor_id and
+ * a consistent contact id.  Used by ast_sip_for_each_contact.
+ */
+struct ast_sip_contact_wrapper {
+	/*! The id of the parent aor. */
+	char *aor_id;
+	/*! The id of contact in form of aor_id/contact_uri. */
+	char *contact_id;
+	/*! Pointer to the actual contact. */
+	struct ast_sip_contact *contact;
 };
 
 /*!
@@ -1620,7 +1633,8 @@
  * \brief For every contact on an AOR call the given 'on_contact' handler.
  *
  * \param aor the aor containing a list of contacts to iterate
- * \param on_contact callback on each contact on an AOR
+ * \param on_contact callback on each contact on an AOR.  The object
+ * received by the callback will be a ast_sip_contact_wrapper structure.
  * \param arg user data passed to handler
  * \retval 0 Success, non-zero on failure
  */

Modified: trunk/include/asterisk/res_pjsip_cli.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/res_pjsip_cli.h?view=diff&rev=410288&r1=410287&r2=410288
==============================================================================
--- trunk/include/asterisk/res_pjsip_cli.h (original)
+++ trunk/include/asterisk/res_pjsip_cli.h Sat Mar  8 10:50:36 2014
@@ -29,23 +29,20 @@
 #define CLI_INDENT_TO_SPACES(x) ((x * 2) + 1 + CLI_MAX_TITLE_NAME)
 
 /*
- * \brief CLI Formatter Context
+ * \brief CLI Formatter Context passed to all formatters.
  */
 struct ast_sip_cli_context {
-	int peers_mon_online;
-	int peers_mon_offline;
-	int peers_unmon_offline;
-	int peers_unmon_online;
+	/*! Buffer used to accumulate cli output. */
 	struct ast_str *output_buffer;
-	const struct ast_cli_args *a;
-	const struct ast_sip_endpoint *current_endpoint;
-	const struct ast_sip_auth *current_auth;
-	const struct ast_sip_aor *current_aor;
+	/*! Used to indicate which direction an auth is used for. "I" or "O" */
 	char *auth_direction;
-	unsigned int print_flags;
+	/*! Allows formatters to know how far to indent their output. */
 	int indent_level;
+	/*! Tells a formatter to dump its object_set. */
 	unsigned show_details : 1;
+	/*! Tells a formatter to descend into child objects. */
 	unsigned recurse : 1;
+	/*! Tells a formatter to dump it's object_set only if it's the root object. */
 	unsigned show_details_only_level_0 : 1;
 };
 
@@ -53,12 +50,24 @@
  * \brief CLI Formatter Registry Entry
  */
 struct ast_sip_cli_formatter_entry {
+	/*! A globally unique name for this formatter.  If this formatter entry
+	 * is for an existing sorcery object type, then this name must match
+	 * the sorcery object type.  Otherwise it can be any string as long as
+	 * it's globally unique.
+	 */
 	const char *name;
+	/*! The callback used to print the object's column headers. */
 	ao2_callback_fn *print_header;
+	/*! The callback used to print the details of the object. */
 	ao2_callback_fn *print_body;
+	/*! The function used to retrieve a container of all objects of this type. */
 	struct ao2_container *(* get_container)(void);
-	int (* iterator)(const void *container, ao2_callback_fn callback, void *args);
-	ao2_sort_fn *comparator;
+	/*! The function used to iterate over a container of objects. */
+	int (* iterate)(void *container, ao2_callback_fn callback, void *args);
+	/*! The function used to retrieve a specific object from it's container. */
+	void *(* retrieve_by_id)(const char *id);
+	/*! The function used to retrieve an id string from an object. */
+	const char *(* get_id)(const void *obj);
 };
 
 /*!

Modified: trunk/include/asterisk/sorcery.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/sorcery.h?view=diff&rev=410288&r1=410287&r2=410288
==============================================================================
--- trunk/include/asterisk/sorcery.h (original)
+++ trunk/include/asterisk/sorcery.h Sat Mar  8 10:50:36 2014
@@ -924,9 +924,19 @@
 int ast_sorcery_object_set_extended(const void *object, const char *name, const char *value);
 
 /*!
- * \brief Sorcery object comparator based on id.
- */
-int ast_sorcery_object_id_compare(const void *obj_left, const void *obj_right, int flags);
+ * \brief ao2 object comparator based on sorcery id.
+ */
+int ast_sorcery_object_id_compare(void *obj, void *arg, int flags);
+
+/*!
+ * \brief ao2 object sorter based on sorcery id.
+ */
+int ast_sorcery_object_id_sort(const void *obj, const void *arg, int flags);
+
+/*!
+ * \brief ao2 object hasher based on sorcery id.
+ */
+int ast_sorcery_object_id_hash(const void *obj, int flags);
 
 /*!
  * \brief Get the sorcery object type given a type name.

Modified: trunk/main/sorcery.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/sorcery.c?view=diff&rev=410288&r1=410287&r2=410288
==============================================================================
--- trunk/main/sorcery.c (original)
+++ trunk/main/sorcery.c Sat Mar  8 10:50:36 2014
@@ -1832,12 +1832,61 @@
 		sorcery_observer_remove, cbs);
 }
 
-int ast_sorcery_object_id_compare(const void *obj_left, const void *obj_right, int flags)
-{
-	if (!obj_left || !obj_right) {
-		return 0;
-	}
-	return strcmp(ast_sorcery_object_get_id(obj_left), ast_sorcery_object_get_id(obj_right));
+int ast_sorcery_object_id_sort(const void *obj, const void *arg, int flags)
+{
+	const char *right_key = arg;
+	int cmp;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = ast_sorcery_object_get_id(arg);
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcmp(ast_sorcery_object_get_id(obj), right_key);
+		break;
+	case OBJ_SEARCH_PARTIAL_KEY:
+		cmp = strncmp(ast_sorcery_object_get_id(obj), right_key, strlen(right_key));
+		break;
+	default:
+		cmp = 0;
+		break;
+	}
+	return cmp;
+}
+
+int ast_sorcery_object_id_compare(void *obj, void *arg, int flags)
+{
+	const char *right_key = arg;
+	int cmp = 0;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = ast_sorcery_object_get_id(arg);
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		if (strcmp(ast_sorcery_object_get_id(obj), right_key) == 0) {
+			cmp = CMP_MATCH | CMP_STOP;
+		}
+		break;
+	case OBJ_SEARCH_PARTIAL_KEY:
+		if (strncmp(ast_sorcery_object_get_id(obj), right_key, strlen(right_key)) == 0) {
+			cmp = CMP_MATCH;
+		}
+		break;
+	default:
+		cmp = 0;
+		break;
+	}
+	return cmp;
+}
+
+int ast_sorcery_object_id_hash(const void *obj, int flags) {
+	if (flags & OBJ_SEARCH_OBJECT) {
+		return ast_str_hash(ast_sorcery_object_get_id(obj));
+	} else if (flags & OBJ_SEARCH_KEY) {
+		return ast_str_hash(obj);
+	}
+	return -1;
 }
 
 struct ast_sorcery_object_type *ast_sorcery_get_object_type(const struct ast_sorcery *sorcery,

Modified: trunk/res/res_pjsip/config_auth.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip/config_auth.c?view=diff&rev=410288&r1=410287&r2=410288
==============================================================================
--- trunk/res/res_pjsip/config_auth.c (original)
+++ trunk/res/res_pjsip/config_auth.c Sat Mar  8 10:50:36 2014
@@ -199,10 +199,10 @@
 	.format_ami = format_ami_endpoint_auth
 };
 
-static struct ao2_container *cli_get_auth_container(void)
+static struct ao2_container *cli_get_container(void)
 {
 	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
-	RAII_VAR(struct ao2_container *, s_container, NULL, ao2_cleanup);
+	struct ao2_container *s_container;
 
 	container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "auth",
 		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
@@ -211,34 +211,36 @@
 	}
 
 	s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
-		ast_sorcery_object_id_compare, NULL);
+		ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
 	if (!s_container) {
 		return NULL;
 	}
 
 	if (ao2_container_dup(s_container, container, 0)) {
-		return NULL;
-	}
-	ao2_ref(s_container, +1);
+		ao2_ref(s_container, -1);
+		return NULL;
+	}
+
 	return s_container;
 }
 
-static int cli_iterator(const void *container, ao2_callback_fn callback, void *args)
-{
-	const struct ast_sip_auth_vector *vector = container;
-
-	return ast_sip_for_each_auth(vector, callback, args);
-}
-
-static int cli_print_auth_header(void *obj, void *arg, int flags)
+static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
+{
+	return ast_sip_for_each_auth(container, callback, args);
+}
+
+static void *cli_retrieve_by_id(const char *id)
+{
+	return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE, id);
+}
+
+static int cli_print_header(void *obj, void *arg, int flags)
 {
 	struct ast_sip_cli_context *context = arg;
 	int indent = CLI_INDENT_TO_SPACES(context->indent_level);
 	int filler = CLI_MAX_WIDTH - indent - 20;
 
-	if (!context->output_buffer) {
-		return -1;
-	}
+	ast_assert(context->output_buffer != NULL);
 
 	ast_str_append(&context->output_buffer, 0,
 		"%*s:  <AuthId/UserName%*.*s>\n", indent, "I/OAuth", filler, filler,
@@ -247,17 +249,13 @@
 	return 0;
 }
 
-static int cli_print_auth_body(void *obj, void *arg, int flags)
+static int cli_print_body(void *obj, void *arg, int flags)
 {
 	struct ast_sip_auth *auth = obj;
 	struct ast_sip_cli_context *context = arg;
 	char title[32];
 
-	context->current_auth = auth;
-
-	if (!context->output_buffer) {
-		return -1;
-	}
+	ast_assert(context->output_buffer != NULL);
 
 	snprintf(title, sizeof(title), "%sAuth",
 		context->auth_direction ? context->auth_direction : "");
@@ -274,15 +272,6 @@
 
 	return 0;
 }
-
-static struct ast_sip_cli_formatter_entry cli_auth_formatter = {
-	.name = SIP_SORCERY_AUTH_TYPE,
-	.print_header = cli_print_auth_header,
-	.print_body = cli_print_auth_body,
-	.get_container = cli_get_auth_container,
-	.iterator = cli_iterator,
-	.comparator = ast_sorcery_object_id_compare,
-};
 
 static struct ast_cli_entry cli_commands[] = {
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Auths",
@@ -299,6 +288,8 @@
 				 "       Show the configured PJSIP Auth\n"),
 };
 
+static struct ast_sip_cli_formatter_entry *cli_formatter;
+
 /*! \brief Initialize sorcery with auth support */
 int ast_sip_initialize_sorcery_auth(void)
 {
@@ -326,7 +317,21 @@
 			"userpass", auth_type_handler, auth_type_to_str, NULL, 0, 0);
 
 	ast_sip_register_endpoint_formatter(&endpoint_auth_formatter);
-	ast_sip_register_cli_formatter(&cli_auth_formatter);
+
+	cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
+	if (!cli_formatter) {
+		ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
+		return -1;
+	}
+	cli_formatter->name = SIP_SORCERY_AUTH_TYPE;
+	cli_formatter->print_header = cli_print_header;
+	cli_formatter->print_body = cli_print_body;
+	cli_formatter->get_container = cli_get_container;
+	cli_formatter->iterate = cli_iterator;
+	cli_formatter->get_id = ast_sorcery_object_get_id;
+	cli_formatter->retrieve_by_id = cli_retrieve_by_id;
+
+	ast_sip_register_cli_formatter(cli_formatter);
 	ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
 
 	return 0;
@@ -335,6 +340,7 @@
 int ast_sip_destroy_sorcery_auth(void)
 {
 	ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
-	ast_sip_unregister_cli_formatter(&cli_auth_formatter);
-	return 0;
-}
+	ast_sip_unregister_cli_formatter(cli_formatter);
+
+	return 0;
+}

Modified: trunk/res/res_pjsip/config_transport.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip/config_transport.c?view=diff&rev=410288&r1=410287&r2=410288
==============================================================================
--- trunk/res/res_pjsip/config_transport.c (original)
+++ trunk/res/res_pjsip/config_transport.c Sat Mar  8 10:50:36 2014
@@ -509,7 +509,7 @@
 static struct ao2_container *cli_get_container(void)
 {
 	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
-	RAII_VAR(struct ao2_container *, s_container, NULL, ao2_cleanup);
+	struct ao2_container *s_container;
 
 	container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport",
 		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
@@ -518,19 +518,20 @@
 	}
 
 	s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
-		ast_sorcery_object_id_compare, NULL);
+		ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
 	if (!s_container) {
 		return NULL;
 	}
 
 	if (ao2_container_dup(s_container, container, 0)) {
+		ao2_ref(s_container, -1);
 		return NULL;
 	}
-	ao2_ref(s_container, +1);
+
 	return s_container;
 }
 
-static int cli_iterator(const void *container, ao2_callback_fn callback, void *args)
+static int cli_iterate(void *container, ao2_callback_fn callback, void *args)
 {
 	const struct ast_sip_endpoint *endpoint = container;
 	struct ast_sip_transport *transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(),
@@ -539,7 +540,13 @@
 	if (!transport) {
 		return -1;
 	}
+
 	return callback(transport, args, 0);
+}
+
+static void *cli_retrieve_by_id(const char *id)
+{
+	return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", id);
 }
 
 static int cli_print_header(void *obj, void *arg, int flags)
@@ -548,9 +555,7 @@
 	int indent = CLI_INDENT_TO_SPACES(context->indent_level);
 	int filler = CLI_MAX_WIDTH - indent - 61;
 
-	if (!context->output_buffer) {
-		return -1;
-	}
+	ast_assert(context->output_buffer != NULL);
 
 	ast_str_append(&context->output_buffer, 0,
 		"%*s:  <TransportId........>  <Type>  <cos>  <tos>  <BindAddress%*.*s>\n",
@@ -565,9 +570,7 @@
 	struct ast_sip_cli_context *context = arg;
 	char hoststr[PJ_INET6_ADDRSTRLEN];
 
-	if (!context->output_buffer) {
-		return -1;
-	}
+	ast_assert(context->output_buffer != NULL);
 
 	pj_sockaddr_print(&transport->host, hoststr, sizeof(hoststr), 3);
 
@@ -585,15 +588,6 @@
 
 	return 0;
 }
-
-static struct ast_sip_cli_formatter_entry  cli_formatter = {
-	.name = "transport",
-	.print_header = cli_print_header,
-	.print_body = cli_print_body,
-	.get_container = cli_get_container,
-	.iterator = cli_iterator,
-	.comparator = ast_sorcery_object_id_compare,
-};
 
 static struct ast_cli_entry cli_commands[] = {
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Transports",
@@ -609,6 +603,8 @@
 		.usage = "Usage: pjsip show transport <id>\n"
 				 "       Show the configured PJSIP Transport\n"),
 };
+
+static struct ast_sip_cli_formatter_entry *cli_formatter;
 
 /*! \brief Initialize sorcery with transport support */
 int ast_sip_initialize_sorcery_transport(void)
@@ -643,7 +639,21 @@
 	ast_sorcery_object_field_register(sorcery, "transport", "cos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, cos));
 
 	ast_sip_register_endpoint_formatter(&endpoint_transport_formatter);
-	ast_sip_register_cli_formatter(&cli_formatter);
+
+	cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
+	if (!cli_formatter) {
+		ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
+		return -1;
+	}
+	cli_formatter->name = "transport";
+	cli_formatter->print_header = cli_print_header;
+	cli_formatter->print_body = cli_print_body;
+	cli_formatter->get_container = cli_get_container;
+	cli_formatter->iterate = cli_iterate;
+	cli_formatter->get_id = ast_sorcery_object_get_id;
+	cli_formatter->retrieve_by_id = cli_retrieve_by_id;
+
+	ast_sip_register_cli_formatter(cli_formatter);
 	ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
 
 	return 0;
@@ -652,6 +662,7 @@
 int ast_sip_destroy_sorcery_transport(void)
 {
 	ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
-	ast_sip_unregister_cli_formatter(&cli_formatter);
-	return 0;
-}
+	ast_sip_unregister_cli_formatter(cli_formatter);
+
+	return 0;
+}

Modified: trunk/res/res_pjsip/location.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip/location.c?view=diff&rev=410288&r1=410287&r2=410288
==============================================================================
--- trunk/res/res_pjsip/location.c (original)
+++ trunk/res/res_pjsip/location.c Sat Mar  8 10:50:36 2014
@@ -181,7 +181,7 @@
 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)
 {
-	char name[AST_UUID_STR_LEN];
+	char name[MAX_OBJECT_FIELD * 2 + 3];
 	RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
 
 	snprintf(name, sizeof(name), "%s;@%s", ast_sorcery_object_get_id(aor), uri);
@@ -279,21 +279,21 @@
 	return 0;
 }
 
-static int contact_to_vl(void *object, void *arg, int flags)
-{
-	struct ast_sip_contact *contact = object;
+static int contact_to_var_list(void *object, void *arg, int flags)
+{
+	struct ast_sip_contact_wrapper *wrapper = object;
 	struct ast_variable **var = arg;
 
-	ast_variable_list_append(&*var, ast_variable_new("contact", contact->uri, ""));
-
-	return 0;
-}
-
-static int contacts_to_vl(const void *obj, struct ast_variable **fields)
+	ast_variable_list_append(&*var, ast_variable_new("contact", wrapper->contact->uri, ""));
+
+	return 0;
+}
+
+static int contacts_to_var_list(const void *obj, struct ast_variable **fields)
 {
 	const struct ast_sip_aor *aor = obj;
 
-	ast_sip_for_each_contact(aor, contact_to_vl, fields);
+	ast_sip_for_each_contact(aor, contact_to_var_list, fields);
 
 	return 0;
 }
@@ -323,12 +323,21 @@
 	return 0;
 }
 
+static void contact_wrapper_destroy(void *obj)
+{
+	struct ast_sip_contact_wrapper *wrapper = obj;
+	ast_free(wrapper->aor_id);
+	ast_free(wrapper->contact_id);
+	ao2_ref(wrapper->contact, -1);
+}
+
 int ast_sip_for_each_contact(const struct ast_sip_aor *aor,
 		ao2_callback_fn on_contact, void *arg)
 {
 	RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
-	struct ast_sip_contact *contact;
 	struct ao2_iterator i;
+	int res = 0;
+	void *object = NULL;
 
 	if (!on_contact ||
 	    !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
@@ -336,26 +345,44 @@
 	}
 
 	i = ao2_iterator_init(contacts, 0);
-	while ((contact = ao2_iterator_next(&i))) {
-		int res;
-
-		ast_sorcery_object_set_extended(contact, "@aor_id", ast_sorcery_object_get_id(aor));
-		if ((res = on_contact(contact, arg, 0))) {
-			ao2_iterator_destroy(&i);
-			return -1;
+	while ((object = ao2_iterator_next(&i))) {
+		RAII_VAR(struct ast_sip_contact *, contact, object, ao2_cleanup);
+		RAII_VAR(struct ast_sip_contact_wrapper *, wrapper, NULL, ao2_cleanup);
+		const char *aor_id = ast_sorcery_object_get_id(aor);
+
+		wrapper = ao2_alloc(sizeof(struct ast_sip_contact_wrapper), contact_wrapper_destroy);
+		if (!wrapper) {
+			res = -1;
+			break;
+		}
+		wrapper->contact_id = ast_malloc(strlen(aor_id) + strlen(contact->uri) + 2);
+		if (!wrapper->contact_id) {
+			res = -1;
+			break;
+		}
+		sprintf(wrapper->contact_id, "%s/%s", aor_id, contact->uri);
+		wrapper->aor_id = ast_strdup(aor_id);
+		if (!wrapper->aor_id) {
+			res = -1;
+			break;
+		}
+		wrapper->contact = contact;
+		ao2_bump(wrapper->contact);
+
+		if ((res = on_contact(wrapper, arg, 0))) {
+			break;
 		}
 	}
 	ao2_iterator_destroy(&i);
-	return 0;
+	return res;
 }
 
 int ast_sip_contact_to_str(void *object, void *arg, int flags)
 {
-	struct ast_sip_contact *contact = object;
+	struct ast_sip_contact_wrapper *wrapper = object;
 	struct ast_str **buf = arg;
 
-	ast_str_append(buf, 0, "%s/%s,",
-		ast_sorcery_object_get_extended(contact, "aor_id"), contact->uri);
+	ast_str_append(buf, 0, "%s,", wrapper->contact_id);
 
 	return 0;
 }
@@ -448,10 +475,10 @@
 	.format_ami = format_ami_endpoint_aor
 };
 
-static struct ao2_container *cli_get_aor_container(void)
+static struct ao2_container *cli_aor_get_container(void)
 {
 	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
-	RAII_VAR(struct ao2_container *, s_container, NULL, ao2_cleanup);
+	struct ao2_container *s_container;
 
 	container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "aor",
 		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
@@ -460,90 +487,144 @@
 	}
 
 	s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
-		ast_sorcery_object_id_compare, NULL);
+		ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
 	if (!s_container) {
 		return NULL;
 	}
 
 	if (ao2_container_dup(s_container, container, 0)) {
-		return NULL;
-	}
-	ao2_ref(s_container, +1);
+		ao2_ref(s_container, -1);
+		return NULL;
+	}
+
 	return s_container;
 }
 
-static int populate_contact_container(void *obj, void *arg, int flags)
-{
-	struct ast_sip_contact *contact = obj;
-	struct ao2_container *container = arg;
-
-	ao2_link(container, contact);
-	return 0;
-}
-
-static int gather_aor_contacts(void *obj, void *arg, int flags)
+static int cli_contact_populate_container(void *obj, void *arg, int flags)
+{
+	ao2_link(arg, obj);
+
+	return 0;
+}
+
+static int cli_aor_gather_contacts(void *obj, void *arg, int flags)
 {
 	struct ast_sip_aor *aor = obj;
-	struct ao2_container *container = arg;
-	ast_sip_for_each_contact(aor, populate_contact_container, container);
-	return 0;
-}
-
-static int cli_contact_compare(const void *left, const void *right, int flags)
-{
-	const struct ast_sip_contact *left_contact = left;
-	const struct ast_sip_contact *right_contact = right;
-	int rc;
-
-	if (!left_contact || !right_contact) {
-		return 0;
-	}
-	rc = strcmp(ast_sorcery_object_get_extended(left_contact, "aor_id"),
-		ast_sorcery_object_get_extended(right_contact, "aor_id"));
-	if (rc) {
-		return rc;
-	}
-	return strcmp(left_contact->uri, right_contact->uri);
-}
-
-static struct ao2_container *cli_get_contact_container(void)
+
+	return ast_sip_for_each_contact(aor, cli_contact_populate_container, arg);
+}
+
+static const char *cli_contact_get_id(const void *obj)
+{
+	const struct ast_sip_contact_wrapper *wrapper = obj;
+	return wrapper->contact_id;
+}
+
+static int cli_contact_sort(const void *obj, const void *arg, int flags)
+{
+	const struct ast_sip_contact_wrapper *left_wrapper = obj;
+	const struct ast_sip_contact_wrapper *right_wrapper = arg;
+	const char *right_key = arg;
+	int cmp = 0;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = right_wrapper->contact_id;
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcmp(left_wrapper->contact_id, right_key);
+		break;
+	case OBJ_SEARCH_PARTIAL_KEY:
+		cmp = strncmp(left_wrapper->contact_id, right_key, strlen(right_key));
+		break;
+	default:
+		cmp = 0;
+		break;
+	}
+
+	return cmp;
+}
+
+static int cli_contact_compare(void *obj, void *arg, int flags)
+{
+	const struct ast_sip_contact_wrapper *left_wrapper = obj;
+	const struct ast_sip_contact_wrapper *right_wrapper = arg;
+	const char *right_key = arg;
+	int cmp = 0;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = right_wrapper->contact_id;
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		if (strcmp(left_wrapper->contact_id, right_key) == 0) {;
+			cmp = CMP_MATCH | CMP_STOP;
+		}
+		break;
+	case OBJ_SEARCH_PARTIAL_KEY:
+		if (strncmp(left_wrapper->contact_id, right_key, strlen(right_key)) == 0) {
+			cmp = CMP_MATCH;
+		}
+		break;
+	default:
+		cmp = 0;
+		break;
+	}
+
+	return cmp;
+}
+
+static int cli_contact_hash(const void *obj, int flags)
+{
+	const struct ast_sip_contact_wrapper *wrapper = obj;
+	if (flags & OBJ_SEARCH_OBJECT) {
+		return ast_str_hash(wrapper->contact_id);
+	} else if (flags & OBJ_SEARCH_KEY) {
+		return ast_str_hash(obj);
+	}
+
+	return -1;
+}
+
+static int cli_contact_iterate(void *container, ao2_callback_fn callback, void *args)
+{
+	return ast_sip_for_each_contact(container, callback, args);
+}
+
+static struct ao2_container *cli_contact_get_container(void)
 {
 	RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup);
 	struct ao2_container *child_container;
 
-	parent_container =  cli_get_aor_container();
+	parent_container =  cli_aor_get_container();
 	if (!parent_container) {
 		return NULL;
 	}
 
-	child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
-		cli_contact_compare, NULL);
+	child_container = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, 17,
+		cli_contact_hash, cli_contact_sort, cli_contact_compare);
 	if (!child_container) {
 		return NULL;
 	}
 
-	ao2_ref(child_container, +1);
-	ao2_callback(parent_container, OBJ_NODATA, gather_aor_contacts, child_container);
+	ao2_callback(parent_container, OBJ_NODATA, cli_aor_gather_contacts, child_container);
 
 	return child_container;
 }
 
-static int cli_contact_iterator(const void *container, ao2_callback_fn callback, void *args)
-{
-	const struct ast_sip_aor *array = container;
-
-	return ast_sip_for_each_contact(array, callback, args);
-}
-
-static int cli_print_contact_header(void *obj, void *arg, int flags)
+static void *cli_contact_retrieve_by_id(const char *id)
+{
+	return ao2_find(cli_contact_get_container(), id, OBJ_KEY | OBJ_NOLOCK);
+}
+
+static int cli_contact_print_header(void *obj, void *arg, int flags)
 {
 	struct ast_sip_cli_context *context = arg;
 	int indent = CLI_INDENT_TO_SPACES(context->indent_level);
 	int filler = CLI_LAST_TABSTOP - indent - 18;
 
-	if (!context->output_buffer) {
-		return -1;
-	}
+	ast_assert(context->output_buffer != NULL);
+
 	ast_str_append(&context->output_buffer, 0,
 		"%*s:  <Aor/ContactUri%*.*s>  <Status....>  <RTT(ms)..>\n",
 		indent, "Contact", filler, filler, CLI_HEADER_FILLER);
@@ -551,27 +632,20 @@
 	return 0;
 }
 
-static int cli_print_contact_body(void *obj, void *arg, int flags)
-{
-	struct ast_sip_contact *contact = obj;
+static int cli_contact_print_body(void *obj, void *arg, int flags)
+{
+	struct ast_sip_contact_wrapper *wrapper = obj;
+	struct ast_sip_contact *contact = wrapper->contact;
 	struct ast_sip_cli_context *context = arg;
-	char *print_name = NULL;
-	int print_name_len;
 	int indent;
 	int flexwidth;
-	const char *aor_id = ast_sorcery_object_get_extended(contact, "aor_id");
 
 	RAII_VAR(struct ast_sip_contact_status *, status,
 		ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)),
 		ao2_cleanup);
 
-	if (!context->output_buffer) {
-		return -1;
-	}
-
-	print_name_len = strlen(aor_id)	+ strlen(contact->uri) + 2;
-	print_name = ast_alloca(print_name_len);
-	snprintf(print_name, print_name_len, "%s/%s", aor_id, contact->uri);
+	ast_assert(contact->uri != NULL);
+	ast_assert(context->output_buffer != NULL);
 
 	indent = CLI_INDENT_TO_SPACES(context->indent_level);
 	flexwidth = CLI_LAST_TABSTOP - indent - 2;
@@ -580,31 +654,40 @@
 		indent,
 		"Contact",
 		flexwidth, flexwidth,
-		print_name,
+		wrapper->contact_id,
 		(status ? (status->status == AVAILABLE ? "Avail" : "Unavail") : "Unknown"),
 		(status ? ((long long) status->rtt) / 1000.0 : NAN));
 
 	return 0;
 }
 
-static int cli_aor_iterator(const void *container, ao2_callback_fn callback, void *args)
+static int cli_aor_iterate(void *container, ao2_callback_fn callback, void *args)
 {
 	const char *aor_list = container;
 
 	return ast_sip_for_each_aor(aor_list, callback, args);
 }
 
-static int cli_print_aor_header(void *obj, void *arg, int flags)
+static void *cli_aor_retrieve_by_id(const char *id)
+{
+	return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "aor", id);
+}
+
+static const char *cli_aor_get_id(const void *obj)
+{
+	return ast_sorcery_object_get_id(obj);
+}
+
+static int cli_aor_print_header(void *obj, void *arg, int flags)
 {
 	struct ast_sip_cli_context *context = arg;
-	struct ast_sip_cli_formatter_entry *formatter_entry;
+	RAII_VAR(struct ast_sip_cli_formatter_entry *, formatter_entry, NULL, ao2_cleanup);
 
 	int indent = CLI_INDENT_TO_SPACES(context->indent_level);
 	int filler = CLI_LAST_TABSTOP - indent - 7;
 
-	if (!context->output_buffer) {
-		return -1;
-	}
+	ast_assert(context->output_buffer != NULL);
+
 	ast_str_append(&context->output_buffer, 0,
 		"%*s:  <Aor%*.*s>  <MaxContact>\n",
 		indent, "Aor", filler, filler, CLI_HEADER_FILLER);
@@ -612,27 +695,26 @@
 	if (context->recurse) {
 		context->indent_level++;
 		formatter_entry = ast_sip_lookup_cli_formatter("contact");
-		if (formatter_entry && formatter_entry->print_header) {
+		if (formatter_entry) {
 			formatter_entry->print_header(NULL, context, 0);
 		}
 		context->indent_level--;
 	}
-	return 0;
-}
-
-static int cli_print_aor_body(void *obj, void *arg, int flags)
+
+	return 0;
+}
+
+static int cli_aor_print_body(void *obj, void *arg, int flags)
 {
 	struct ast_sip_aor *aor = obj;
 	struct ast_sip_cli_context *context = arg;
-	struct ast_sip_cli_formatter_entry *formatter_entry;
+	RAII_VAR(struct ast_sip_cli_formatter_entry *, formatter_entry, NULL, ao2_cleanup);
 	int indent;
 	int flexwidth;
 
-	if (!context->output_buffer) {
-		return -1;
-	}
-
-	context->current_aor = aor;
+	ast_assert(context->output_buffer != NULL);
+
+//	context->current_aor = aor;
 
 	indent = CLI_INDENT_TO_SPACES(context->indent_level);
 	flexwidth = CLI_LAST_TABSTOP - indent - 12;
@@ -647,8 +729,8 @@
 		context->indent_level++;
 
 		formatter_entry = ast_sip_lookup_cli_formatter("contact");
-		if (formatter_entry && formatter_entry->print_body && formatter_entry->iterator) {
-			formatter_entry->iterator(aor, formatter_entry->print_body, context);
+		if (formatter_entry) {
+			formatter_entry->iterate(aor, formatter_entry->print_body, context);
 		}
 
 		context->indent_level--;
@@ -665,24 +747,6 @@
 
 	return 0;
 }
-
-static struct ast_sip_cli_formatter_entry cli_contact_formatter = {
-	.name = "contact",
-	.print_header = cli_print_contact_header,
-	.print_body = cli_print_contact_body,
-	.get_container = cli_get_contact_container,
-	.iterator = cli_contact_iterator,
-	.comparator = cli_contact_compare,
-};
-
-static struct ast_sip_cli_formatter_entry cli_aor_formatter = {
-	.name = "aor",
-	.print_header = cli_print_aor_header,
-	.print_body = cli_print_aor_body,
-	.get_container = cli_get_aor_container,
-	.iterator = cli_aor_iterator,
-	.comparator = ast_sorcery_object_id_compare,
-};
 
 static struct ast_cli_entry cli_commands[] = {
 	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Aors",
@@ -706,7 +770,14 @@
 		.command = "pjsip show contacts",
 		.usage = "Usage: pjsip show contacts\n"
 				 "       Show the configured PJSIP contacts\n"),
+	AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Contact",
+		.command = "pjsip show contact",
+		.usage = "Usage: pjsip show contact\n"
+				 "       Show the configured PJSIP contact\n"),
 };
+
+struct ast_sip_cli_formatter_entry *contact_formatter;
+struct ast_sip_cli_formatter_entry *aor_formatter;
 
 /*! \brief Initialize sorcery with location support */
 int ast_sip_initialize_sorcery_location(void)
@@ -737,23 +808,52 @@
 	ast_sorcery_object_field_register(sorcery, "aor", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, authenticate_qualify));
 	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, contacts_to_str, contacts_to_vl, 0, 0);
+	ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, contacts_to_str, contacts_to_var_list, 0, 0);
 	ast_sorcery_object_field_register(sorcery, "aor", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, mailboxes));
 	ast_sorcery_object_field_register(sorcery, "aor", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, outbound_proxy));
 	ast_sorcery_object_field_register(sorcery, "aor", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, support_path));
 
 	ast_sip_register_endpoint_formatter(&endpoint_aor_formatter);
-	ast_sip_register_cli_formatter(&cli_contact_formatter);
-	ast_sip_register_cli_formatter(&cli_aor_formatter);
+
+	contact_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
+	if (!contact_formatter) {
+		ast_log(LOG_ERROR, "Unable to allocate memory for contact_formatter\n");
+		return -1;
+	}
+	contact_formatter->name = "contact";
+	contact_formatter->print_header = cli_contact_print_header;
+	contact_formatter->print_body = cli_contact_print_body;
+	contact_formatter->get_container = cli_contact_get_container;
+	contact_formatter->iterate = cli_contact_iterate;
+	contact_formatter->get_id = cli_contact_get_id;
+	contact_formatter->retrieve_by_id = cli_contact_retrieve_by_id;
+
+	aor_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
+	if (!aor_formatter) {
+		ast_log(LOG_ERROR, "Unable to allocate memory for aor_formatter\n");
+		return -1;
+	}
+	aor_formatter->name = "aor";
+	aor_formatter->print_header = cli_aor_print_header;
+	aor_formatter->print_body = cli_aor_print_body;
+	aor_formatter->get_container = cli_aor_get_container;
+	aor_formatter->iterate = cli_aor_iterate;
+	aor_formatter->get_id = cli_aor_get_id;
+	aor_formatter->retrieve_by_id = cli_aor_retrieve_by_id;
+
+	ast_sip_register_cli_formatter(contact_formatter);
+	ast_sip_register_cli_formatter(aor_formatter);
 	ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
+
 	return 0;
 }
 
 int ast_sip_destroy_sorcery_location(void)
 {
 	ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
-	ast_sip_unregister_cli_formatter(&cli_contact_formatter);
-	ast_sip_unregister_cli_formatter(&cli_aor_formatter);
-	return 0;
-}
-
+	ast_sip_unregister_cli_formatter(contact_formatter);
+	ast_sip_unregister_cli_formatter(aor_formatter);
+
+	return 0;
+}
+

Modified: trunk/res/res_pjsip/pjsip_cli.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip/pjsip_cli.c?view=diff&rev=410288&r1=410287&r2=410288
==============================================================================
--- trunk/res/res_pjsip/pjsip_cli.c (original)
+++ trunk/res/res_pjsip/pjsip_cli.c Sat Mar  8 10:50:36 2014
@@ -31,15 +31,7 @@
 #include "asterisk/utils.h"
 #include "asterisk/sorcery.h"
 
-static struct ast_hashtab *formatter_registry;
-
-struct ast_sip_cli_formatter_entry *ast_sip_lookup_cli_formatter(const char *name)
-{
-	struct ast_sip_cli_formatter_entry fake_entry = {
-		.name = name,
-	};
-	return ast_hashtab_lookup(formatter_registry, &fake_entry);
-}
+static struct ao2_container *formatter_registry;
 
 int ast_sip_cli_print_sorcery_objectset(void *obj, void *arg, int flags)
 {
@@ -91,6 +83,7 @@
 }
 
 static char *complete_show_sorcery_object(struct ao2_container *container,
+	struct ast_sip_cli_formatter_entry *formatter_entry,
 	const char *word, int state)
 {
 	char *result = NULL;
@@ -101,9 +94,10 @@
 	void *object;
 
 	while ((object = ao2_t_iterator_next(&i, "iterate thru endpoints table"))) {
-		if (!strncasecmp(word, ast_sorcery_object_get_id(object), wordlen)
+		const char *id = formatter_entry->get_id(object);
+		if (!strncasecmp(word, id, wordlen)
 			&& ++which > state) {
-			result = ast_strdup(ast_sorcery_object_get_id(object));
+			result = ast_strdup(id);
 		}
 		ao2_t_ref(object, -1, "toss iterator endpoint ptr before break");
 		if (result) {
@@ -111,6 +105,7 @@
 		}
 	}
 	ao2_iterator_destroy(&i);
+
 	return result;
 }
 
@@ -123,20 +118,15 @@
 char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_sip_cli_formatter_entry *, formatter_entry, NULL, ao2_cleanup);
 	RAII_VAR(void *, object, NULL, ao2_cleanup);
 	int is_container = 0;
 	const char *cmd1;
 	const char *cmd2;
 	const char *object_id;
 	char formatter_type[64];
-	struct ast_sip_cli_formatter_entry *formatter_entry;
 
 	struct ast_sip_cli_context context = {
-		.peers_mon_online = 0,
-		.peers_mon_offline = 0,
-		.peers_unmon_online = 0,
-		.peers_unmon_offline = 0,
-		.a = a,
 		.indent_level = 0,
 		.show_details = 0,
 		.show_details_only_level_0 = 0,
@@ -203,7 +193,7 @@
 
 	if (cmd == CLI_GENERATE) {
 		ast_free(context.output_buffer);
-		return complete_show_sorcery_object(container, a->word, a->n);
+		return complete_show_sorcery_object(container, formatter_entry, a->word, a->n);
 	}
 
 	if (is_container) {
@@ -219,8 +209,8 @@
 			ast_cli(a->fd, "No object specified.\n");
 			return CLI_FAILURE;
 		}
-		object = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), formatter_type,
-			object_id);
+
+		object = formatter_entry->retrieve_by_id(object_id);
 		if (!object) {
 			dump_str_and_free(a->fd, context.output_buffer);
 			ast_cli(a->fd, "Unable to find object %s.\n\n", object_id);
@@ -234,44 +224,110 @@
 	return CLI_SUCCESS;
 }
 
-static int compare_formatters(const void *a, const void *b)
-{
-	const struct ast_sip_cli_formatter_entry *afe = a;
-	const struct ast_sip_cli_formatter_entry *bfe = b;
-	if (!afe || !bfe) {
-		ast_log(LOG_ERROR, "One of the arguments to compare_formatters was NULL\n");
-		return -1;
-	}
-	return strcmp(afe->name, bfe->name);
-}
-
-static unsigned int hash_formatters(const void *a)
-{
-	const struct ast_sip_cli_formatter_entry *afe = a;
-	return ast_hashtab_hash_string(afe->name);
+static int formatter_sort(const void *obj, const void *arg, int flags)
+{
+	const struct ast_sip_cli_formatter_entry *left_obj = obj;
+	const struct ast_sip_cli_formatter_entry *right_obj = arg;
+	const char *right_key = arg;
+	int cmp = 0;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = right_obj->name;
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcmp(left_obj->name, right_key);
+		break;
+	case OBJ_SEARCH_PARTIAL_KEY:
+		cmp = strncmp(left_obj->name, right_key, strlen(right_key));
+		break;
+	default:
+		cmp = 0;
+		break;
+	}
+
+	return cmp;
+}
+
+static int formatter_compare(void *obj, void *arg, int flags)
+{
+	const struct ast_sip_cli_formatter_entry *left_obj = obj;
+	const struct ast_sip_cli_formatter_entry *right_obj = arg;
+	const char *right_key = arg;
+	int cmp = 0;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = right_obj->name;

[... 960 lines stripped ...]



More information about the asterisk-commits mailing list