| 
									
										
										
										
											2009-03-05 18:18:27 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2009, 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \file
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \brief Built in bridging features | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \author Joshua Colp <jcolp@digium.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \ingroup bridges | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-14 20:28:54 +00:00
										 |  |  | /*** MODULEINFO
 | 
					
						
							| 
									
										
										
										
											2017-11-02 02:57:52 -04:00
										 |  |  | 	<use type="module">res_monitor</use> | 
					
						
							| 
									
										
										
										
											2011-07-14 20:28:54 +00:00
										 |  |  | 	<support_level>core</support_level> | 
					
						
							|  |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-05 18:18:27 +00:00
										 |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <sys/types.h>
 | 
					
						
							|  |  |  | #include <sys/stat.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk/module.h"
 | 
					
						
							|  |  |  | #include "asterisk/channel.h"
 | 
					
						
							| 
									
										
										
										
											2013-07-25 04:06:32 +00:00
										 |  |  | #include "asterisk/bridge.h"
 | 
					
						
							|  |  |  | #include "asterisk/bridge_technology.h"
 | 
					
						
							| 
									
										
										
										
											2009-03-05 18:18:27 +00:00
										 |  |  | #include "asterisk/frame.h"
 | 
					
						
							|  |  |  | #include "asterisk/file.h"
 | 
					
						
							|  |  |  | #include "asterisk/app.h"
 | 
					
						
							|  |  |  | #include "asterisk/astobj2.h"
 | 
					
						
							| 
									
										
										
										
											2013-05-21 18:00:22 +00:00
										 |  |  | #include "asterisk/pbx.h"
 | 
					
						
							|  |  |  | #include "asterisk/parking.h"
 | 
					
						
							| 
									
										
										
										
											2013-06-06 21:40:35 +00:00
										 |  |  | #include "asterisk/features_config.h"
 | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | #include "asterisk/monitor.h"
 | 
					
						
							|  |  |  | #include "asterisk/mixmonitor.h"
 | 
					
						
							|  |  |  | #include "asterisk/audiohook.h"
 | 
					
						
							| 
									
										
										
										
											2013-08-22 21:09:52 +00:00
										 |  |  | #include "asterisk/causes.h"
 | 
					
						
							| 
									
										
										
										
											2009-03-05 18:18:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | enum set_touch_variables_res { | 
					
						
							|  |  |  | 	SET_TOUCH_SUCCESS, | 
					
						
							|  |  |  | 	SET_TOUCH_UNSET, | 
					
						
							|  |  |  | 	SET_TOUCH_ALLOC_FAILURE, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void set_touch_variable(enum set_touch_variables_res *res, struct ast_channel *chan, const char *var_name, char **touch) | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	const char *c_touch; | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	if (*res == SET_TOUCH_ALLOC_FAILURE) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	c_touch = pbx_builtin_getvar_helper(chan, var_name); | 
					
						
							|  |  |  | 	if (!ast_strlen_zero(c_touch)) { | 
					
						
							|  |  |  | 		*touch = ast_strdup(c_touch); | 
					
						
							|  |  |  | 		if (!*touch) { | 
					
						
							|  |  |  | 			*res = SET_TOUCH_ALLOC_FAILURE; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			*res = SET_TOUCH_SUCCESS; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static enum set_touch_variables_res set_touch_variables(struct ast_channel *chan, int is_mixmonitor, char **touch_format, char **touch_monitor, char **touch_monitor_prefix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	enum set_touch_variables_res res = SET_TOUCH_UNSET; | 
					
						
							|  |  |  | 	const char *var_format; | 
					
						
							|  |  |  | 	const char *var_monitor; | 
					
						
							|  |  |  | 	const char *var_prefix; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	SCOPED_CHANNELLOCK(lock, chan); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (is_mixmonitor) { | 
					
						
							|  |  |  | 		var_format = "TOUCH_MIXMONITOR_FORMAT"; | 
					
						
							|  |  |  | 		var_monitor = "TOUCH_MIXMONITOR"; | 
					
						
							|  |  |  | 		var_prefix = "TOUCH_MIXMONITOR_PREFIX"; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		var_format = "TOUCH_MONITOR_FORMAT"; | 
					
						
							|  |  |  | 		var_monitor = "TOUCH_MONITOR"; | 
					
						
							|  |  |  | 		var_prefix = "TOUCH_MONITOR_PREFIX"; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	set_touch_variable(&res, chan, var_format, touch_format); | 
					
						
							|  |  |  | 	set_touch_variable(&res, chan, var_monitor, touch_monitor); | 
					
						
							|  |  |  | 	set_touch_variable(&res, chan, var_prefix, touch_monitor_prefix); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | static void stop_automonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *stop_message) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 	ast_verb(4, "AutoMonitor used to stop recording call.\n"); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ast_channel_lock(peer_chan); | 
					
						
							|  |  |  | 	if (ast_channel_monitor(peer_chan)) { | 
					
						
							|  |  |  | 		if (ast_channel_monitor(peer_chan)->stop(peer_chan, 1)) { | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 			ast_verb(4, "Cannot stop AutoMonitor for %s\n", ast_channel_name(bridge_channel->chan)); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 			if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) { | 
					
						
							|  |  |  | 				ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			ast_channel_unlock(peer_chan); | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/* Something else removed the Monitor before we got to it. */ | 
					
						
							|  |  |  | 		ast_channel_unlock(peer_chan); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_channel_unlock(peer_chan); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (features_cfg && !(ast_strlen_zero(features_cfg->courtesytone))) { | 
					
						
							|  |  |  | 		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL); | 
					
						
							|  |  |  | 		ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ast_strlen_zero(stop_message)) { | 
					
						
							|  |  |  | 		ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL); | 
					
						
							|  |  |  | 		ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | static void start_automonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *start_message) | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	char *touch_filename; | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	size_t len; | 
					
						
							|  |  |  | 	int x; | 
					
						
							|  |  |  | 	enum set_touch_variables_res set_touch_res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	RAII_VAR(char *, touch_format, NULL, ast_free); | 
					
						
							|  |  |  | 	RAII_VAR(char *, touch_monitor, NULL, ast_free); | 
					
						
							|  |  |  | 	RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	set_touch_res = set_touch_variables(bridge_channel->chan, 0, &touch_format, | 
					
						
							|  |  |  | 		&touch_monitor, &touch_monitor_prefix); | 
					
						
							|  |  |  | 	switch (set_touch_res) { | 
					
						
							|  |  |  | 	case SET_TOUCH_SUCCESS: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SET_TOUCH_UNSET: | 
					
						
							|  |  |  | 		set_touch_res = set_touch_variables(peer_chan, 0, &touch_format, &touch_monitor, | 
					
						
							|  |  |  | 			&touch_monitor_prefix); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 		if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) { | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 			return; | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case SET_TOUCH_ALLOC_FAILURE: | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ast_strlen_zero(touch_monitor)) { | 
					
						
							|  |  |  | 		len = strlen(touch_monitor) + 50; | 
					
						
							|  |  |  | 		touch_filename = ast_alloca(len); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		snprintf(touch_filename, len, "%s-%ld-%s", | 
					
						
							|  |  |  | 			S_OR(touch_monitor_prefix, "auto"), | 
					
						
							|  |  |  | 			(long) time(NULL), | 
					
						
							|  |  |  | 			touch_monitor); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		char *caller_chan_id; | 
					
						
							|  |  |  | 		char *peer_chan_id; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 		caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid, | 
					
						
							|  |  |  | 			ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan))); | 
					
						
							|  |  |  | 		peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid, | 
					
						
							|  |  |  | 			ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan))); | 
					
						
							|  |  |  | 		len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50; | 
					
						
							|  |  |  | 		touch_filename = ast_alloca(len); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		snprintf(touch_filename, len, "%s-%ld-%s-%s", | 
					
						
							|  |  |  | 			S_OR(touch_monitor_prefix, "auto"), | 
					
						
							|  |  |  | 			(long) time(NULL), | 
					
						
							|  |  |  | 			caller_chan_id, | 
					
						
							|  |  |  | 			peer_chan_id); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	for (x = 0; x < strlen(touch_filename); x++) { | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 		if (touch_filename[x] == '/') { | 
					
						
							|  |  |  | 			touch_filename[x] = '-'; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 	ast_verb(4, "AutoMonitor used to record call. Filename: %s\n", touch_filename); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-15 23:21:19 +00:00
										 |  |  | 	if (ast_monitor_start(peer_chan, touch_format, touch_filename, 1, X_REC_IN | X_REC_OUT, NULL)) { | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 		ast_verb(4, "AutoMonitor feature was tried by '%s' but monitor failed to start.\n", | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 			ast_channel_name(bridge_channel->chan)); | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-06 10:46:30 +01:00
										 |  |  | 	ast_monitor_setjoinfiles(peer_chan, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) { | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL); | 
					
						
							|  |  |  | 		ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	if (!ast_strlen_zero(start_message)) { | 
					
						
							|  |  |  | 		ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL); | 
					
						
							|  |  |  | 		ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pbx_builtin_setvar_helper(peer_chan, "TOUCH_MONITOR_OUTPUT", touch_filename); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-26 21:34:23 +00:00
										 |  |  | static int feature_automonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt) | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	const char *start_message; | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	const char *stop_message; | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	struct ast_bridge_features_automonitor *options = hook_pvt; | 
					
						
							|  |  |  | 	enum ast_bridge_features_monitor start_stop = options ? options->start_stop : AUTO_MONITOR_TOGGLE; | 
					
						
							|  |  |  | 	int is_monitoring; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup); | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-28 21:35:41 +00:00
										 |  |  | 	ast_channel_lock(bridge_channel->chan); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	features_cfg = ast_get_chan_features_general_config(bridge_channel->chan); | 
					
						
							| 
									
										
										
										
											2014-10-28 21:35:41 +00:00
										 |  |  | 	ast_channel_unlock(bridge_channel->chan); | 
					
						
							| 
									
										
										
										
											2013-07-26 21:34:23 +00:00
										 |  |  | 	ast_bridge_channel_lock_bridge(bridge_channel); | 
					
						
							|  |  |  | 	peer_chan = ast_bridge_peer_nolock(bridge_channel->bridge, bridge_channel->chan); | 
					
						
							|  |  |  | 	ast_bridge_unlock(bridge_channel->bridge); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!peer_chan) { | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 		ast_verb(4, "Cannot start AutoMonitor for %s - can not determine peer in bridge.\n", | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 			ast_channel_name(bridge_channel->chan)); | 
					
						
							|  |  |  | 		if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) { | 
					
						
							|  |  |  | 			ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ast_channel_lock(bridge_channel->chan); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	start_message = pbx_builtin_getvar_helper(bridge_channel->chan, | 
					
						
							|  |  |  | 		"TOUCH_MONITOR_MESSAGE_START"); | 
					
						
							|  |  |  | 	start_message = ast_strdupa(S_OR(start_message, "")); | 
					
						
							|  |  |  | 	stop_message = pbx_builtin_getvar_helper(bridge_channel->chan, | 
					
						
							|  |  |  | 		"TOUCH_MONITOR_MESSAGE_STOP"); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	stop_message = ast_strdupa(S_OR(stop_message, "")); | 
					
						
							|  |  |  | 	ast_channel_unlock(bridge_channel->chan); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	is_monitoring = ast_channel_monitor(peer_chan) != NULL; | 
					
						
							|  |  |  | 	switch (start_stop) { | 
					
						
							|  |  |  | 	case AUTO_MONITOR_TOGGLE: | 
					
						
							|  |  |  | 		if (is_monitoring) { | 
					
						
							|  |  |  | 			stop_automonitor(bridge_channel, peer_chan, features_cfg, stop_message); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			start_automonitor(bridge_channel, peer_chan, features_cfg, start_message); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	case AUTO_MONITOR_START: | 
					
						
							|  |  |  | 		if (!is_monitoring) { | 
					
						
							|  |  |  | 			start_automonitor(bridge_channel, peer_chan, features_cfg, start_message); | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 		ast_verb(4, "AutoMonitor already recording call.\n"); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case AUTO_MONITOR_STOP: | 
					
						
							|  |  |  | 		if (is_monitoring) { | 
					
						
							|  |  |  | 			stop_automonitor(bridge_channel, peer_chan, features_cfg, stop_message); | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 		ast_verb(4, "AutoMonitor already stopped on call.\n"); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Fake start/stop to invoker so will think it did something but | 
					
						
							|  |  |  | 	 * was already in that mode. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) { | 
					
						
							|  |  |  | 		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (is_monitoring) { | 
					
						
							|  |  |  | 		if (!ast_strlen_zero(start_message)) { | 
					
						
							|  |  |  | 			ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if (!ast_strlen_zero(stop_message)) { | 
					
						
							|  |  |  | 			ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void stop_automixmonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *stop_message) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 	ast_verb(4, "AutoMixMonitor used to stop recording call.\n"); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_stop_mixmonitor(peer_chan, NULL)) { | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 		ast_verb(4, "Failed to stop AutoMixMonitor for %s.\n", ast_channel_name(bridge_channel->chan)); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 		if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) { | 
					
						
							|  |  |  | 			ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) { | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL); | 
					
						
							|  |  |  | 		ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ast_strlen_zero(stop_message)) { | 
					
						
							|  |  |  | 		ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL); | 
					
						
							|  |  |  | 		ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | static void start_automixmonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *start_message) | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	char *touch_filename; | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	size_t len; | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	int x; | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	enum set_touch_variables_res set_touch_res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	RAII_VAR(char *, touch_format, NULL, ast_free); | 
					
						
							|  |  |  | 	RAII_VAR(char *, touch_monitor, NULL, ast_free); | 
					
						
							|  |  |  | 	RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	set_touch_res = set_touch_variables(bridge_channel->chan, 1, &touch_format, | 
					
						
							|  |  |  | 		&touch_monitor, &touch_monitor_prefix); | 
					
						
							|  |  |  | 	switch (set_touch_res) { | 
					
						
							|  |  |  | 	case SET_TOUCH_SUCCESS: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SET_TOUCH_UNSET: | 
					
						
							|  |  |  | 		set_touch_res = set_touch_variables(peer_chan, 1, &touch_format, &touch_monitor, | 
					
						
							|  |  |  | 			&touch_monitor_prefix); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 		if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) { | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 			return; | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case SET_TOUCH_ALLOC_FAILURE: | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ast_strlen_zero(touch_monitor)) { | 
					
						
							|  |  |  | 		len = strlen(touch_monitor) + 50; | 
					
						
							|  |  |  | 		touch_filename = ast_alloca(len); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		snprintf(touch_filename, len, "%s-%ld-%s.%s", | 
					
						
							|  |  |  | 			S_OR(touch_monitor_prefix, "auto"), | 
					
						
							|  |  |  | 			(long) time(NULL), | 
					
						
							|  |  |  | 			touch_monitor, | 
					
						
							|  |  |  | 			S_OR(touch_format, "wav")); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		char *caller_chan_id; | 
					
						
							|  |  |  | 		char *peer_chan_id; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 		caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid, | 
					
						
							|  |  |  | 			ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan))); | 
					
						
							|  |  |  | 		peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid, | 
					
						
							|  |  |  | 			ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan))); | 
					
						
							|  |  |  | 		len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50; | 
					
						
							|  |  |  | 		touch_filename = ast_alloca(len); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		snprintf(touch_filename, len, "%s-%ld-%s-%s.%s", | 
					
						
							|  |  |  | 			S_OR(touch_monitor_prefix, "auto"), | 
					
						
							|  |  |  | 			(long) time(NULL), | 
					
						
							|  |  |  | 			caller_chan_id, | 
					
						
							|  |  |  | 			peer_chan_id, | 
					
						
							|  |  |  | 			S_OR(touch_format, "wav")); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	for (x = 0; x < strlen(touch_filename); x++) { | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 		if (touch_filename[x] == '/') { | 
					
						
							|  |  |  | 			touch_filename[x] = '-'; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 	ast_verb(4, "AutoMixMonitor used to record call. Filename: %s\n", touch_filename); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_start_mixmonitor(peer_chan, touch_filename, "b")) { | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 		ast_verb(4, "AutoMixMonitor feature was tried by '%s' but MixMonitor failed to start.\n", | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 			ast_channel_name(bridge_channel->chan)); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) { | 
					
						
							|  |  |  | 			ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) { | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL); | 
					
						
							|  |  |  | 		ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	if (!ast_strlen_zero(start_message)) { | 
					
						
							|  |  |  | 		ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL); | 
					
						
							|  |  |  | 		ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pbx_builtin_setvar_helper(peer_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-26 21:34:23 +00:00
										 |  |  | static int feature_automixmonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt) | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	static const char *mixmonitor_spy_type = "MixMonitor"; | 
					
						
							|  |  |  | 	const char *stop_message; | 
					
						
							|  |  |  | 	const char *start_message; | 
					
						
							|  |  |  | 	struct ast_bridge_features_automixmonitor *options = hook_pvt; | 
					
						
							|  |  |  | 	enum ast_bridge_features_monitor start_stop = options ? options->start_stop : AUTO_MONITOR_TOGGLE; | 
					
						
							|  |  |  | 	int is_monitoring; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup); | 
					
						
							|  |  |  | 	RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-28 21:35:41 +00:00
										 |  |  | 	ast_channel_lock(bridge_channel->chan); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	features_cfg = ast_get_chan_features_general_config(bridge_channel->chan); | 
					
						
							| 
									
										
										
										
											2014-10-28 21:35:41 +00:00
										 |  |  | 	ast_channel_unlock(bridge_channel->chan); | 
					
						
							| 
									
										
										
										
											2013-07-26 21:34:23 +00:00
										 |  |  | 	ast_bridge_channel_lock_bridge(bridge_channel); | 
					
						
							|  |  |  | 	peer_chan = ast_bridge_peer_nolock(bridge_channel->bridge, bridge_channel->chan); | 
					
						
							|  |  |  | 	ast_bridge_unlock(bridge_channel->bridge); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!peer_chan) { | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 		ast_verb(4, "Cannot start AutoMixMonitor for %s - cannot determine peer in bridge.\n", | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 			ast_channel_name(bridge_channel->chan)); | 
					
						
							|  |  |  | 		if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) { | 
					
						
							|  |  |  | 			ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 	ast_channel_lock(bridge_channel->chan); | 
					
						
							|  |  |  | 	start_message = pbx_builtin_getvar_helper(bridge_channel->chan, | 
					
						
							|  |  |  | 		"TOUCH_MIXMONITOR_MESSAGE_START"); | 
					
						
							|  |  |  | 	start_message = ast_strdupa(S_OR(start_message, "")); | 
					
						
							|  |  |  | 	stop_message = pbx_builtin_getvar_helper(bridge_channel->chan, | 
					
						
							|  |  |  | 		"TOUCH_MIXMONITOR_MESSAGE_STOP"); | 
					
						
							|  |  |  | 	stop_message = ast_strdupa(S_OR(stop_message, "")); | 
					
						
							|  |  |  | 	ast_channel_unlock(bridge_channel->chan); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	is_monitoring = | 
					
						
							|  |  |  | 		0 < ast_channel_audiohook_count_by_source(peer_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY); | 
					
						
							|  |  |  | 	switch (start_stop) { | 
					
						
							|  |  |  | 	case AUTO_MONITOR_TOGGLE: | 
					
						
							|  |  |  | 		if (is_monitoring) { | 
					
						
							|  |  |  | 			stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			start_automixmonitor(bridge_channel, peer_chan, features_cfg, start_message); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	case AUTO_MONITOR_START: | 
					
						
							|  |  |  | 		if (!is_monitoring) { | 
					
						
							|  |  |  | 			start_automixmonitor(bridge_channel, peer_chan, features_cfg, start_message); | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 		ast_verb(4, "AutoMixMonitor already recording call.\n"); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case AUTO_MONITOR_STOP: | 
					
						
							|  |  |  | 		if (is_monitoring) { | 
					
						
							|  |  |  | 			stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message); | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-05-28 22:54:12 +00:00
										 |  |  | 		ast_verb(4, "AutoMixMonitor already stopped on call.\n"); | 
					
						
							| 
									
										
										
										
											2013-07-03 22:36:38 +00:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Fake start/stop to invoker so will think it did something but | 
					
						
							|  |  |  | 	 * was already in that mode. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) { | 
					
						
							|  |  |  | 		ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (is_monitoring) { | 
					
						
							|  |  |  | 		if (!ast_strlen_zero(start_message)) { | 
					
						
							|  |  |  | 			ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if (!ast_strlen_zero(stop_message)) { | 
					
						
							|  |  |  | 			ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-05 18:18:27 +00:00
										 |  |  | /*! \brief Internal built in feature for hangup */ | 
					
						
							| 
									
										
										
										
											2013-07-26 21:34:23 +00:00
										 |  |  | static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_pvt) | 
					
						
							| 
									
										
										
										
											2009-03-05 18:18:27 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-21 17:55:48 +00:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * This is very simple, we simply change the state on the | 
					
						
							|  |  |  | 	 * bridge_channel to force the channel out of the bridge and the | 
					
						
							|  |  |  | 	 * core takes care of the rest. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2013-08-22 21:09:52 +00:00
										 |  |  | 	ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, | 
					
						
							|  |  |  | 		AST_CAUSE_NORMAL_CLEARING); | 
					
						
							| 
									
										
										
										
											2009-03-05 18:18:27 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int unload_module(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-02-11 17:03:04 +00:00
										 |  |  | 	ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_HANGUP); | 
					
						
							|  |  |  | 	ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMON); | 
					
						
							|  |  |  | 	ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMIXMON); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-05 18:18:27 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int load_module(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ast_bridge_features_register(AST_BRIDGE_BUILTIN_HANGUP, feature_hangup, NULL); | 
					
						
							| 
									
										
										
										
											2013-07-01 16:01:24 +00:00
										 |  |  | 	ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMON, feature_automonitor, NULL); | 
					
						
							|  |  |  | 	ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMIXMON, feature_automixmonitor, NULL); | 
					
						
							| 
									
										
										
										
											2009-03-05 18:18:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-11 17:03:04 +00:00
										 |  |  | 	/* This module cannot be unloaded until shutdown */ | 
					
						
							|  |  |  | 	ast_module_shutdown_ref(ast_module_info->self); | 
					
						
							| 
									
										
										
										
											2009-03-05 18:18:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return AST_MODULE_LOAD_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-18 10:01:26 -05:00
										 |  |  | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Built in bridging features", | 
					
						
							|  |  |  | 	.support_level = AST_MODULE_SUPPORT_CORE, | 
					
						
							|  |  |  | 	.load = load_module, | 
					
						
							|  |  |  | 	.unload = unload_module, | 
					
						
							|  |  |  | 	.optional_modules = "res_monitor", | 
					
						
							|  |  |  | ); |