diff options
Diffstat (limited to 'bridges/bridge_builtin_features.c')
-rw-r--r-- | bridges/bridge_builtin_features.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c index 2d8a68a1a..91f709bc4 100644 --- a/bridges/bridge_builtin_features.c +++ b/bridges/bridge_builtin_features.c @@ -50,6 +50,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/parking.h" #include "asterisk/features_config.h" +#include "asterisk/monitor.h" +#include "asterisk/mixmonitor.h" +#include "asterisk/audiohook.h" /*! * \brief Helper function that presents dialtone and grabs extension @@ -463,6 +466,304 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg return 0; } +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; + + ast_channel_lock(bridge_channel->chan); + stop_message = pbx_builtin_getvar_helper(bridge_channel->chan, "TOUCH_MONITOR_MESSAGE_STOP"); + stop_message = ast_strdupa(S_OR(stop_message, "")); + ast_channel_unlock(bridge_channel->chan); + + ast_verb(3, "AutoMonitor used to stop recording call.\n"); + + ast_channel_lock(peer_chan); + if (ast_channel_monitor(peer_chan)) { + if (ast_channel_monitor(peer_chan)->stop(peer_chan, 1)) { + ast_verb(3, "Cannot stop AutoMonitor for %s\n", 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); + } + 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); + } +} + +enum set_touch_variables_res { + SET_TOUCH_SUCCESS = 0, + SET_TOUCH_UNSET, + SET_TOUCH_ALLOC_FAILURE, +}; + +static int 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 *c_touch_format, *c_touch_monitor, *c_touch_monitor_prefix; + + SCOPED_CHANNELLOCK(lock, chan); + + c_touch_format = pbx_builtin_getvar_helper(chan, is_mixmonitor ? "TOUCH_MIXMONITOR_FORMAT" : "TOUCH_MONITOR_FORMAT"); + + if (!ast_strlen_zero(c_touch_format)) { + if (!(*touch_format = ast_strdup(c_touch_format))) { + return SET_TOUCH_ALLOC_FAILURE; + } + res = SET_TOUCH_SUCCESS; + } + + c_touch_monitor = pbx_builtin_getvar_helper(chan, is_mixmonitor ? "TOUCH_MIXMONITOR" : "TOUCH_MONITOR"); + + if (!ast_strlen_zero(c_touch_monitor)) { + if (!(*touch_monitor = ast_strdup(c_touch_monitor))) { + return SET_TOUCH_ALLOC_FAILURE; + } + res = SET_TOUCH_SUCCESS; + } + + c_touch_monitor_prefix = pbx_builtin_getvar_helper(chan, is_mixmonitor ? "TOUCH_MIXMONITOR_PREFIX" : "TOUCH_MONITOR_PREFIX"); + + if (!ast_strlen_zero(c_touch_monitor_prefix)) { + if (!(*touch_monitor_prefix = ast_strdup(c_touch_monitor_prefix))) { + return SET_TOUCH_ALLOC_FAILURE; + } + res = SET_TOUCH_SUCCESS; + } + + return res; +} + +static int feature_automonitor(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt) +{ + char *caller_chan_id = NULL, *peer_chan_id = NULL, *touch_filename = NULL; + size_t len; + const char *automon_message; + 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); + + RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup); + RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup); + + features_cfg = ast_get_chan_features_general_config(bridge_channel->chan); + peer_chan = ast_bridge_peer(bridge, bridge_channel->chan); + + if (!peer_chan) { + ast_verb(3, "Cannot start AutoMonitor for %s - can not determine peer in bridge.\n", 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; + } + + if (ast_channel_monitor(peer_chan)) { + stop_automonitor(bridge_channel, peer_chan, features_cfg); + return 0; + } + + if ((set_touch_res = set_touch_variables(bridge_channel->chan, 0, &touch_format, &touch_monitor, &touch_monitor_prefix))) { + if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) { + return 0; + } + if (set_touch_variables(peer_chan, 0, &touch_format, &touch_monitor, &touch_monitor_prefix) == SET_TOUCH_ALLOC_FAILURE) { + return 0; + } + } + + if (!ast_strlen_zero(touch_monitor)) { + len = strlen(touch_monitor) + 50; + touch_filename = ast_alloca(len); + snprintf(touch_filename, len, "%s-%ld-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), touch_monitor); + } else { + 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); + snprintf(touch_filename, len, "%s-%ld-%s-%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), caller_chan_id, peer_chan_id); + } + + for ( x = 0; x < strlen(touch_filename); x++) { + if (touch_filename[x] == '/') { + touch_filename[x] = '-'; + } + } + + ast_verb(3, "AutoMonitor used to record call. Filename: %s\n", touch_filename); + + if (ast_monitor_start(peer_chan, touch_format, touch_filename, 1, X_REC_IN | X_REC_OUT)) { + ast_verb(3, "automon feature was tried by '%s' but monitor failed to start.\n", ast_channel_name(bridge_channel->chan)); + return 0; + } + + ast_channel_lock(bridge_channel->chan); + if ((automon_message = pbx_builtin_getvar_helper(bridge_channel->chan, "TOUCH_MONITOR_MESSAGE_START"))) { + automon_message = ast_strdupa(automon_message); + } + ast_channel_unlock(bridge_channel->chan); + + if ((features_cfg = ast_get_chan_features_general_config(bridge_channel->chan)) && !(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(automon_message)) { + ast_bridge_channel_queue_playfile(bridge_channel, NULL, automon_message, NULL); + ast_bridge_channel_write_playfile(bridge_channel, NULL, automon_message, NULL); + } + + pbx_builtin_setvar_helper(peer_chan, "TOUCH_MONITOR_OUTPUT", touch_filename); + + 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; + + ast_channel_lock(bridge_channel->chan); + 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); + + ast_verb(3, "AutoMixMonitor used to stop recording call.\n"); + + if (ast_stop_mixmonitor(peer_chan, NULL)) { + ast_verb(3, "Failed to stop Mixmonitor for %s.\n", 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; + } + + 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); + } + +} + +static int feature_automixmonitor(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt) +{ + char *caller_chan_id = NULL, *peer_chan_id = NULL, *touch_filename = NULL; + size_t len; + const char *automon_message; + static char *mixmonitor_spy_type = "MixMonitor"; + int count, 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); + + RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup); + RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup); + + features_cfg = ast_get_chan_features_general_config(bridge_channel->chan); + + peer_chan = ast_bridge_peer(bridge, bridge_channel->chan); + + if (!peer_chan) { + ast_verb(3, "Cannot start AutoMixMonitor for %s - can not determine peer in bridge.\n", 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; + } + + count = ast_channel_audiohook_count_by_source(peer_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY); + if (count > 0) { + stop_automixmonitor(bridge_channel, peer_chan, features_cfg); + return 0; + } + + if ((set_touch_res = set_touch_variables(bridge_channel->chan, 1, &touch_format, &touch_monitor, &touch_monitor_prefix))) { + if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) { + return 0; + } + if (set_touch_variables(peer_chan, 1, &touch_format, &touch_monitor, &touch_monitor_prefix) == SET_TOUCH_ALLOC_FAILURE) { + return 0; + } + } + + if (!ast_strlen_zero(touch_monitor)) { + len = strlen(touch_monitor) + 50; + touch_filename = ast_alloca(len); + snprintf(touch_filename, len, "%s-%ld-%s.%s", S_OR(touch_monitor_prefix, "auto"), (long)time(NULL), touch_monitor, S_OR(touch_format, "wav")); + } else { + 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); + 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")); + } + + for ( x = 0; x < strlen(touch_filename); x++) { + if (touch_filename[x] == '/') { + touch_filename[x] = '-'; + } + } + + ast_verb(3, "AutoMixMonitor used to record call. Filename: %s\n", touch_filename); + + if (ast_start_mixmonitor(peer_chan, touch_filename, "b")) { + ast_verb(3, "automixmon feature was tried by '%s' but mixmonitor failed to start.\n", 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; + } + + ast_channel_lock(bridge_channel->chan); + if ((automon_message = pbx_builtin_getvar_helper(bridge_channel->chan, "TOUCH_MIXMONITOR_MESSAGE_START"))) { + automon_message = ast_strdupa(automon_message); + } + ast_channel_unlock(bridge_channel->chan); + + if ((features_cfg = ast_get_chan_features_general_config(bridge_channel->chan)) && !(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(automon_message)) { + ast_bridge_channel_queue_playfile(bridge_channel, NULL, automon_message, NULL); + ast_bridge_channel_write_playfile(bridge_channel, NULL, automon_message, NULL); + } + + pbx_builtin_setvar_helper(peer_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename); + + return 0; +} + /*! \brief Internal built in feature for hangup */ static int feature_hangup(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt) { @@ -485,6 +786,8 @@ static int load_module(void) ast_bridge_features_register(AST_BRIDGE_BUILTIN_BLINDTRANSFER, feature_blind_transfer, NULL); ast_bridge_features_register(AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, feature_attended_transfer, NULL); ast_bridge_features_register(AST_BRIDGE_BUILTIN_HANGUP, feature_hangup, NULL); + ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMON, feature_automonitor, NULL); + ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMIXMON, feature_automixmonitor, NULL); /* Bump up our reference count so we can't be unloaded */ ast_module_ref(ast_module_info->self); |