mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 02:37:10 +00:00 
			
		
		
		
	res_pjsip_header_funcs: Add custom parameter support.
Adds support for custom URI and header parameters in the From header in PJSIP. Parameters can be both set and read using this function. ASTERISK-30150 #close Change-Id: Ifb1bc3c512ad5f6faeaebd7817f004a2ecbd6428
This commit is contained in:
		
				
					committed by
					
						 Friendly Automation
						Friendly Automation
					
				
			
			
				
	
			
			
			
						parent
						
							83eb113e0f
						
					
				
				
					commit
					406143ae61
				
			| @@ -5,6 +5,7 @@ | ||||
|  * | ||||
|  * George Joseph <george.joseph@fairview5.com> | ||||
|  * José Lopes <jose.lopes@nfon.com> | ||||
|  * Naveen Albert <asterisk@phreaknet.org> | ||||
|  * | ||||
|  * See http://www.asterisk.org for more information about | ||||
|  * the Asterisk project. Please do not directly contact | ||||
| @@ -250,7 +251,53 @@ | ||||
| 			<ref type="function">PJSIP_HEADERS</ref> | ||||
| 		</see-also> | ||||
| 	</function> | ||||
|  | ||||
| 	<function name="PJSIP_HEADER_PARAM" language="en_US"> | ||||
| 		<synopsis> | ||||
| 			Get or set header/URI parameters on a PJSIP channel. | ||||
| 		</synopsis> | ||||
| 		<syntax> | ||||
| 			<parameter name="header_name" required="true"> | ||||
| 				<para>Header in which parameter should be read or set.</para> | ||||
| 				<para>Currently, the only supported header is <literal>From</literal>.</para> | ||||
| 			</parameter> | ||||
| 			<parameter name="parameter_type" required="true"> | ||||
| 				<para>The type of parameter to get or set.</para> | ||||
| 				<para>Default is header parameter.</para> | ||||
| 				<enumlist> | ||||
| 					<enum name="header"> | ||||
| 						<para>Header parameter.</para> | ||||
| 					</enum> | ||||
| 					<enum name="uri"> | ||||
| 						<para>URI parameter.</para> | ||||
| 					</enum> | ||||
| 				</enumlist> | ||||
| 			</parameter> | ||||
| 			<parameter name="parameter_name" required="true"> | ||||
| 				<para>Name of parameter.</para> | ||||
| 			</parameter> | ||||
| 		</syntax> | ||||
| 		<description> | ||||
| 			<para>PJSIP_HEADER_PARAM allows you to read or set parameters in a SIP header on a | ||||
| 			PJSIP channel.</para> | ||||
| 			<para>Both URI parameters and header parameters can be read and set using | ||||
| 			this function. URI parameters appear in the URI (inside the <> in the header) | ||||
| 			while header parameters appear afterwards.</para> | ||||
| 			<note><para>If you call PJSIP_HEADER_PARAM in a normal dialplan context you'll be | ||||
| 			operating on the <emphasis>caller's (incoming)</emphasis> channel which | ||||
| 			may not be what you want. To operate on the <emphasis>callee's (outgoing)</emphasis> | ||||
| 			channel call PJSIP_HEADER_PARAM in a pre-dial handler. </para></note> | ||||
| 			<example title="Set URI parameter in From header on outbound channel"> | ||||
| 			[handler] | ||||
| 			exten => addheader,1,Set(PJSIP_HEADER_PARAM(From,uri,isup-oli)=27) | ||||
| 			same => n,Return() | ||||
| 			[somecontext] | ||||
| 			exten => 1,1,Dial(PJSIP/${EXTEN},,b(handler^addheader^1)) | ||||
| 			</example> | ||||
| 			<example title="Read URI parameter in From header on inbound channel"> | ||||
| 			same => n,Set(value=${PJSIP_HEADER_PARAM(From,uri,isup-oli)}) | ||||
| 			</example> | ||||
| 		</description> | ||||
| 	</function> | ||||
|  ***/ | ||||
|  | ||||
| /*! \brief Linked list for accumulating headers */ | ||||
| @@ -996,6 +1043,223 @@ static struct ast_sip_session_supplement header_funcs_supplement = { | ||||
| 	.incoming_response = incoming_response, | ||||
| }; | ||||
|  | ||||
| enum param_type { | ||||
| 	PARAMETER_HEADER, | ||||
| 	PARAMETER_URI, | ||||
| }; | ||||
|  | ||||
| struct param_data { | ||||
| 	struct ast_sip_channel_pvt *channel; | ||||
| 	char *header_name; | ||||
| 	char *param_name; | ||||
| 	const char *param_value; /* Only used for write */ | ||||
| 	enum param_type paramtype; | ||||
| 	/* For read function only */ | ||||
| 	char *buf; | ||||
| 	size_t len; | ||||
| }; | ||||
|  | ||||
| static int read_param(void *obj) | ||||
| { | ||||
| 	struct param_data *data = obj; | ||||
| 	struct ast_sip_session *session = data->channel->session; | ||||
| 	pj_str_t param_name; | ||||
|  | ||||
| 	pjsip_fromto_hdr *dlg_info; | ||||
| 	pjsip_name_addr *dlg_info_name_addr; | ||||
| 	pjsip_sip_uri *dlg_info_uri; | ||||
| 	pjsip_param *param; | ||||
| 	size_t param_len; | ||||
|  | ||||
| 	dlg_info = session->inv_session->dlg->remote.info; /* Remote dialog for incoming */ | ||||
| 	dlg_info_name_addr = (pjsip_name_addr *) dlg_info->uri; | ||||
| 	dlg_info_uri = pjsip_uri_get_uri(dlg_info_name_addr); | ||||
|  | ||||
| 	pj_cstr(¶m_name, data->param_name); | ||||
|  | ||||
| 	if (data->paramtype == PARAMETER_URI) { /* URI parameter */ | ||||
| 		param = pjsip_param_find(&dlg_info_uri->other_param, ¶m_name); | ||||
| 	} else { /* Header parameter */ | ||||
| 		param = pjsip_param_find(&dlg_info->other_param, ¶m_name); | ||||
| 	} | ||||
|  | ||||
| 	if (!param) { | ||||
| 		ast_debug(1, "No %s parameter found named %s\n", | ||||
| 			data->paramtype == PARAMETER_URI ? "URI" : "header", data->param_name); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	param_len = pj_strlen(¶m->value); | ||||
| 	if (param_len >= data->len) { | ||||
| 		ast_log(LOG_ERROR, "Buffer is too small for parameter value (%zu > %zu)\n", param_len, data->len); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	ast_debug(2, "Successfully read %s parameter %s (length %zu)\n", | ||||
| 		data->paramtype == PARAMETER_URI ? "URI" : "header", data->param_name, param_len); | ||||
| 	ast_copy_string(data->buf, pj_strbuf(¶m->value), data->len); | ||||
| 	data->buf[pj_strlen(¶m->value)] = '\0'; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! | ||||
|  * \internal | ||||
|  * \brief Implements PJSIP_HEADER_PARAM 'add' by adding the specified parameter. | ||||
|  * \note Unlike add_header, we can't add parameters in the outgoing_request callback: that's too late. | ||||
|  *       That's why we do it here and not in a callback. | ||||
|  */ | ||||
| static int add_param(void *obj) | ||||
| { | ||||
| 	struct param_data *data = obj; | ||||
| 	struct ast_sip_session *session = data->channel->session; | ||||
| 	pj_pool_t *pool = session->inv_session->dlg->pool; | ||||
|  | ||||
| 	pjsip_fromto_hdr *dlg_info; | ||||
| 	pjsip_name_addr *dlg_info_name_addr; | ||||
| 	pjsip_sip_uri *dlg_info_uri; | ||||
|  | ||||
| 	dlg_info = session->inv_session->dlg->local.info; /* Local for outgoing */ | ||||
| 	dlg_info_name_addr = (pjsip_name_addr *) dlg_info->uri; | ||||
| 	dlg_info_uri = pjsip_uri_get_uri(dlg_info_name_addr); | ||||
| 	if (!PJSIP_URI_SCHEME_IS_SIP(dlg_info_uri) && !PJSIP_URI_SCHEME_IS_SIPS(dlg_info_uri)) { | ||||
| 		ast_log(LOG_WARNING, "Non SIP/SIPS URI\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	ast_debug(1, "Adding custom %s param %s = %s\n", | ||||
| 		data->paramtype == PARAMETER_URI ? "URI" : "header", data->param_name, data->param_value); | ||||
|  | ||||
| 	/* This works the same as doing this in set_from_header in res_pjsip_session.c | ||||
| 	 * The way that this maps to pjproject is a little confusing. | ||||
| 	 * Say we have <sip:foo@bar.com;p1=abc;p2=def?h1=qrs&h2=tuv>;o1=foo;o2=bar | ||||
| 	 * p1 and p2 are URI parameters. | ||||
| 	 * (h1 and h2 are URI headers) | ||||
| 	 * o1 and o2 are header parameters (and don't have anything to do with the URI) | ||||
| 	 * In pjproject, other_param is used for adding all custom parameters. | ||||
| 	 * We use the URI for URI stuff, including URI parameters, and the header directly for header parameters. | ||||
| 	 */ | ||||
|  | ||||
| #define param_add(pool, list, pname, pvalue) { \ | ||||
| 	pjsip_param *param; \ | ||||
| 	param = PJ_POOL_ALLOC_T(pool, pjsip_param); \ | ||||
| 	pj_strdup2(pool, ¶m->name, pname); \ | ||||
| 	pj_strdup2(pool, ¶m->value, pvalue); \ | ||||
| 	pj_list_insert_before(list, param); \ | ||||
| } | ||||
|  | ||||
| 	if (data->paramtype == PARAMETER_URI) { /* URI parameter */ | ||||
| 		param_add(pool, &dlg_info_uri->other_param, data->param_name, S_OR(data->param_value, "")); | ||||
| 	} else { /* Header parameter */ | ||||
| 		param_add(pool, &dlg_info->other_param, data->param_name, S_OR(data->param_value, "")); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int func_read_param(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len) | ||||
| { | ||||
| 	struct ast_sip_channel_pvt *channel = chan ? ast_channel_tech_pvt(chan) : NULL; | ||||
| 	struct param_data param_data; | ||||
|  | ||||
| 	AST_DECLARE_APP_ARGS(args, | ||||
| 		AST_APP_ARG(header_name); | ||||
| 		AST_APP_ARG(param_type); | ||||
| 		AST_APP_ARG(param_name); | ||||
| 	); | ||||
|  | ||||
| 	AST_STANDARD_APP_ARGS(args, data); | ||||
|  | ||||
| 	param_data.channel = channel; | ||||
|  | ||||
| 	if (!channel || strncmp(ast_channel_name(chan), "PJSIP/", 6)) { | ||||
| 		ast_log(LOG_ERROR, "This function requires a PJSIP channel.\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (ast_strlen_zero(args.param_type)) { | ||||
| 		ast_log(AST_LOG_ERROR, "This function requires a parameter type.\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (ast_strlen_zero(args.param_name)) { | ||||
| 		ast_log(AST_LOG_ERROR, "This function requires a parameter name.\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* Currently, only From is supported, but this could be extended in the future. */ | ||||
| 	if (ast_strlen_zero(args.header_name) || strcasecmp(args.header_name, "From")) { | ||||
| 		ast_log(LOG_WARNING, "Only the From header is currently supported\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	param_data.param_name = args.param_name; | ||||
| 	if (!strcasecmp(args.param_type, "header")) { | ||||
| 		param_data.paramtype = PARAMETER_HEADER; | ||||
| 	} else if (!strcasecmp(args.param_type, "uri")) { | ||||
| 		param_data.paramtype = PARAMETER_URI; | ||||
| 	} else { | ||||
| 		ast_log(LOG_WARNING, "Parameter type '%s' is invalid: must be 'header' or 'uri'\n", args.param_type); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	param_data.buf = buf; | ||||
| 	param_data.len = len; | ||||
|  | ||||
| 	return ast_sip_push_task_wait_serializer(channel->session->serializer, read_param, ¶m_data); | ||||
| } | ||||
|  | ||||
| static int func_write_param(struct ast_channel *chan, const char *cmd, char *data, const char *value) | ||||
| { | ||||
| 	struct ast_sip_channel_pvt *channel = chan ? ast_channel_tech_pvt(chan) : NULL; | ||||
| 	struct param_data param_data; | ||||
| 	AST_DECLARE_APP_ARGS(args, | ||||
| 		AST_APP_ARG(header_name); | ||||
| 		AST_APP_ARG(param_type); | ||||
| 		AST_APP_ARG(param_name); | ||||
| 	); | ||||
|  | ||||
| 	AST_STANDARD_APP_ARGS(args, data); | ||||
|  | ||||
| 	param_data.channel = channel; | ||||
|  | ||||
| 	if (!channel || strncmp(ast_channel_name(chan), "PJSIP/", 6)) { | ||||
| 		ast_log(LOG_ERROR, "This function requires a PJSIP channel.\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (ast_strlen_zero(args.param_type)) { | ||||
| 		ast_log(AST_LOG_ERROR, "This function requires a parameter type.\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (ast_strlen_zero(args.param_name)) { | ||||
| 		ast_log(AST_LOG_ERROR, "This function requires a parameter name.\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* Currently, only From is supported, but this could be extended in the future. */ | ||||
| 	if (ast_strlen_zero(args.header_name) || strcasecmp(args.header_name, "From")) { | ||||
| 		ast_log(LOG_WARNING, "Only the From header is currently supported\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	param_data.param_name = args.param_name; | ||||
| 	if (!strcasecmp(args.param_type, "header")) { | ||||
| 		param_data.paramtype = PARAMETER_HEADER; | ||||
| 	} else if (!strcasecmp(args.param_type, "uri")) { | ||||
| 		param_data.paramtype = PARAMETER_URI; | ||||
| 	} else { | ||||
| 		ast_log(LOG_WARNING, "Parameter type '%s' is invalid: must be 'header' or 'uri'\n", args.param_type); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	param_data.param_value = value; | ||||
|  | ||||
| 	return ast_sip_push_task_wait_serializer(channel->session->serializer, add_param, ¶m_data); | ||||
| } | ||||
|  | ||||
| static struct ast_custom_function pjsip_header_param_function = { | ||||
| 	.name = "PJSIP_HEADER_PARAM", | ||||
| 	.read = func_read_param, | ||||
| 	.write = func_write_param, | ||||
| }; | ||||
|  | ||||
| static int load_module(void) | ||||
| { | ||||
| 	ast_sip_session_register_supplement(&header_funcs_supplement); | ||||
| @@ -1003,6 +1267,7 @@ static int load_module(void) | ||||
| 	ast_custom_function_register(&pjsip_headers_function); | ||||
| 	ast_custom_function_register(&pjsip_response_header_function); | ||||
| 	ast_custom_function_register(&pjsip_response_headers_function); | ||||
| 	ast_custom_function_register(&pjsip_header_param_function); | ||||
|  | ||||
| 	return AST_MODULE_LOAD_SUCCESS; | ||||
| } | ||||
| @@ -1013,6 +1278,7 @@ static int unload_module(void) | ||||
| 	ast_custom_function_unregister(&pjsip_headers_function); | ||||
| 	ast_custom_function_unregister(&pjsip_response_header_function); | ||||
| 	ast_custom_function_unregister(&pjsip_response_headers_function); | ||||
| 	ast_custom_function_unregister(&pjsip_header_param_function); | ||||
| 	ast_sip_session_unregister_supplement(&header_funcs_supplement); | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user