| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2013, Digium, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Kevin Harwell <kharwell@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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*** MODULEINFO
 | 
					
						
							| 
									
										
										
										
											2013-07-02 20:34:42 +00:00
										 |  |  | 	<depend>pjproject</depend> | 
					
						
							| 
									
										
										
										
											2013-07-30 18:14:50 +00:00
										 |  |  | 	<depend>res_pjsip</depend> | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 	<support_level>core</support_level> | 
					
						
							|  |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <pjsip.h>
 | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | #include <pjsip_ua.h>
 | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk/cli.h"
 | 
					
						
							|  |  |  | #include "asterisk/config.h"
 | 
					
						
							|  |  |  | #include "asterisk/manager.h"
 | 
					
						
							|  |  |  | #include "asterisk/module.h"
 | 
					
						
							|  |  |  | #include "asterisk/pbx.h"
 | 
					
						
							| 
									
										
										
										
											2013-07-30 18:14:50 +00:00
										 |  |  | #include "asterisk/res_pjsip.h"
 | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | #include "asterisk/res_pjsip_session.h"
 | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | #include "asterisk/sorcery.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-02 12:40:03 +00:00
										 |  |  | /*** DOCUMENTATION
 | 
					
						
							|  |  |  | 	<manager name="PJSIPNotify" language="en_US"> | 
					
						
							|  |  |  | 		<synopsis> | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | 			Send a NOTIFY to either an endpoint, an arbitrary URI, or inside a SIP dialog. | 
					
						
							| 
									
										
										
										
											2013-08-02 12:40:03 +00:00
										 |  |  | 		</synopsis> | 
					
						
							|  |  |  | 		<syntax> | 
					
						
							|  |  |  | 			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /> | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 			<parameter name="Endpoint" required="false"> | 
					
						
							| 
									
										
										
										
											2013-08-02 12:40:03 +00:00
										 |  |  | 				<para>The endpoint to which to send the NOTIFY.</para> | 
					
						
							|  |  |  | 			</parameter> | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 			<parameter name="URI" required="false"> | 
					
						
							|  |  |  | 				<para>Abritrary URI to which to send the NOTIFY.</para> | 
					
						
							|  |  |  | 			</parameter> | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | 			<parameter name="channel" required="false"> | 
					
						
							|  |  |  | 				<para>Channel name to send the NOTIFY. Must be a PJSIP channel.</para> | 
					
						
							|  |  |  | 			</parameter> | 
					
						
							| 
									
										
										
										
											2014-06-10 16:06:12 +00:00
										 |  |  | 			<parameter name="Variable" required="true"> | 
					
						
							|  |  |  | 				<para>Appends variables as headers/content to the NOTIFY. If the variable is | 
					
						
							|  |  |  | 				named <literal>Content</literal>, then the value will compose the body | 
					
						
							|  |  |  | 				of the message if another variable sets <literal>Content-Type</literal>. | 
					
						
							|  |  |  | 				<replaceable>name</replaceable>=<replaceable>value</replaceable></para> | 
					
						
							|  |  |  | 			</parameter> | 
					
						
							| 
									
										
										
										
											2013-08-02 12:40:03 +00:00
										 |  |  | 		</syntax> | 
					
						
							|  |  |  | 		<description> | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | 			<para>Sends a NOTIFY to an endpoint, an arbitrary URI, or inside a SIP dialog.</para> | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 			<para>All parameters for this event must be specified in the body of this | 
					
						
							|  |  |  | 			request	via multiple <literal>Variable: name=value</literal> sequences.</para> | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | 			<note><para>One (and only one) of <literal>Endpoint</literal>, | 
					
						
							|  |  |  | 			<literal>URI</literal>, or <literal>Channel</literal> must be specified. | 
					
						
							|  |  |  | 			If <literal>URI</literal> is used, the default outbound endpoint will be used | 
					
						
							|  |  |  | 			to send the message. If the default outbound endpoint isn't configured, this command | 
					
						
							|  |  |  | 			can not send to an arbitrary URI.</para></note> | 
					
						
							| 
									
										
										
										
											2013-08-02 12:40:03 +00:00
										 |  |  | 		</description> | 
					
						
							|  |  |  | 	</manager> | 
					
						
							| 
									
										
										
										
											2013-09-28 22:57:17 +00:00
										 |  |  | 	<configInfo name="res_pjsip_notify" language="en_US"> | 
					
						
							|  |  |  | 		<synopsis>Module that supports sending NOTIFY requests to endpoints from external sources</synopsis> | 
					
						
							|  |  |  | 		<configFile name="pjsip_notify.conf"> | 
					
						
							|  |  |  | 			<configObject name="general"> | 
					
						
							|  |  |  | 				<synopsis>Unused, but reserved.</synopsis> | 
					
						
							|  |  |  | 			</configObject> | 
					
						
							|  |  |  | 			<configObject name="notify"> | 
					
						
							|  |  |  | 				<synopsis>Configuration of a NOTIFY request.</synopsis> | 
					
						
							|  |  |  | 				<description> | 
					
						
							|  |  |  | 					<para>Each key-value pair in a <literal>notify</literal> | 
					
						
							|  |  |  | 					configuration section defines either a SIP header to send | 
					
						
							|  |  |  | 					in the request or a line of content in the request message | 
					
						
							|  |  |  | 					body. A key of <literal>Content</literal> is treated | 
					
						
							|  |  |  | 					as part of the message body and is appended in sequential | 
					
						
							|  |  |  | 					order; any other header is treated as part of the SIP | 
					
						
							|  |  |  | 					request.</para> | 
					
						
							|  |  |  | 				</description> | 
					
						
							| 
									
										
										
										
											2017-12-12 13:55:12 -05:00
										 |  |  | 				<configOption name=""> | 
					
						
							| 
									
										
										
										
											2013-09-28 22:57:17 +00:00
										 |  |  | 					<synopsis>A key/value pair to add to a NOTIFY request.</synopsis> | 
					
						
							|  |  |  | 					<description> | 
					
						
							|  |  |  | 						<para>If the key is <literal>Content</literal>, | 
					
						
							|  |  |  | 						it will be treated as part of the message body. Otherwise, | 
					
						
							|  |  |  | 						it will be added as a header in the NOTIFY request.</para> | 
					
						
							|  |  |  | 						<para>The following headers are reserved and cannot be | 
					
						
							|  |  |  | 						specified:</para> | 
					
						
							|  |  |  | 						<enumlist> | 
					
						
							|  |  |  | 							<enum name="Call-ID" /> | 
					
						
							|  |  |  | 							<enum name="Contact" /> | 
					
						
							|  |  |  | 							<enum name="CSeq" /> | 
					
						
							|  |  |  | 							<enum name="To" /> | 
					
						
							|  |  |  | 							<enum name="From" /> | 
					
						
							|  |  |  | 							<enum name="Record-Route" /> | 
					
						
							|  |  |  | 							<enum name="Route" /> | 
					
						
							|  |  |  | 							<enum name="Via" /> | 
					
						
							|  |  |  | 						</enumlist> | 
					
						
							|  |  |  | 					</description> | 
					
						
							|  |  |  | 				</configOption> | 
					
						
							|  |  |  | 			</configObject> | 
					
						
							|  |  |  | 		</configFile> | 
					
						
							|  |  |  | 	</configInfo> | 
					
						
							| 
									
										
										
										
											2013-08-02 12:40:03 +00:00
										 |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | #define CONTENT_TYPE_SIZE 64
 | 
					
						
							|  |  |  | #define CONTENT_SIZE 512
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief The configuration file containing NOTIFY payload types to send. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-09-28 22:57:17 +00:00
										 |  |  | static const char notify_config[] = "pjsip_notify.conf"; | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | struct notify_option_item { | 
					
						
							|  |  |  | 	const char *name; | 
					
						
							|  |  |  | 	const char *value; | 
					
						
							|  |  |  | 	char buf[0]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct notify_option { | 
					
						
							|  |  |  | 	/*! Contains header and/or content information */ | 
					
						
							|  |  |  | 	struct ao2_container *items; | 
					
						
							|  |  |  | 	/*! The name of the notify option */ | 
					
						
							|  |  |  | 	char name[0]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int notify_option_hash(const void *obj, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const struct notify_option *option = obj; | 
					
						
							|  |  |  | 	return ast_str_case_hash(flags & OBJ_KEY ? obj : option->name); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int notify_option_cmp(void *obj, void *arg, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_option *option1 = obj; | 
					
						
							|  |  |  | 	struct notify_option *option2 = arg; | 
					
						
							|  |  |  | 	const char *key = flags & OBJ_KEY ? arg : option2->name; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return strcasecmp(option1->name, key) ? 0 : CMP_MATCH; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void notify_option_destroy(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_option *option = obj; | 
					
						
							|  |  |  | 	ao2_cleanup(option->items); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *notify_option_alloc(const char *category) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int category_size = strlen(category) + 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	struct notify_option *option = ao2_alloc( | 
					
						
							|  |  |  | 		sizeof(*option) + category_size, notify_option_destroy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!option) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_copy_string(option->name, category, category_size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(option->items = ao2_container_alloc_list( | 
					
						
							|  |  |  | 		      AO2_ALLOC_OPT_LOCK_NOLOCK, | 
					
						
							|  |  |  | 		      AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW, NULL, NULL))) { | 
					
						
							|  |  |  | 		ao2_cleanup(option); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return option; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *notify_option_find(struct ao2_container *container, const char *category) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ao2_find(container, category, OBJ_KEY); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int notify_option_handler(const struct aco_option *opt, | 
					
						
							|  |  |  | 				 struct ast_variable *var, void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_option *option = obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int name_size = strlen(var->name) + 1; | 
					
						
							|  |  |  | 	int value_size = strlen(var->value) + 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	RAII_VAR(struct notify_option_item *, item, | 
					
						
							|  |  |  | 		 ao2_alloc(sizeof(*item) + name_size + value_size, | 
					
						
							|  |  |  | 			   NULL), ao2_cleanup); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	item->name = item->buf; | 
					
						
							|  |  |  | 	item->value = item->buf + name_size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_copy_string(item->buf, var->name, name_size); | 
					
						
							|  |  |  | 	ast_copy_string(item->buf + name_size, var->value, value_size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ao2_link(option->items, item)) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct notify_cfg { | 
					
						
							|  |  |  | 	struct ao2_container *notify_options; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void notify_cfg_destroy(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_cfg *cfg = obj; | 
					
						
							|  |  |  | 	ao2_cleanup(cfg->notify_options); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *notify_cfg_alloc(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_cfg *cfg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(cfg = ao2_alloc(sizeof(*cfg), notify_cfg_destroy))) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-16 15:11:06 -04:00
										 |  |  | 	cfg->notify_options = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, | 
					
						
							|  |  |  | 		20, notify_option_hash, NULL, notify_option_cmp); | 
					
						
							|  |  |  | 	if (!cfg->notify_options) { | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		ao2_cleanup(cfg); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return cfg; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct aco_type notify_option = { | 
					
						
							|  |  |  | 	.type = ACO_ITEM, | 
					
						
							|  |  |  | 	.name = "notify", | 
					
						
							| 
									
										
										
										
											2017-12-12 13:55:12 -05:00
										 |  |  | 	.category_match = ACO_BLACKLIST_EXACT, | 
					
						
							|  |  |  | 	.category = "general", | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 	.item_offset = offsetof(struct notify_cfg, notify_options), | 
					
						
							|  |  |  | 	.item_alloc = notify_option_alloc, | 
					
						
							|  |  |  | 	.item_find = notify_option_find | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct aco_type *notify_options[] = ACO_TYPES(¬ify_option); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct aco_file module_conf = { | 
					
						
							|  |  |  | 	.filename = notify_config, | 
					
						
							|  |  |  | 	.types = ACO_TYPES(¬ify_option), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AO2_GLOBAL_OBJ_STATIC(globals); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CONFIG_INFO_STANDARD(notify_cfg, globals, notify_cfg_alloc, | 
					
						
							|  |  |  | 	.files = ACO_FILES(&module_conf) | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Structure to hold task data for notifications. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct notify_data { | 
					
						
							|  |  |  | 	/*! The endpoint being notified */ | 
					
						
							|  |  |  | 	struct ast_sip_endpoint *endpoint; | 
					
						
							|  |  |  | 	/*! The info of headers, types and content */ | 
					
						
							|  |  |  | 	void *info; | 
					
						
							|  |  |  | 	/*! Function to help build notify request */ | 
					
						
							|  |  |  | 	void (*build_notify)(pjsip_tx_data *, void *); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Destroy the notify CLI data releasing any resources. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void notify_cli_data_destroy(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_data *data = obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_cleanup(data->endpoint); | 
					
						
							|  |  |  | 	ao2_cleanup(data->info); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Structure to hold task data for notifications (URI variant) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct notify_uri_data { | 
					
						
							|  |  |  | 	char *uri; | 
					
						
							|  |  |  | 	void *info; | 
					
						
							|  |  |  | 	void (*build_notify)(pjsip_tx_data *, void *); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Structure to hold task data for notifications (channel variant) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct notify_channel_data { | 
					
						
							|  |  |  | 	struct ast_sip_session *session; | 
					
						
							|  |  |  | 	void *info; | 
					
						
							|  |  |  | 	void (*build_notify)(pjsip_tx_data *, void *); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | static void notify_cli_uri_data_destroy(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_uri_data *data = obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_free(data->uri); | 
					
						
							|  |  |  | 	ao2_cleanup(data->info); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Destroy the notify CLI data releasing any resources (URI variant) | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | static void build_cli_notify(pjsip_tx_data *tdata, void *info); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Construct a notify data object for CLI. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct notify_data* notify_cli_data_create( | 
					
						
							|  |  |  | 	struct ast_sip_endpoint *endpoint, void *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_data *data = ao2_alloc(sizeof(*data), | 
					
						
							|  |  |  | 					     notify_cli_data_destroy); | 
					
						
							|  |  |  | 	if (!data) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data->endpoint = endpoint; | 
					
						
							|  |  |  | 	ao2_ref(data->endpoint, +1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data->info = info; | 
					
						
							|  |  |  | 	ao2_ref(data->info, +1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data->build_notify = build_cli_notify; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Construct a notify URI data object for CLI. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct notify_uri_data* notify_cli_uri_data_create( | 
					
						
							|  |  |  | 	const char *uri, void *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_uri_data *data = ao2_alloc(sizeof(*data), | 
					
						
							|  |  |  | 		notify_cli_uri_data_destroy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!data) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data->uri = ast_strdup(uri); | 
					
						
							|  |  |  | 	if (!data->uri) { | 
					
						
							|  |  |  | 		ao2_ref(data, -1); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data->info = info; | 
					
						
							|  |  |  | 	ao2_ref(data->info, +1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data->build_notify = build_cli_notify; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Destroy the notify AMI data releasing any resources. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void notify_ami_data_destroy(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_data *data = obj; | 
					
						
							|  |  |  | 	struct ast_variable *info = data->info; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_cleanup(data->endpoint); | 
					
						
							|  |  |  | 	ast_variables_destroy(info); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Destroy the notify AMI URI data releasing any resources. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void notify_ami_uri_data_destroy(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_uri_data *data = obj; | 
					
						
							|  |  |  | 	struct ast_variable *info = data->info; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_free(data->uri); | 
					
						
							|  |  |  | 	ast_variables_destroy(info); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Destroy the notify AMI channel data releasing any resources. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void notify_ami_channel_data_destroy(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_channel_data *data = obj; | 
					
						
							|  |  |  | 	struct ast_variable *info = data->info; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_cleanup(data->session); | 
					
						
							|  |  |  | 	ast_variables_destroy(info); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | static void build_ami_notify(pjsip_tx_data *tdata, void *info); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Construct a notify data object for AMI. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct notify_data* notify_ami_data_create( | 
					
						
							|  |  |  | 	struct ast_sip_endpoint *endpoint, void *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_data *data = ao2_alloc(sizeof(*data), | 
					
						
							|  |  |  | 					     notify_ami_data_destroy); | 
					
						
							|  |  |  | 	if (!data) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data->endpoint = endpoint; | 
					
						
							|  |  |  | 	ao2_ref(data->endpoint, +1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data->info = info; | 
					
						
							|  |  |  | 	data->build_notify = build_ami_notify; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Construct a notify URI data object for AMI. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct notify_uri_data* notify_ami_uri_data_create( | 
					
						
							|  |  |  | 	const char *uri, void *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_uri_data *data = ao2_alloc(sizeof(*data), | 
					
						
							|  |  |  | 							notify_ami_uri_data_destroy); | 
					
						
							|  |  |  | 	if (!data) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data->uri = ast_strdup(uri); | 
					
						
							|  |  |  | 	if (!data->uri) { | 
					
						
							|  |  |  | 		ao2_ref(data, -1); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data->info = info; | 
					
						
							|  |  |  | 	data->build_notify = build_ami_notify; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Construct a notify channel data object for AMI. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct notify_channel_data *notify_ami_channel_data_create( | 
					
						
							|  |  |  | 	struct ast_sip_session *session, void *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_channel_data *data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data = ao2_alloc_options(sizeof(*data), notify_ami_channel_data_destroy, | 
					
						
							|  |  |  | 		AO2_ALLOC_OPT_LOCK_NOLOCK); | 
					
						
							|  |  |  | 	if (!data) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data->session = session; | 
					
						
							|  |  |  | 	data->info = info; | 
					
						
							|  |  |  | 	data->build_notify = build_ami_notify; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Checks if the given header name is not allowed. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \details Some headers are not allowed to be set by the user within the | 
					
						
							|  |  |  |  *          scope of a NOTIFY request.  If the given var header name is | 
					
						
							|  |  |  |  *          found in the "not allowed" list then return true. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int not_allowed(const char *name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	static const char *names[] = { | 
					
						
							|  |  |  | 		"Call-ID", | 
					
						
							|  |  |  | 		"Contact", | 
					
						
							|  |  |  | 		"CSeq", | 
					
						
							|  |  |  | 		"To", | 
					
						
							|  |  |  | 		"From", | 
					
						
							|  |  |  | 		"Record-Route", | 
					
						
							|  |  |  | 		"Route", | 
					
						
							|  |  |  | 		"Request-URI", | 
					
						
							|  |  |  | 		"Via", | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ARRAY_LEN(names); ++i) { | 
					
						
							|  |  |  | 		if (!strcasecmp(name, names[i])) { | 
					
						
							|  |  |  | 			return 1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-10 15:43:24 -05:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Check if the given header can be added to a message more than once. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int multiple_headers_allowed(const char *name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* This can be extended to include additional headers */ | 
					
						
							|  |  |  | 	return strcasecmp("Event", name); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief If a content type was specified add it and the content body to the | 
					
						
							|  |  |  |  *        NOTIFY request. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void build_notify_body(pjsip_tx_data *tdata, struct ast_str *content_type, | 
					
						
							|  |  |  | 			      struct ast_str *content) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (content_type) { | 
					
						
							|  |  |  | 		char *p; | 
					
						
							|  |  |  | 		struct ast_sip_body body; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (content) { | 
					
						
							|  |  |  | 			body.body_text = ast_str_buffer(content); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		body.type = ast_str_buffer(content_type); | 
					
						
							|  |  |  | 		if ((p = strchr(body.type, '/'))) { | 
					
						
							|  |  |  | 			*p++ = '\0'; | 
					
						
							|  |  |  | 			body.subtype = p; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ast_sip_add_body(tdata, &body); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Build the NOTIFY request adding content or header info. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void build_notify(pjsip_tx_data *tdata, const char *name, const char *value, | 
					
						
							|  |  |  | 			 struct ast_str **content_type, struct ast_str **content) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (not_allowed(name)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Cannot specify %s header, " | 
					
						
							|  |  |  | 			"ignoring\n", name); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!strcasecmp(name, "Content-type")) { | 
					
						
							|  |  |  | 		if (!(*content_type)) { | 
					
						
							|  |  |  | 			*content_type = ast_str_create(CONTENT_TYPE_SIZE); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ast_str_set(content_type, 0,"%s", value); | 
					
						
							|  |  |  | 	} else if (!strcasecmp(name, "Content")) { | 
					
						
							|  |  |  | 		if (!(*content)) { | 
					
						
							|  |  |  | 			*content = ast_str_create(CONTENT_SIZE); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (ast_str_strlen(*content)) { | 
					
						
							|  |  |  | 			ast_str_append(content, 0, "\r\n"); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ast_str_append(content, 0, "%s", value); | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2020-01-10 15:43:24 -05:00
										 |  |  | 		/* See if there is an existing one */ | 
					
						
							|  |  |  | 		if (!multiple_headers_allowed(name)) { | 
					
						
							|  |  |  | 			pj_str_t hdr_name; | 
					
						
							|  |  |  | 			pj_cstr(&hdr_name, name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (pjsip_msg_find_hdr_by_name(tdata->msg, &hdr_name, NULL)) { | 
					
						
							|  |  |  | 				ast_log(LOG_ERROR, "Only one '%s' header can be added to a NOTIFY, " | 
					
						
							|  |  |  | 						"ignoring \"%s: %s\"\n", name, name, value); | 
					
						
							|  |  |  | 				return; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		ast_sip_add_header(tdata, name, value); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Build the NOTIFY request from CLI info adding header and content | 
					
						
							|  |  |  |  *        when specified. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void build_cli_notify(pjsip_tx_data *tdata, void *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_option *option = info; | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_str *, content_type, NULL, ast_free); | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_str *, content, NULL, ast_free); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	struct notify_option_item *item; | 
					
						
							|  |  |  | 	struct ao2_iterator i = ao2_iterator_init(option->items, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while ((item = ao2_iterator_next(&i))) { | 
					
						
							|  |  |  | 		build_notify(tdata, item->name, item->value, | 
					
						
							|  |  |  | 			     &content_type, &content); | 
					
						
							|  |  |  | 		ao2_cleanup(item); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ao2_iterator_destroy(&i); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	build_notify_body(tdata, content_type, content); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Build the NOTIFY request from AMI info adding header and content | 
					
						
							|  |  |  |  *        when specified. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void build_ami_notify(pjsip_tx_data *tdata, void *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_variable *vars = info; | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_str *, content_type, NULL, ast_free); | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_str *, content, NULL, ast_free); | 
					
						
							|  |  |  | 	struct ast_variable *i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = vars; i; i = i->next) { | 
					
						
							| 
									
										
										
										
											2014-06-10 16:06:12 +00:00
										 |  |  | 		if (!strcasecmp(i->name, "Content-Length")) { | 
					
						
							|  |  |  | 			ast_log(LOG_NOTICE, "It is not necessary to specify Content-Length, ignoring.\n"); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		build_notify(tdata, i->name, i->value, | 
					
						
							|  |  |  | 			     &content_type, &content); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	build_notify_body(tdata, content_type, content); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Build and send a NOTIFY request to a contact. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int notify_contact(void *obj, void *arg, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_sip_contact *contact = obj; | 
					
						
							|  |  |  | 	struct notify_data *data = arg; | 
					
						
							|  |  |  | 	pjsip_tx_data *tdata; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_sip_create_request("NOTIFY", NULL, data->endpoint, | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 				   NULL, contact, &tdata)) { | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request for " | 
					
						
							|  |  |  | 			"contact %s\n",	contact->uri); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_sip_add_header(tdata, "Subscription-State", "terminated"); | 
					
						
							|  |  |  | 	data->build_notify(tdata, data->info); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-15 13:16:10 +00:00
										 |  |  | 	if (ast_sip_send_request(tdata, NULL, data->endpoint, NULL, NULL)) { | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request for " | 
					
						
							|  |  |  | 			"contact %s\n",	contact->uri); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Send a NOTIFY request to the endpoint. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-11-19 09:54:42 +01:00
										 |  |  |  * \details Iterates over an endpoint's AORs sending a NOTIFY request | 
					
						
							|  |  |  |  *          with the appropriate payload information to each contact. | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | static int notify_endpoint(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	RAII_VAR(struct notify_data *, data, obj, ao2_cleanup); | 
					
						
							|  |  |  | 	char *aor_name, *aors; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(data->endpoint->aors)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to NOTIFY - " | 
					
						
							|  |  |  | 			"endpoint has no configured AORs\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	aors = ast_strdupa(data->endpoint->aors); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												res_pjsip:  Strip spaces from items parsed from comma-separated lists
Configurations like "aors = a, b, c" were either ignoring everything after "a"
or trying to look up " b".  Same for mailboxes,  ciphers, contacts and a few
others.
To fix, all the strsep(©, ",") calls have been wrapped in ast_strip.  To
facilitate this, ast_strip, ast_skip_blanks and ast_skip_nonblanks were
updated to handle null pointers.
In some cases, an ast_strlen_zero() test was added to skip consecutive commas.
There was also an attempt to ast_free an ast_strdupa'd string in
ast_sip_for_each_aor which was causing a SEGV.  I removed it.
Although this issue was reported for realtime, the issue was in the res_pjsip
modules so all config mechanisms were affected.
ASTERISK-25829 #close
Reported-by: Mateusz Kowalski
Change-Id: I0b22a2cf22a7c1c50d4ecacbfa540155bec0e7a2
											
										 
											2016-03-06 13:38:41 -07:00
										 |  |  | 	while ((aor_name = ast_strip(strsep(&aors, ",")))) { | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		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; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ao2_callback(contacts, OBJ_NODATA, notify_contact, data); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Send a notify request to the URI. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int notify_uri(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	RAII_VAR(struct notify_uri_data *, data, obj, ao2_cleanup); | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_sip_endpoint *, endpoint, | 
					
						
							|  |  |  | 		ast_sip_default_outbound_endpoint(), ao2_cleanup); | 
					
						
							|  |  |  | 	pjsip_tx_data *tdata; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!endpoint) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "No default outbound endpoint set, can not send " | 
					
						
							|  |  |  | 			"NOTIFY requests to arbitrary URIs.\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(data->uri)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to NOTIFY - URI is blank.\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_sip_create_request("NOTIFY", NULL, endpoint, | 
					
						
							|  |  |  | 				   data->uri, NULL, &tdata)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request for " | 
					
						
							|  |  |  | 			"uri %s\n",	data->uri); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_sip_add_header(tdata, "Subscription-State", "terminated"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data->build_notify(tdata, data->info); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL)) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request for " | 
					
						
							|  |  |  | 			"uri %s\n",	data->uri); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Send a notify request to a channel. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int notify_channel(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	RAII_VAR(struct notify_channel_data *, data, obj, ao2_cleanup); | 
					
						
							|  |  |  | 	pjsip_tx_data *tdata; | 
					
						
							|  |  |  | 	struct pjsip_dialog *dlg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!data->session->channel | 
					
						
							|  |  |  | 		|| !data->session->inv_session | 
					
						
							|  |  |  | 		|| data->session->inv_session->state < PJSIP_INV_STATE_EARLY | 
					
						
							|  |  |  | 		|| data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_debug(1, "Sending notify on channel %s\n", ast_channel_name(data->session->channel)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dlg = data->session->inv_session->dlg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_sip_create_request("NOTIFY", dlg, NULL, NULL, NULL, &tdata)) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_sip_add_header(tdata, "Subscription-State", "terminated"); | 
					
						
							|  |  |  | 	data->build_notify(tdata, data->info); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_sip_send_request(tdata, dlg, NULL, NULL, NULL)) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | enum notify_result { | 
					
						
							|  |  |  | 	SUCCESS, | 
					
						
							|  |  |  | 	INVALID_ENDPOINT, | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | 	INVALID_CHANNEL, | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 	ALLOC_ERROR, | 
					
						
							|  |  |  | 	TASK_PUSH_ERROR | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct notify_data *(*task_data_create)( | 
					
						
							|  |  |  | 	struct ast_sip_endpoint *, void *info); | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | typedef struct notify_uri_data *(*task_uri_data_create)( | 
					
						
							|  |  |  | 	const char *uri, void *info); | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | typedef struct notify_channel_data *(*task_channel_data_create)( | 
					
						
							|  |  |  | 	struct ast_sip_session *session, void *info); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Send a NOTIFY request to the endpoint within a threaded task. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static enum notify_result push_notify(const char *endpoint_name, void *info, | 
					
						
							|  |  |  | 				      task_data_create data_create) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); | 
					
						
							|  |  |  | 	struct notify_data *data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(endpoint = ast_sorcery_retrieve_by_id( | 
					
						
							|  |  |  | 		      ast_sip_get_sorcery(), "endpoint", endpoint_name))) { | 
					
						
							|  |  |  | 		return INVALID_ENDPOINT; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(data = data_create(endpoint, info))) { | 
					
						
							|  |  |  | 		return ALLOC_ERROR; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_sip_push_task(NULL, notify_endpoint, data)) { | 
					
						
							|  |  |  | 		ao2_cleanup(data); | 
					
						
							|  |  |  | 		return TASK_PUSH_ERROR; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Send a NOTIFY request to the URI within an threaded task. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static enum notify_result push_notify_uri(const char *uri, void *info, | 
					
						
							|  |  |  | 	task_uri_data_create data_create) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_uri_data *data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(data = data_create(uri, info))) { | 
					
						
							|  |  |  | 		return ALLOC_ERROR; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_sip_push_task(NULL, notify_uri, data)) { | 
					
						
							|  |  |  | 		ao2_cleanup(data); | 
					
						
							|  |  |  | 		return TASK_PUSH_ERROR; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Send a NOTIFY request in a channel within an threaded task. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static enum notify_result push_notify_channel(const char *channel_name, void *info, | 
					
						
							|  |  |  | 	task_channel_data_create data_create) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct notify_channel_data *data; | 
					
						
							|  |  |  | 	struct ast_channel *ch; | 
					
						
							|  |  |  | 	struct ast_sip_session *session; | 
					
						
							|  |  |  | 	struct ast_sip_channel_pvt *ch_pvt; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* note: this increases the refcount of the channel */ | 
					
						
							|  |  |  | 	ch = ast_channel_get_by_name(channel_name); | 
					
						
							|  |  |  | 	if (!ch) { | 
					
						
							|  |  |  | 		ast_debug(1, "No channel found with name %s", channel_name); | 
					
						
							|  |  |  | 		return INVALID_CHANNEL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (strcmp(ast_channel_tech(ch)->type, "PJSIP")) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Channel was a non-PJSIP channel: %s\n", channel_name); | 
					
						
							|  |  |  | 		ast_channel_unref(ch); | 
					
						
							|  |  |  | 		return INVALID_CHANNEL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_channel_lock(ch); | 
					
						
							|  |  |  | 	ch_pvt = ast_channel_tech_pvt(ch); | 
					
						
							|  |  |  | 	session = ch_pvt->session; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!session || !session->inv_session | 
					
						
							|  |  |  | 			|| session->inv_session->state < PJSIP_INV_STATE_EARLY | 
					
						
							|  |  |  | 			|| session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) { | 
					
						
							|  |  |  | 		ast_debug(1, "No active session for channel %s\n", channel_name); | 
					
						
							|  |  |  | 		ast_channel_unlock(ch); | 
					
						
							|  |  |  | 		ast_channel_unref(ch); | 
					
						
							|  |  |  | 		return INVALID_CHANNEL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(session, +1); | 
					
						
							|  |  |  | 	ast_channel_unlock(ch); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* don't keep a reference to the channel, we've got a reference to the session */ | 
					
						
							|  |  |  | 	ast_channel_unref(ch); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * data_create will take ownership of the session, | 
					
						
							|  |  |  | 	 * and take care of releasing the ref. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	data = data_create(session, info); | 
					
						
							|  |  |  | 	if (!data) { | 
					
						
							|  |  |  | 		ao2_ref(session, -1); | 
					
						
							|  |  |  | 		return ALLOC_ERROR; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_sip_push_task(session->serializer, notify_channel, data)) { | 
					
						
							|  |  |  | 		ao2_ref(data, -1); | 
					
						
							|  |  |  | 		return TASK_PUSH_ERROR; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Do completion on the endpoint. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-26 17:18:38 -04:00
										 |  |  | static char *cli_complete_endpoint(const char *word) | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	int wordlen = strlen(word); | 
					
						
							| 
									
										
										
										
											2018-10-26 17:18:38 -04:00
										 |  |  | 	struct ao2_container * endpoints; | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 	struct ast_sip_endpoint *endpoint; | 
					
						
							| 
									
										
										
										
											2018-10-26 17:18:38 -04:00
										 |  |  | 	struct ao2_iterator i; | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-26 17:18:38 -04:00
										 |  |  | 	endpoints = ast_sorcery_retrieve_by_prefix(ast_sip_get_sorcery(), | 
					
						
							|  |  |  | 		"endpoint", word, wordlen); | 
					
						
							|  |  |  | 	if (endpoints == NULL) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-26 17:18:38 -04:00
										 |  |  | 	i = ao2_iterator_init(endpoints, 0); | 
					
						
							|  |  |  | 	while ((endpoint = ao2_iterator_next(&i))) { | 
					
						
							|  |  |  | 		ast_cli_completion_add( | 
					
						
							|  |  |  | 			ast_strdup(ast_sorcery_object_get_id(endpoint))); | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		ao2_cleanup(endpoint); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ao2_iterator_destroy(&i); | 
					
						
							| 
									
										
										
										
											2018-10-26 17:18:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(endpoints, -1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Do completion on the notify CLI command. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static char *cli_complete_notify(const char *line, const char *word, | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 				 int pos, int state, int using_uri) | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	char *c = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-02 12:40:03 +00:00
										 |  |  | 	if (pos == 3) { | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		int which = 0; | 
					
						
							|  |  |  | 		int wordlen = strlen(word); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		RAII_VAR(struct notify_cfg *, cfg, | 
					
						
							|  |  |  | 			 ao2_global_obj_ref(globals), ao2_cleanup); | 
					
						
							|  |  |  | 		struct notify_option *option; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* do completion for notify type */ | 
					
						
							|  |  |  | 		struct ao2_iterator i = ao2_iterator_init(cfg->notify_options, 0); | 
					
						
							|  |  |  | 		while ((option = ao2_iterator_next(&i))) { | 
					
						
							|  |  |  | 			if (!strncasecmp(word, option->name, wordlen) && ++which > state) { | 
					
						
							|  |  |  | 				c = ast_strdup(option->name); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			ao2_cleanup(option); | 
					
						
							|  |  |  | 			if (c) { | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ao2_iterator_destroy(&i); | 
					
						
							|  |  |  | 		return c; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (pos == 4) { | 
					
						
							|  |  |  | 		int wordlen = strlen(word); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (ast_strlen_zero(word)) { | 
					
						
							|  |  |  | 		    if (state == 0) { | 
					
						
							|  |  |  | 		        c = ast_strdup("endpoint"); | 
					
						
							|  |  |  | 		    } else if (state == 1) { | 
					
						
							|  |  |  | 		        c = ast_strdup("uri"); | 
					
						
							|  |  |  | 		    } | 
					
						
							|  |  |  | 		} else if (state == 0) { | 
					
						
							|  |  |  | 		    if (!strncasecmp(word, "endpoint", wordlen)) { | 
					
						
							|  |  |  | 		        c = ast_strdup("endpoint"); | 
					
						
							|  |  |  | 		    } else if (!strncasecmp(word, "uri", wordlen)) { | 
					
						
							|  |  |  | 		        c = ast_strdup("uri"); | 
					
						
							|  |  |  | 		    } | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return c; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-26 17:18:38 -04:00
										 |  |  | 	return pos > 4 && !using_uri ? cli_complete_endpoint(word) : NULL; | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief CLI command to send a SIP notify to an endpoint. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \details Attempts to match the "type" given in the CLI command to a | 
					
						
							|  |  |  |  *          configured one.  If found, sends a NOTIFY to the endpoint | 
					
						
							|  |  |  |  *          with the associated payload. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	RAII_VAR(struct notify_cfg *, cfg, NULL, ao2_cleanup); | 
					
						
							|  |  |  | 	RAII_VAR(struct notify_option *, option, NULL, ao2_cleanup); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 	int using_uri = 0; | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (cmd) { | 
					
						
							|  |  |  | 	case CLI_INIT: | 
					
						
							| 
									
										
										
										
											2013-08-02 12:40:03 +00:00
										 |  |  | 		e->command = "pjsip send notify"; | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		e->usage = | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 			"Usage: pjsip send notify <type> {endpoint|uri} <peer> [<peer>...]\n" | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 			"       Send a NOTIFY request to an endpoint\n" | 
					
						
							| 
									
										
										
										
											2015-11-24 14:07:12 -05:00
										 |  |  | 			"       Message types are defined in pjsip_notify.conf\n"; | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		return NULL; | 
					
						
							|  |  |  | 	case CLI_GENERATE: | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 		if (a->argc > 4 && (!strcasecmp(a->argv[4], "uri"))) { | 
					
						
							|  |  |  | 			using_uri = 1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return cli_complete_notify(a->line, a->word, a->pos, a->n, using_uri); | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 	if (a->argc < 6) { | 
					
						
							|  |  |  | 		return CLI_SHOWUSAGE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!strcasecmp(a->argv[4], "uri")) { | 
					
						
							|  |  |  | 		using_uri = 1; | 
					
						
							|  |  |  | 	} else if (strcasecmp(a->argv[4], "endpoint")) { | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		return CLI_SHOWUSAGE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cfg = ao2_global_obj_ref(globals); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-02 12:40:03 +00:00
										 |  |  | 	if (!(option = notify_option_find(cfg->notify_options, a->argv[3]))) | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		ast_cli(a->fd, "Unable to find notify type '%s'\n", | 
					
						
							| 
									
										
										
										
											2013-08-02 12:40:03 +00:00
										 |  |  | 			a->argv[3]); | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		return CLI_FAILURE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 	for (i = 5; i < a->argc; ++i) { | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n", | 
					
						
							| 
									
										
										
										
											2013-08-02 12:40:03 +00:00
										 |  |  | 			a->argv[3], a->argv[i]); | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 		switch (using_uri ? push_notify_uri(a->argv[i], option, notify_cli_uri_data_create) : | 
					
						
							|  |  |  | 		                    push_notify(a->argv[i], option, notify_cli_data_create)) { | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		case INVALID_ENDPOINT: | 
					
						
							|  |  |  | 			ast_cli(a->fd, "Unable to retrieve endpoint %s\n", | 
					
						
							|  |  |  | 				a->argv[i]); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case ALLOC_ERROR: | 
					
						
							|  |  |  | 			ast_cli(a->fd, "Unable to allocate NOTIFY task data\n"); | 
					
						
							|  |  |  | 			return CLI_FAILURE; | 
					
						
							|  |  |  | 		case TASK_PUSH_ERROR: | 
					
						
							|  |  |  | 			ast_cli(a->fd, "Unable to push NOTIFY task\n"); | 
					
						
							|  |  |  | 			return CLI_FAILURE; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return CLI_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_cli_entry cli_options[] = { | 
					
						
							|  |  |  | 	AST_CLI_DEFINE(cli_notify, "Send a NOTIFY request to a SIP endpoint") | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							| 
									
										
										
										
											2021-11-19 09:54:42 +01:00
										 |  |  |  * \internal | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  |  * \brief Completes SIPNotify AMI command in Endpoint mode. | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | static void manager_notify_endpoint(struct mansession *s, | 
					
						
							|  |  |  | 	const struct message *m, const char *endpoint_name) | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-06-10 16:06:12 +00:00
										 |  |  | 	struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL); | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!strncasecmp(endpoint_name, "sip/", 4)) { | 
					
						
							|  |  |  | 		endpoint_name += 4; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-10 16:06:12 +00:00
										 |  |  | 	if (!strncasecmp(endpoint_name, "pjsip/", 6)) { | 
					
						
							|  |  |  | 		endpoint_name += 6; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 	switch (push_notify(endpoint_name, vars, notify_ami_data_create)) { | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | 	case INVALID_CHANNEL: | 
					
						
							|  |  |  | 		/* Shouldn't be possible. */ | 
					
						
							|  |  |  | 		ast_assert(0); | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 	case INVALID_ENDPOINT: | 
					
						
							| 
									
										
										
										
											2014-06-10 16:06:12 +00:00
										 |  |  | 		ast_variables_destroy(vars); | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 		astman_send_error_va(s, m, "Unable to retrieve endpoint %s", | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 			endpoint_name); | 
					
						
							| 
									
										
										
										
											2014-06-10 16:06:12 +00:00
										 |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 	case ALLOC_ERROR: | 
					
						
							| 
									
										
										
										
											2014-06-10 16:06:12 +00:00
										 |  |  | 		ast_variables_destroy(vars); | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 		astman_send_error(s, m, "Unable to allocate NOTIFY task data"); | 
					
						
							| 
									
										
										
										
											2014-06-10 16:06:12 +00:00
										 |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 	case TASK_PUSH_ERROR: | 
					
						
							| 
									
										
										
										
											2014-06-10 16:06:12 +00:00
										 |  |  | 		/* Don't need to destroy vars since it is handled by cleanup in push_notify */ | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 		astman_send_error(s, m, "Unable to push NOTIFY task"); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SUCCESS: | 
					
						
							|  |  |  | 		astman_send_ack(s, m, "NOTIFY sent"); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Completes SIPNotify AMI command in URI mode. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void manager_notify_uri(struct mansession *s, | 
					
						
							|  |  |  | 	const struct message *m, const char *uri) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (push_notify_uri(uri, vars, notify_ami_uri_data_create)) { | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | 	case INVALID_CHANNEL: | 
					
						
							|  |  |  | 		/* Shouldn't be possible. */ | 
					
						
							|  |  |  | 		ast_assert(0); | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 	case INVALID_ENDPOINT: | 
					
						
							|  |  |  | 		/* Shouldn't be possible. */ | 
					
						
							|  |  |  | 		ast_assert(0); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case ALLOC_ERROR: | 
					
						
							|  |  |  | 		ast_variables_destroy(vars); | 
					
						
							|  |  |  | 		astman_send_error(s, m, "Unable to allocate NOTIFY task data"); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case TASK_PUSH_ERROR: | 
					
						
							|  |  |  | 		/* Don't need to destroy vars since it is handled by cleanup in push_notify_uri */ | 
					
						
							|  |  |  | 		astman_send_error(s, m, "Unable to push Notify task"); | 
					
						
							| 
									
										
										
										
											2014-06-10 16:06:12 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case SUCCESS: | 
					
						
							|  |  |  | 		astman_send_ack(s, m, "NOTIFY sent"); | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Completes SIPNotify AMI command in channel mode. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void manager_notify_channel(struct mansession *s, | 
					
						
							|  |  |  | 	const struct message *m, const char *channel) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (push_notify_channel(channel, vars, notify_ami_channel_data_create)) { | 
					
						
							|  |  |  | 	case INVALID_CHANNEL: | 
					
						
							|  |  |  | 		ast_variables_destroy(vars); | 
					
						
							|  |  |  | 		astman_send_error(s, m, "Channel not found"); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case INVALID_ENDPOINT: | 
					
						
							|  |  |  | 		/* Shouldn't be possible. */ | 
					
						
							|  |  |  | 		ast_assert(0); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case ALLOC_ERROR: | 
					
						
							|  |  |  | 		ast_variables_destroy(vars); | 
					
						
							|  |  |  | 		astman_send_error(s, m, "Unable to allocate NOTIFY task data"); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case TASK_PUSH_ERROR: | 
					
						
							|  |  |  | 		/* Don't need to destroy vars since it is handled by cleanup in push_notify_channel */ | 
					
						
							|  |  |  | 		astman_send_error(s, m, "Unable to push Notify task"); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SUCCESS: | 
					
						
							|  |  |  | 		astman_send_ack(s, m, "NOTIFY sent"); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief AMI entry point to send a SIP notify to an endpoint. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int manager_notify(struct mansession *s, const struct message *m) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const char *endpoint_name = astman_get_header(m, "Endpoint"); | 
					
						
							|  |  |  | 	const char *uri = astman_get_header(m, "URI"); | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | 	const char *channel = astman_get_header(m, "Channel"); | 
					
						
							|  |  |  | 	int count = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ast_strlen_zero(endpoint_name)) { | 
					
						
							|  |  |  | 		++count; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!ast_strlen_zero(uri)) { | 
					
						
							|  |  |  | 		++count; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!ast_strlen_zero(channel)) { | 
					
						
							|  |  |  | 		++count; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | 	if (1 < count) { | 
					
						
							|  |  |  | 		astman_send_error(s, m, | 
					
						
							|  |  |  | 			"PJSIPNotify requires either an endpoint name, a SIP URI, or a channel.  " | 
					
						
							|  |  |  | 			"You must use only one of them."); | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 	} else if (!ast_strlen_zero(endpoint_name)) { | 
					
						
							|  |  |  | 		manager_notify_endpoint(s, m, endpoint_name); | 
					
						
							|  |  |  | 	} else if (!ast_strlen_zero(uri)) { | 
					
						
							|  |  |  | 		manager_notify_uri(s, m, uri); | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | 	} else if (!ast_strlen_zero(channel)) { | 
					
						
							|  |  |  | 		manager_notify_channel(s, m, channel); | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2018-02-22 19:18:48 +01:00
										 |  |  | 		astman_send_error(s, m, | 
					
						
							|  |  |  | 			"PJSIPNotify requires either an endpoint name, a SIP URI, or a channel."); | 
					
						
							| 
									
										
										
										
											2014-07-31 16:19:50 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int load_module(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (aco_info_init(¬ify_cfg)) { | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-12 13:55:12 -05:00
										 |  |  | 	aco_option_register_custom(¬ify_cfg, "", ACO_PREFIX, notify_options, | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 				   "", notify_option_handler, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (aco_process_config(¬ify_cfg, 0)) { | 
					
						
							|  |  |  | 		aco_info_destroy(¬ify_cfg); | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options)); | 
					
						
							| 
									
										
										
										
											2013-07-30 18:14:50 +00:00
										 |  |  | 	ast_manager_register_xml("PJSIPNotify", EVENT_FLAG_SYSTEM, manager_notify); | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return AST_MODULE_LOAD_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int reload_module(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-09-19 19:51:50 +00:00
										 |  |  | 	if (aco_process_config(¬ify_cfg, 1) == ACO_PROCESS_ERROR) { | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int unload_module(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-30 18:14:50 +00:00
										 |  |  | 	ast_manager_unregister("PJSIPNotify"); | 
					
						
							| 
									
										
										
										
											2014-09-19 19:51:50 +00:00
										 |  |  | 	ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options)); | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 	aco_info_destroy(¬ify_cfg); | 
					
						
							| 
									
										
										
										
											2015-03-26 17:47:42 +00:00
										 |  |  | 	ao2_global_obj_release(globals); | 
					
						
							| 
									
										
										
										
											2013-07-01 21:28:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-02 12:40:03 +00:00
										 |  |  | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "CLI/AMI PJSIP NOTIFY Support", | 
					
						
							| 
									
										
										
										
											2015-05-05 20:49:04 -04:00
										 |  |  | 	.support_level = AST_MODULE_SUPPORT_CORE, | 
					
						
							|  |  |  | 	.load = load_module, | 
					
						
							|  |  |  | 	.reload = reload_module, | 
					
						
							|  |  |  | 	.unload = unload_module, | 
					
						
							|  |  |  | 	.load_pri = AST_MODPRI_APP_DEPEND, | 
					
						
							| 
									
										
										
										
											2017-11-19 17:30:49 -05:00
										 |  |  | 	.requires = "res_pjsip", | 
					
						
							| 
									
										
										
										
											2015-05-05 20:49:04 -04:00
										 |  |  | ); |