123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- /*
- * 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
- */
- /*** MODULEINFO
- <support_level>core</support_level>
- ***/
- #include "asterisk.h"
- ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
- #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"
- #include "asterisk/bridge.h"
- #include "asterisk/bridge_technology.h"
- #include "asterisk/frame.h"
- #include "asterisk/file.h"
- #include "asterisk/app.h"
- #include "asterisk/astobj2.h"
- #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"
- #include "asterisk/causes.h"
- 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)
- {
- const char *c_touch;
- 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;
- }
- 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_verb(4, "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(4, "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);
- }
- }
- 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)
- {
- char *touch_filename;
- 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);
- 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);
- if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
- return;
- }
- break;
- case SET_TOUCH_ALLOC_FAILURE:
- return;
- }
- 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 {
- char *caller_chan_id;
- char *peer_chan_id;
- 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(4, "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, NULL)) {
- ast_verb(4, "AutoMonitor feature was tried by '%s' but monitor failed to start.\n",
- ast_channel_name(bridge_channel->chan));
- 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(start_message)) {
- ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
- ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL);
- }
- pbx_builtin_setvar_helper(peer_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
- }
- static int feature_automonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
- {
- const char *start_message;
- const char *stop_message;
- 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);
- ast_channel_lock(bridge_channel->chan);
- features_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
- ast_channel_unlock(bridge_channel->chan);
- 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);
- if (!peer_chan) {
- ast_verb(4, "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;
- }
- ast_channel_lock(bridge_channel->chan);
- 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");
- stop_message = ast_strdupa(S_OR(stop_message, ""));
- ast_channel_unlock(bridge_channel->chan);
- 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;
- }
- ast_verb(4, "AutoMonitor already recording call.\n");
- break;
- case AUTO_MONITOR_STOP:
- if (is_monitoring) {
- stop_automonitor(bridge_channel, peer_chan, features_cfg, stop_message);
- return 0;
- }
- ast_verb(4, "AutoMonitor already stopped on call.\n");
- 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)
- {
- ast_verb(4, "AutoMixMonitor used to stop recording call.\n");
- if (ast_stop_mixmonitor(peer_chan, NULL)) {
- ast_verb(4, "Failed to stop AutoMixMonitor 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 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)
- {
- char *touch_filename;
- 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);
- 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);
- if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
- return;
- }
- break;
- case SET_TOUCH_ALLOC_FAILURE:
- return;
- }
- 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 {
- char *caller_chan_id;
- char *peer_chan_id;
- 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(4, "AutoMixMonitor used to record call. Filename: %s\n", touch_filename);
- if (ast_start_mixmonitor(peer_chan, touch_filename, "b")) {
- ast_verb(4, "AutoMixMonitor 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;
- }
- 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(start_message)) {
- ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
- ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL);
- }
- pbx_builtin_setvar_helper(peer_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
- }
- static int feature_automixmonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
- {
- 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);
- ast_channel_lock(bridge_channel->chan);
- features_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
- ast_channel_unlock(bridge_channel->chan);
- 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);
- if (!peer_chan) {
- ast_verb(4, "Cannot start AutoMixMonitor for %s - cannot 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;
- }
- 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;
- }
- ast_verb(4, "AutoMixMonitor already recording call.\n");
- break;
- case AUTO_MONITOR_STOP:
- if (is_monitoring) {
- stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message);
- return 0;
- }
- ast_verb(4, "AutoMixMonitor already stopped on call.\n");
- 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;
- }
- /*! \brief Internal built in feature for hangup */
- static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
- {
- /*
- * 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.
- */
- ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
- AST_CAUSE_NORMAL_CLEARING);
- return 0;
- }
- static int unload_module(void)
- {
- ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_HANGUP);
- ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMON);
- ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMIXMON);
- return 0;
- }
- static int load_module(void)
- {
- 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);
- /* This module cannot be unloaded until shutdown */
- ast_module_shutdown_ref(ast_module_info->self);
- return AST_MODULE_LOAD_SUCCESS;
- }
- AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Built in bridging features");
|