mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 18:55:19 +00:00 
			
		
		
		
	res_pjsip_header_funcs: Add functions PJSIP_RESPONSE_HEADER and PJSIP_RESPONSE_HEADERS
These new functions allow retrieving information from headers on 200 OK INVITE response. ASTERISK-29999 Change-Id: I264a610a9333359297a0825feb29a1bb4f4ad144
This commit is contained in:
		
				
					committed by
					
						 Kevin Harwell
						Kevin Harwell
					
				
			
			
				
	
			
			
			
						parent
						
							22be2a2857
						
					
				
				
					commit
					b29a0e08a3
				
			
							
								
								
									
										5
									
								
								doc/CHANGES-staging/res_pjsip_header_funcs.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								doc/CHANGES-staging/res_pjsip_header_funcs.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| Subject: res_pjsip_header_funcs | ||||
|  | ||||
| Add function PJSIP_RESPONSE_HEADERS() to get list of header names from 200 response, in the same way as PJSIP_HEADERS() from the request. | ||||
|  | ||||
| Add function PJSIP_RESPONSE_HEADER() to read header from 200 response, in the same way as PJSIP_HEADER() from the request. | ||||
| @@ -4,6 +4,7 @@ | ||||
|  * Copyright (C) 2013, Fairview 5 Engineering, LLC | ||||
|  * | ||||
|  * George Joseph <george.joseph@fairview5.com> | ||||
|  * José Lopes <jose.lopes@nfon.com> | ||||
|  * | ||||
|  * See http://www.asterisk.org for more information about | ||||
|  * the Asterisk project. Please do not directly contact | ||||
| @@ -166,6 +167,89 @@ | ||||
| 			<ref type="function">PJSIP_HEADER</ref> | ||||
| 		</see-also> | ||||
| 	</function> | ||||
| 	<function name="PJSIP_RESPONSE_HEADER" language="en_US"> | ||||
| 		<synopsis> | ||||
| 			Gets headers of 200 response from an outbound PJSIP channel. | ||||
| 		</synopsis> | ||||
| 		<syntax> | ||||
| 			<parameter name="action" required="true"> | ||||
| 				<enumlist> | ||||
| 					<enum name="read"> | ||||
| 						<para>Returns instance <replaceable>number</replaceable> | ||||
| 						of response header <replaceable>name</replaceable>.</para> | ||||
| 					</enum> | ||||
| 				</enumlist> | ||||
| 			</parameter> | ||||
|  | ||||
| 			<parameter name="name" required="true"> | ||||
| 				<para>The <replaceable>name</replaceable> of the response header. | ||||
| 				A <literal>*</literal> can be appended to the <replaceable>name</replaceable> | ||||
| 				to iterate over all response headers <emphasis>beginning with</emphasis> | ||||
| 				<replaceable>name</replaceable>.</para> | ||||
| 			</parameter> | ||||
|  | ||||
| 			<parameter name="number" required="false"> | ||||
| 				<para>If there's more than 1 header with the same name, this specifies which header | ||||
| 				to read.  If not specified, defaults to <literal>1</literal> meaning | ||||
| 				the first matching header. | ||||
| 				</para> | ||||
| 			</parameter> | ||||
|  | ||||
| 		</syntax> | ||||
| 		<description> | ||||
| 			<para>PJSIP_RESPONSE_HEADER allows you to read specific SIP headers of 200 response | ||||
| 			from the outbound PJSIP channel.</para> | ||||
| 			<para>Examples:</para> | ||||
| 			<example title="Set 'somevar' to the value of the 'From' header"> | ||||
| 				exten => 1,1,Set(somevar=${PJSIP_RESPONSE_HEADER(read,From)}) | ||||
| 			</example> | ||||
| 			<example title="Set 'via2' to the value of the 2nd 'Via' header"> | ||||
| 				exten => 1,1,Set(via2=${PJSIP_RESPONSE_HEADER(read,Via,2)}) | ||||
| 			</example> | ||||
| 			<example title="Set 'xhdr' to the value of the 1sx X-header"> | ||||
| 				exten => 1,1,Set(xhdr=${PJSIP_RESPONSE_HEADER(read,X-*,1)}) | ||||
| 			</example> | ||||
|  | ||||
| 			<note><para>If you call PJSIP_RESPONSE_HEADER 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_RESPONSE_HEADER in a pre-connect handler.</para> | ||||
| 			</note> | ||||
| 			<example title="Usage on pre-connect handler"> | ||||
| 				[handler] | ||||
| 				exten => readheader,1,NoOp(PJSIP_RESPONSE_HEADER(read,X-MyHeader)) | ||||
| 				[somecontext] | ||||
| 				exten => 1,1,Dial(PJSIP/${EXTEN},,U(handler^readheader^1)) | ||||
| 			</example> | ||||
| 		</description> | ||||
| 		<see-also> | ||||
| 			<ref type="function">PJSIP_RESPONSE_HEADERS</ref> | ||||
| 			<ref type="function">PJSIP_HEADER</ref> | ||||
| 		</see-also> | ||||
| 	</function> | ||||
| 	<function name="PJSIP_RESPONSE_HEADERS" language="en_US"> | ||||
| 		<synopsis> | ||||
| 			Gets the list of SIP header names from the 200 response of INVITE message. | ||||
| 		</synopsis> | ||||
| 		<syntax> | ||||
| 			<parameter name="prefix"> | ||||
| 				<para>If specified, only the headers matching the given prefix are returned.</para> | ||||
| 			</parameter> | ||||
| 		</syntax> | ||||
| 		<description> | ||||
| 			<para>Returns a comma-separated list of header names (without values) from the 200 | ||||
| 			response of INVITE message. Multiple headers with the same name are included in the | ||||
| 			list only once.</para> | ||||
| 			<para>For example, <literal>${PJSIP_RESPONSE_HEADERS(Co)}</literal> might return | ||||
| 			<literal>Contact,Content-Length,Content-Type</literal>. As a practical example, | ||||
| 			you may use <literal>${PJSIP_RESPONSE_HEADERS(X-)}</literal> to enumerate optional | ||||
| 			extended headers.</para> | ||||
| 		</description> | ||||
| 		<see-also> | ||||
| 			<ref type="function">PJSIP_RESPONSE_HEADER</ref> | ||||
| 			<ref type="function">PJSIP_HEADERS</ref> | ||||
| 		</see-also> | ||||
| 	</function> | ||||
|  | ||||
|  ***/ | ||||
|  | ||||
| @@ -180,6 +264,10 @@ AST_LIST_HEAD_NOLOCK(hdr_list, hdr_list_entry); | ||||
| static const struct ast_datastore_info header_datastore = { | ||||
| 	.type = "header_datastore", | ||||
| }; | ||||
| /*! \brief Datastore for saving response headers */ | ||||
| static const struct ast_datastore_info response_header_datastore = { | ||||
| 	.type = "response_header_datastore", | ||||
| }; | ||||
|  | ||||
| /*! \brief Data structure used for ast_sip_push_task_wait_serializer  */ | ||||
| struct header_data { | ||||
| @@ -189,6 +277,7 @@ struct header_data { | ||||
| 	char *buf; | ||||
| 	int header_number; | ||||
| 	size_t len; | ||||
| 	const struct ast_datastore_info *header_datastore; | ||||
| }; | ||||
|  | ||||
| /*! | ||||
| @@ -243,6 +332,42 @@ static int incoming_request(struct ast_sip_session *session, pjsip_rx_data * rda | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /*! | ||||
|  * \internal | ||||
|  * \brief Session supplement callback on an incoming INVITE response | ||||
|  * | ||||
|  * Retrieve the response_header_datastore from the session or create one if it doesn't exist. | ||||
|  * Create and initialize the list if needed. | ||||
|  * Insert the headers. | ||||
|  */ | ||||
| static void incoming_response(struct ast_sip_session *session, pjsip_rx_data * rdata) | ||||
| { | ||||
| 	pj_pool_t *pool = session->inv_session->dlg->pool; | ||||
| 	RAII_VAR(struct ast_datastore *, datastore, | ||||
| 			 ast_sip_session_get_datastore(session, response_header_datastore.type), ao2_cleanup); | ||||
| 	pjsip_status_line status = rdata->msg_info.msg->line.status; | ||||
|  | ||||
| 	/* Skip responses different of 200 OK, when 2xx is received. */ | ||||
| 	if (session->inv_session->state != PJSIP_INV_STATE_CONNECTING || status.code!=200) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (!datastore) { | ||||
| 		if (!(datastore = | ||||
| 			  ast_sip_session_alloc_datastore(&response_header_datastore, response_header_datastore.type)) | ||||
| 			|| | ||||
| 			!(datastore->data = pj_pool_alloc(pool, sizeof(struct hdr_list))) || | ||||
| 			ast_sip_session_add_datastore(session, datastore)) { | ||||
| 			ast_log(AST_LOG_ERROR, "Unable to create datastore for header functions.\n"); | ||||
| 			return; | ||||
| 		} | ||||
| 		AST_LIST_HEAD_INIT_NOLOCK((struct hdr_list *) datastore->data); | ||||
| 	} | ||||
| 	insert_headers(pool, (struct hdr_list *) datastore->data, rdata->msg_info.msg); | ||||
|  | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| /*! | ||||
|  * \internal | ||||
|  * \brief Search list for nth occurrence of specific header. | ||||
| @@ -270,7 +395,7 @@ static pjsip_hdr *find_header(struct hdr_list *list, const char *header_name, | ||||
|  | ||||
| /*! | ||||
|  * \internal | ||||
|  * \brief Implements PJSIP_HEADERS by searching for the requested header prefix. | ||||
|  * \brief Implements PJSIP_HEADERS/PJSIP_RESPONSE_HEADERS by searching for the requested header prefix. | ||||
|  * | ||||
|  * Retrieve the header_datastore. | ||||
|  * Search for the all matching headers. | ||||
| @@ -292,7 +417,7 @@ static int read_headers(void *obj) | ||||
| 	struct hdr_list *list; | ||||
|  | ||||
| 	RAII_VAR(struct ast_datastore *, datastore, | ||||
| 			 ast_sip_session_get_datastore(data->channel->session, header_datastore.type), | ||||
| 			 ast_sip_session_get_datastore(data->channel->session, data->header_datastore->type), | ||||
| 			 ao2_cleanup); | ||||
|  | ||||
| 	if (!datastore || !datastore->data) { | ||||
| @@ -354,10 +479,9 @@ static int read_headers(void *obj) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /*! | ||||
|  * \internal | ||||
|  * \brief Implements PJSIP_HEADER 'read' by searching the for the requested header. | ||||
|  * \brief Implements PJSIP_HEADER/PJSIP_RESPONSE_HEADER 'read' by searching the for the requested header. | ||||
|  * | ||||
|  * Retrieve the header_datastore. | ||||
|  * Search for the nth matching header. | ||||
| @@ -378,7 +502,7 @@ static int read_header(void *obj) | ||||
| 	struct hdr_list *list; | ||||
| 	int i = 1; | ||||
| 	RAII_VAR(struct ast_datastore *, datastore, | ||||
| 			 ast_sip_session_get_datastore(data->channel->session, header_datastore.type), | ||||
| 			 ast_sip_session_get_datastore(data->channel->session, data->header_datastore->type), | ||||
| 			 ao2_cleanup); | ||||
|  | ||||
| 	if (!datastore || !datastore->data) { | ||||
| @@ -459,11 +583,11 @@ static int add_header(void *obj) | ||||
| 	struct hdr_list *list; | ||||
|  | ||||
| 	RAII_VAR(struct ast_datastore *, datastore, | ||||
| 			 ast_sip_session_get_datastore(session, header_datastore.type), ao2_cleanup); | ||||
| 			 ast_sip_session_get_datastore(session, data->header_datastore->type), ao2_cleanup); | ||||
|  | ||||
| 	if (!datastore) { | ||||
| 		if (!(datastore = ast_sip_session_alloc_datastore(&header_datastore, | ||||
| 														  header_datastore.type)) | ||||
| 		if (!(datastore = ast_sip_session_alloc_datastore(data->header_datastore, | ||||
| 														data->header_datastore->type)) | ||||
| 			|| !(datastore->data = pj_pool_alloc(pool, sizeof(struct hdr_list))) | ||||
| 			|| ast_sip_session_add_datastore(session, datastore)) { | ||||
| 			ast_log(AST_LOG_ERROR, "Unable to create datastore for header functions.\n"); | ||||
| @@ -502,7 +626,7 @@ static int update_header(void *obj) | ||||
| 	struct header_data *data = obj; | ||||
| 	pjsip_hdr *hdr = NULL; | ||||
| 	RAII_VAR(struct ast_datastore *, datastore, | ||||
| 			 ast_sip_session_get_datastore(data->channel->session, header_datastore.type), | ||||
| 			 ast_sip_session_get_datastore(data->channel->session, data->header_datastore->type), | ||||
| 			 ao2_cleanup); | ||||
|  | ||||
| 	if (!datastore || !datastore->data) { | ||||
| @@ -539,7 +663,7 @@ static int remove_header(void *obj) | ||||
| 	struct hdr_list_entry *le; | ||||
| 	int removed_count = 0; | ||||
| 	RAII_VAR(struct ast_datastore *, datastore, | ||||
| 			 ast_sip_session_get_datastore(data->channel->session, header_datastore.type), | ||||
| 			 ast_sip_session_get_datastore(data->channel->session, data->header_datastore->type), | ||||
| 			 ao2_cleanup); | ||||
|  | ||||
| 	if (!datastore || !datastore->data) { | ||||
| @@ -597,13 +721,47 @@ static int func_read_headers(struct ast_channel *chan, const char *function, cha | ||||
| 	header_data.header_value = NULL; | ||||
| 	header_data.buf = buf; | ||||
| 	header_data.len = len; | ||||
| 	header_data.header_datastore = &header_datastore; | ||||
|  | ||||
| 	return ast_sip_push_task_wait_serializer(channel->session->serializer, read_headers, &header_data); | ||||
|  | ||||
| } | ||||
|  | ||||
| /*! | ||||
|  * \brief Implements function 'read' callback. | ||||
|  * \brief Read list of unique SIP response headers | ||||
|  */ | ||||
| static int func_response_read_headers(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 header_data header_data; | ||||
| 	AST_DECLARE_APP_ARGS(args, | ||||
| 		AST_APP_ARG(header_pattern); | ||||
| 	); | ||||
| 	AST_STANDARD_APP_ARGS(args, data); | ||||
|  | ||||
| 	if (!chan || 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.header_pattern)) { | ||||
| 		ast_log(AST_LOG_ERROR, "This function requires a pattern.\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	header_data.channel = channel; | ||||
| 	header_data.header_name = args.header_pattern; | ||||
| 	header_data.header_value = NULL; | ||||
| 	header_data.buf = buf; | ||||
| 	header_data.len = len; | ||||
| 	header_data.header_datastore = &response_header_datastore; | ||||
|  | ||||
| 	return ast_sip_push_task_wait_serializer(channel->session->serializer, read_headers, &header_data); | ||||
|  | ||||
| } | ||||
|  | ||||
| /*! | ||||
|  * \brief Implements PJSIP_HEADER function 'read' callback. | ||||
|  * | ||||
|  * Valid actions are 'read' and 'remove'. | ||||
|  */ | ||||
| @@ -645,6 +803,7 @@ static int func_read_header(struct ast_channel *chan, const char *function, char | ||||
| 	header_data.header_value = NULL; | ||||
| 	header_data.buf = buf; | ||||
| 	header_data.len = len; | ||||
| 	header_data.header_datastore = &header_datastore; | ||||
|  | ||||
| 	if (!strcasecmp(args.action, "read")) { | ||||
| 		return ast_sip_push_task_wait_serializer(channel->session->serializer, read_header, &header_data); | ||||
| @@ -660,7 +819,62 @@ static int func_read_header(struct ast_channel *chan, const char *function, char | ||||
| } | ||||
|  | ||||
| /*! | ||||
|  * \brief Implements function 'write' callback. | ||||
|  * \brief Implements PJSIP_RESPONSE_HEADER function 'read' callback. | ||||
|  * | ||||
|  * Valid actions are 'read' | ||||
|  */ | ||||
| static int func_response_read_header(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 header_data header_data; | ||||
| 	int number; | ||||
| 	AST_DECLARE_APP_ARGS(args, | ||||
| 						 AST_APP_ARG(action); | ||||
| 						 AST_APP_ARG(header_name); AST_APP_ARG(header_number);); | ||||
| 	AST_STANDARD_APP_ARGS(args, data); | ||||
|  | ||||
| 	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.action)) { | ||||
| 		ast_log(AST_LOG_ERROR, "This function requires an action.\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (ast_strlen_zero(args.header_name)) { | ||||
| 		ast_log(AST_LOG_ERROR, "This function requires a header name.\n"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (!args.header_number) { | ||||
| 		number = 1; | ||||
| 	} else { | ||||
| 		sscanf(args.header_number, "%30d", &number); | ||||
| 		if (number < 1) { | ||||
| 			number = 1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	header_data.channel = channel; | ||||
| 	header_data.header_name = args.header_name; | ||||
| 	header_data.header_number = number; | ||||
| 	header_data.header_value = NULL; | ||||
| 	header_data.buf = buf; | ||||
| 	header_data.len = len; | ||||
| 	header_data.header_datastore = &response_header_datastore; | ||||
|  | ||||
| 	if (!strcasecmp(args.action, "read")) { | ||||
| 		return ast_sip_push_task_wait_serializer(channel->session->serializer, read_header, &header_data); | ||||
| 	} else { | ||||
| 		ast_log(AST_LOG_ERROR, | ||||
| 				"Unknown action '%s' is not valid, must be 'read'.\n", | ||||
| 				args.action); | ||||
| 		return -1; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /*! | ||||
|  * \brief Implements PJSIP_HEADER function 'write' callback. | ||||
|  * | ||||
|  * Valid actions are 'add', 'update' and 'remove'. | ||||
|  */ | ||||
| @@ -703,6 +917,7 @@ static int func_write_header(struct ast_channel *chan, const char *cmd, char *da | ||||
| 	header_data.header_value = value; | ||||
| 	header_data.buf = NULL; | ||||
| 	header_data.len = 0; | ||||
| 	header_data.header_datastore = &header_datastore; | ||||
|  | ||||
| 	if (!strcasecmp(args.action, "add")) { | ||||
| 		return ast_sip_push_task_wait_serializer(channel->session->serializer, | ||||
| @@ -732,6 +947,16 @@ static struct ast_custom_function pjsip_headers_function = { | ||||
| 	.read = func_read_headers | ||||
| }; | ||||
|  | ||||
| static struct ast_custom_function pjsip_response_header_function = { | ||||
| 	.name = "PJSIP_RESPONSE_HEADER", | ||||
| 	.read = func_response_read_header | ||||
| }; | ||||
|  | ||||
| static struct ast_custom_function pjsip_response_headers_function = { | ||||
| 	.name = "PJSIP_RESPONSE_HEADERS", | ||||
| 	.read = func_response_read_headers | ||||
| }; | ||||
|  | ||||
| /*! | ||||
|  * \internal | ||||
|  * \brief Session supplement callback for outgoing INVITE requests | ||||
| @@ -768,6 +993,7 @@ static struct ast_sip_session_supplement header_funcs_supplement = { | ||||
| 	.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 1000, | ||||
| 	.incoming_request = incoming_request, | ||||
| 	.outgoing_request = outgoing_request, | ||||
| 	.incoming_response = incoming_response, | ||||
| }; | ||||
|  | ||||
| static int load_module(void) | ||||
| @@ -775,6 +1001,8 @@ static int load_module(void) | ||||
| 	ast_sip_session_register_supplement(&header_funcs_supplement); | ||||
| 	ast_custom_function_register(&pjsip_header_function); | ||||
| 	ast_custom_function_register(&pjsip_headers_function); | ||||
| 	ast_custom_function_register(&pjsip_response_header_function); | ||||
| 	ast_custom_function_register(&pjsip_response_headers_function); | ||||
|  | ||||
| 	return AST_MODULE_LOAD_SUCCESS; | ||||
| } | ||||
| @@ -783,6 +1011,8 @@ static int unload_module(void) | ||||
| { | ||||
| 	ast_custom_function_unregister(&pjsip_header_function); | ||||
| 	ast_custom_function_unregister(&pjsip_headers_function); | ||||
| 	ast_custom_function_unregister(&pjsip_response_header_function); | ||||
| 	ast_custom_function_unregister(&pjsip_response_headers_function); | ||||
| 	ast_sip_session_unregister_supplement(&header_funcs_supplement); | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user