| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2013, Digium, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Joshua Colp <jcolp@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
 | 
					
						
							|  |  |  | 	<depend>pjproject</depend> | 
					
						
							| 
									
										
										
										
											2013-07-30 18:14:50 +00:00
										 |  |  | 	<depend>res_pjsip</depend> | 
					
						
							| 
									
										
										
										
											2020-04-17 11:47:01 +02:00
										 |  |  | 	<depend>res_pjsip_session</depend> | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	<support_level>core</support_level> | 
					
						
							|  |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <pjsip.h>
 | 
					
						
							|  |  |  | #include <pjsip_ua.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-30 18:14:50 +00:00
										 |  |  | #include "asterisk/res_pjsip.h"
 | 
					
						
							|  |  |  | #include "asterisk/res_pjsip_session.h"
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | #include "asterisk/module.h"
 | 
					
						
							|  |  |  | #include "asterisk/causes.h"
 | 
					
						
							| 
									
										
										
										
											2015-11-13 14:32:10 -06:00
										 |  |  | #include "asterisk/threadpool.h"
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void rfc3326_use_reason_header(struct ast_sip_session *session, struct pjsip_rx_data *rdata) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-03-14 16:17:23 +00:00
										 |  |  | 	static const pj_str_t str_reason = { "Reason", 6 }; | 
					
						
							|  |  |  | 	pjsip_generic_string_hdr *header; | 
					
						
							| 
									
										
										
										
											2025-09-04 10:54:27 +06:00
										 |  |  | 	char buf[128]; | 
					
						
							| 
									
										
										
										
											2018-03-14 16:17:23 +00:00
										 |  |  | 	char *cause; | 
					
						
							| 
									
										
										
										
											2023-06-12 11:31:33 -04:00
										 |  |  | 	int code_q850 = 0, code_sip = 0; | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-14 16:17:23 +00:00
										 |  |  | 	header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_reason, NULL); | 
					
						
							|  |  |  | 	for (; header; | 
					
						
							|  |  |  | 		header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_reason, header->next)) { | 
					
						
							| 
									
										
										
										
											2023-06-12 11:31:33 -04:00
										 |  |  | 		int cause_q850, cause_sip; | 
					
						
							| 
									
										
										
										
											2025-09-04 10:54:27 +06:00
										 |  |  | 		struct ast_control_pvt_cause_code *cause_code; | 
					
						
							|  |  |  | 		int data_size = sizeof(*cause_code); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-14 16:17:23 +00:00
										 |  |  | 		ast_copy_pj_str(buf, &header->hvalue, sizeof(buf)); | 
					
						
							|  |  |  | 		cause = ast_skip_blanks(buf); | 
					
						
							| 
									
										
										
										
											2022-11-18 08:16:50 +06:00
										 |  |  | 		cause_q850 = !strncasecmp(cause, "Q.850", 5); | 
					
						
							|  |  |  | 		cause_sip = !strncasecmp(cause, "SIP", 3); | 
					
						
							|  |  |  | 		if ((cause_q850 || cause_sip) && (cause = strstr(cause, "cause="))) { | 
					
						
							| 
									
										
										
										
											2023-06-12 11:31:33 -04:00
										 |  |  | 			int *code = cause_q850 ? &code_q850 : &code_sip; | 
					
						
							|  |  |  | 			if (sscanf(cause, "cause=%30d", code) != 1) { | 
					
						
							|  |  |  | 				*code = 0; | 
					
						
							| 
									
										
										
										
											2022-11-18 08:16:50 +06:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2025-09-04 10:54:27 +06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			/* Safe */ | 
					
						
							|  |  |  | 			/* Build and send the tech-specific cause information */ | 
					
						
							|  |  |  | 			/* size of the string making up the cause code is "SIP " + reason length */ | 
					
						
							|  |  |  | 			data_size += 4 + strlen(cause) + 1; | 
					
						
							|  |  |  | 			cause_code = ast_alloca(data_size); | 
					
						
							|  |  |  | 			memset(cause_code, 0, data_size); | 
					
						
							|  |  |  | 			ast_copy_string(cause_code->chan_name, ast_channel_name(session->channel), AST_CHANNEL_NAME); | 
					
						
							|  |  |  | 			snprintf(cause_code->code, data_size, "SIP %s", cause); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			cause_code->cause_extended = 1; | 
					
						
							|  |  |  | 			if (code_q850) { | 
					
						
							|  |  |  | 				cause_code->ast_cause = *code & 0x7; | 
					
						
							|  |  |  | 			} else if (code_sip) { | 
					
						
							|  |  |  | 				cause_code->ast_cause = ast_sip_hangup_sip2cause(*code); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			ast_queue_control_data(session->channel, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size); | 
					
						
							|  |  |  | 			ast_channel_hangupcause_hash_set(session->channel, cause_code, data_size); | 
					
						
							| 
									
										
										
										
											2022-11-18 08:16:50 +06:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-06-12 11:31:33 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (code_q850) { | 
					
						
							|  |  |  | 		ast_channel_hangupcause_set(session->channel, code_q850 & 0x7f); | 
					
						
							|  |  |  | 	} else if (code_sip) { | 
					
						
							|  |  |  | 		ast_channel_hangupcause_set(session->channel, ast_sip_hangup_sip2cause(code_sip)); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rfc3326_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if ((pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method) && | 
					
						
							|  |  |  | 	     pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method)) || | 
					
						
							|  |  |  | 	    !session->channel) { | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfc3326_use_reason_header(session, rdata); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfc3326_incoming_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pjsip_status_line status = rdata->msg_info.msg->line.status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((status.code < 300) || !session->channel) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfc3326_use_reason_header(session, rdata); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfc3326_add_reason_header(struct ast_sip_session *session, struct pjsip_tx_data *tdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char buf[20]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_channel_hangupcause(session->channel) == AST_CAUSE_ANSWERED_ELSEWHERE) { | 
					
						
							|  |  |  | 		ast_sip_add_header(tdata, "Reason", "SIP;cause=200;text=\"Call completed elsewhere\""); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-06 19:31:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-06 06:57:37 -06:00
										 |  |  | 	if (session->endpoint && session->endpoint->suppress_q850_reason_headers) { | 
					
						
							| 
									
										
										
										
											2021-10-30 21:04:36 -04:00
										 |  |  | 		ast_debug(1, "A Q.850 '%s'(%i) Reason header was suppressed for endpoint '%s'\n", | 
					
						
							| 
									
										
										
										
											2018-07-06 06:57:37 -06:00
										 |  |  | 			ast_cause2str((ast_channel_hangupcause(session->channel) & 0x7f)), | 
					
						
							|  |  |  | 			(ast_channel_hangupcause(session->channel) & 0x7f), | 
					
						
							|  |  |  | 			ast_sorcery_object_get_id(session->endpoint)); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		snprintf(buf, sizeof(buf), "Q.850;cause=%i", ast_channel_hangupcause(session->channel) & 0x7f); | 
					
						
							|  |  |  | 		ast_sip_add_header(tdata, "Reason", buf); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfc3326_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-11-13 14:32:10 -06:00
										 |  |  | 	if ((pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_bye_method) | 
					
						
							|  |  |  | 			&& pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_cancel_method)) | 
					
						
							|  |  |  | 		|| !session->channel | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * The session->channel has been seen to go away on us between | 
					
						
							|  |  |  | 		 * checks so we must also be running under the call's serializer | 
					
						
							|  |  |  | 		 * thread. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		|| session->serializer != ast_threadpool_serializer_get_current()) { | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfc3326_add_reason_header(session, tdata); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rfc3326_outgoing_response(struct ast_sip_session *session, struct pjsip_tx_data *tdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pjsip_status_line status = tdata->msg->line.status; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-13 14:32:10 -06:00
										 |  |  | 	if (status.code < 300 | 
					
						
							|  |  |  | 		|| !session->channel | 
					
						
							|  |  |  | 		|| session->serializer != ast_threadpool_serializer_get_current()) { | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rfc3326_add_reason_header(session, tdata); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_sip_session_supplement rfc3326_supplement = { | 
					
						
							|  |  |  | 	.incoming_request = rfc3326_incoming_request, | 
					
						
							|  |  |  | 	.incoming_response = rfc3326_incoming_response, | 
					
						
							|  |  |  | 	.outgoing_request = rfc3326_outgoing_request, | 
					
						
							|  |  |  | 	.outgoing_response = rfc3326_outgoing_response, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int load_module(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ast_sip_session_register_supplement(&rfc3326_supplement); | 
					
						
							|  |  |  | 	return AST_MODULE_LOAD_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int unload_module(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ast_sip_session_unregister_supplement(&rfc3326_supplement); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-30 18:14:50 +00:00
										 |  |  | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP RFC3326 Support", | 
					
						
							| 
									
										
										
										
											2015-05-05 20:49:04 -04:00
										 |  |  | 	.support_level = AST_MODULE_SUPPORT_CORE, | 
					
						
							|  |  |  | 	.load = load_module, | 
					
						
							|  |  |  | 	.unload = unload_module, | 
					
						
							|  |  |  | 	.load_pri = AST_MODPRI_APP_DEPEND, | 
					
						
							| 
									
										
										
										
											2017-12-29 03:57:17 -05:00
										 |  |  | 	.requires = "res_pjsip,res_pjsip_session", | 
					
						
							| 
									
										
										
										
											2015-05-05 20:49:04 -04:00
										 |  |  | ); |