2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2009, Digium, Inc.
6 * Joshua Colp <jcolp@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Built in bridging features
23 * \author Joshua Colp <jcolp@digium.com>
29 <support_level>core</support_level>
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39 #include <sys/types.h>
42 #include "asterisk/module.h"
43 #include "asterisk/channel.h"
44 #include "asterisk/bridge.h"
45 #include "asterisk/bridge_technology.h"
46 #include "asterisk/frame.h"
47 #include "asterisk/file.h"
48 #include "asterisk/app.h"
49 #include "asterisk/astobj2.h"
50 #include "asterisk/pbx.h"
51 #include "asterisk/parking.h"
52 #include "asterisk/features_config.h"
53 #include "asterisk/monitor.h"
54 #include "asterisk/mixmonitor.h"
55 #include "asterisk/audiohook.h"
56 #include "asterisk/causes.h"
58 enum set_touch_variables_res {
61 SET_TOUCH_ALLOC_FAILURE,
64 static void set_touch_variable(enum set_touch_variables_res *res, struct ast_channel *chan, const char *var_name, char **touch)
68 if (*res == SET_TOUCH_ALLOC_FAILURE) {
71 c_touch = pbx_builtin_getvar_helper(chan, var_name);
72 if (!ast_strlen_zero(c_touch)) {
73 *touch = ast_strdup(c_touch);
75 *res = SET_TOUCH_ALLOC_FAILURE;
77 *res = SET_TOUCH_SUCCESS;
82 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)
84 enum set_touch_variables_res res = SET_TOUCH_UNSET;
85 const char *var_format;
86 const char *var_monitor;
87 const char *var_prefix;
89 SCOPED_CHANNELLOCK(lock, chan);
92 var_format = "TOUCH_MIXMONITOR_FORMAT";
93 var_monitor = "TOUCH_MIXMONITOR";
94 var_prefix = "TOUCH_MIXMONITOR_PREFIX";
96 var_format = "TOUCH_MONITOR_FORMAT";
97 var_monitor = "TOUCH_MONITOR";
98 var_prefix = "TOUCH_MONITOR_PREFIX";
100 set_touch_variable(&res, chan, var_format, touch_format);
101 set_touch_variable(&res, chan, var_monitor, touch_monitor);
102 set_touch_variable(&res, chan, var_prefix, touch_monitor_prefix);
107 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)
109 ast_verb(4, "AutoMonitor used to stop recording call.\n");
111 ast_channel_lock(peer_chan);
112 if (ast_channel_monitor(peer_chan)) {
113 if (ast_channel_monitor(peer_chan)->stop(peer_chan, 1)) {
114 ast_verb(4, "Cannot stop AutoMonitor for %s\n", ast_channel_name(bridge_channel->chan));
115 if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
116 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
118 ast_channel_unlock(peer_chan);
122 /* Something else removed the Monitor before we got to it. */
123 ast_channel_unlock(peer_chan);
127 ast_channel_unlock(peer_chan);
129 if (features_cfg && !(ast_strlen_zero(features_cfg->courtesytone))) {
130 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
131 ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
134 if (!ast_strlen_zero(stop_message)) {
135 ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
136 ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL);
140 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)
142 char *touch_filename;
145 enum set_touch_variables_res set_touch_res;
147 RAII_VAR(char *, touch_format, NULL, ast_free);
148 RAII_VAR(char *, touch_monitor, NULL, ast_free);
149 RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);
151 set_touch_res = set_touch_variables(bridge_channel->chan, 0, &touch_format,
152 &touch_monitor, &touch_monitor_prefix);
153 switch (set_touch_res) {
154 case SET_TOUCH_SUCCESS:
156 case SET_TOUCH_UNSET:
157 set_touch_res = set_touch_variables(peer_chan, 0, &touch_format, &touch_monitor,
158 &touch_monitor_prefix);
159 if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
163 case SET_TOUCH_ALLOC_FAILURE:
167 if (!ast_strlen_zero(touch_monitor)) {
168 len = strlen(touch_monitor) + 50;
169 touch_filename = ast_alloca(len);
170 snprintf(touch_filename, len, "%s-%ld-%s",
171 S_OR(touch_monitor_prefix, "auto"),
175 char *caller_chan_id;
178 caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid,
179 ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan)));
180 peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid,
181 ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan)));
182 len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50;
183 touch_filename = ast_alloca(len);
184 snprintf(touch_filename, len, "%s-%ld-%s-%s",
185 S_OR(touch_monitor_prefix, "auto"),
191 for (x = 0; x < strlen(touch_filename); x++) {
192 if (touch_filename[x] == '/') {
193 touch_filename[x] = '-';
197 ast_verb(4, "AutoMonitor used to record call. Filename: %s\n", touch_filename);
199 if (ast_monitor_start(peer_chan, touch_format, touch_filename, 1, X_REC_IN | X_REC_OUT, NULL)) {
200 ast_verb(4, "AutoMonitor feature was tried by '%s' but monitor failed to start.\n",
201 ast_channel_name(bridge_channel->chan));
205 if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
206 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
207 ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
210 if (!ast_strlen_zero(start_message)) {
211 ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
212 ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL);
215 pbx_builtin_setvar_helper(peer_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
218 static int feature_automonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
220 const char *start_message;
221 const char *stop_message;
222 struct ast_bridge_features_automonitor *options = hook_pvt;
223 enum ast_bridge_features_monitor start_stop = options ? options->start_stop : AUTO_MONITOR_TOGGLE;
226 RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup);
227 RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup);
229 ast_channel_lock(bridge_channel->chan);
230 features_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
231 ast_channel_unlock(bridge_channel->chan);
232 ast_bridge_channel_lock_bridge(bridge_channel);
233 peer_chan = ast_bridge_peer_nolock(bridge_channel->bridge, bridge_channel->chan);
234 ast_bridge_unlock(bridge_channel->bridge);
237 ast_verb(4, "Cannot start AutoMonitor for %s - can not determine peer in bridge.\n",
238 ast_channel_name(bridge_channel->chan));
239 if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
240 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
245 ast_channel_lock(bridge_channel->chan);
246 start_message = pbx_builtin_getvar_helper(bridge_channel->chan,
247 "TOUCH_MONITOR_MESSAGE_START");
248 start_message = ast_strdupa(S_OR(start_message, ""));
249 stop_message = pbx_builtin_getvar_helper(bridge_channel->chan,
250 "TOUCH_MONITOR_MESSAGE_STOP");
251 stop_message = ast_strdupa(S_OR(stop_message, ""));
252 ast_channel_unlock(bridge_channel->chan);
254 is_monitoring = ast_channel_monitor(peer_chan) != NULL;
255 switch (start_stop) {
256 case AUTO_MONITOR_TOGGLE:
258 stop_automonitor(bridge_channel, peer_chan, features_cfg, stop_message);
260 start_automonitor(bridge_channel, peer_chan, features_cfg, start_message);
263 case AUTO_MONITOR_START:
264 if (!is_monitoring) {
265 start_automonitor(bridge_channel, peer_chan, features_cfg, start_message);
268 ast_verb(4, "AutoMonitor already recording call.\n");
270 case AUTO_MONITOR_STOP:
272 stop_automonitor(bridge_channel, peer_chan, features_cfg, stop_message);
275 ast_verb(4, "AutoMonitor already stopped on call.\n");
280 * Fake start/stop to invoker so will think it did something but
281 * was already in that mode.
283 if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
284 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
287 if (!ast_strlen_zero(start_message)) {
288 ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
291 if (!ast_strlen_zero(stop_message)) {
292 ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
298 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)
300 ast_verb(4, "AutoMixMonitor used to stop recording call.\n");
302 if (ast_stop_mixmonitor(peer_chan, NULL)) {
303 ast_verb(4, "Failed to stop AutoMixMonitor for %s.\n", ast_channel_name(bridge_channel->chan));
304 if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
305 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
310 if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
311 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
312 ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
315 if (!ast_strlen_zero(stop_message)) {
316 ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
317 ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL);
321 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)
323 char *touch_filename;
326 enum set_touch_variables_res set_touch_res;
328 RAII_VAR(char *, touch_format, NULL, ast_free);
329 RAII_VAR(char *, touch_monitor, NULL, ast_free);
330 RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);
332 set_touch_res = set_touch_variables(bridge_channel->chan, 1, &touch_format,
333 &touch_monitor, &touch_monitor_prefix);
334 switch (set_touch_res) {
335 case SET_TOUCH_SUCCESS:
337 case SET_TOUCH_UNSET:
338 set_touch_res = set_touch_variables(peer_chan, 1, &touch_format, &touch_monitor,
339 &touch_monitor_prefix);
340 if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
344 case SET_TOUCH_ALLOC_FAILURE:
348 if (!ast_strlen_zero(touch_monitor)) {
349 len = strlen(touch_monitor) + 50;
350 touch_filename = ast_alloca(len);
351 snprintf(touch_filename, len, "%s-%ld-%s.%s",
352 S_OR(touch_monitor_prefix, "auto"),
355 S_OR(touch_format, "wav"));
357 char *caller_chan_id;
360 caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid,
361 ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan)));
362 peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid,
363 ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan)));
364 len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50;
365 touch_filename = ast_alloca(len);
366 snprintf(touch_filename, len, "%s-%ld-%s-%s.%s",
367 S_OR(touch_monitor_prefix, "auto"),
371 S_OR(touch_format, "wav"));
374 for (x = 0; x < strlen(touch_filename); x++) {
375 if (touch_filename[x] == '/') {
376 touch_filename[x] = '-';
380 ast_verb(4, "AutoMixMonitor used to record call. Filename: %s\n", touch_filename);
382 if (ast_start_mixmonitor(peer_chan, touch_filename, "b")) {
383 ast_verb(4, "AutoMixMonitor feature was tried by '%s' but MixMonitor failed to start.\n",
384 ast_channel_name(bridge_channel->chan));
386 if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
387 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
392 if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
393 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
394 ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
397 if (!ast_strlen_zero(start_message)) {
398 ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
399 ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL);
402 pbx_builtin_setvar_helper(peer_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
405 static int feature_automixmonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
407 static const char *mixmonitor_spy_type = "MixMonitor";
408 const char *stop_message;
409 const char *start_message;
410 struct ast_bridge_features_automixmonitor *options = hook_pvt;
411 enum ast_bridge_features_monitor start_stop = options ? options->start_stop : AUTO_MONITOR_TOGGLE;
414 RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup);
415 RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup);
417 ast_channel_lock(bridge_channel->chan);
418 features_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
419 ast_channel_unlock(bridge_channel->chan);
420 ast_bridge_channel_lock_bridge(bridge_channel);
421 peer_chan = ast_bridge_peer_nolock(bridge_channel->bridge, bridge_channel->chan);
422 ast_bridge_unlock(bridge_channel->bridge);
425 ast_verb(4, "Cannot start AutoMixMonitor for %s - cannot determine peer in bridge.\n",
426 ast_channel_name(bridge_channel->chan));
427 if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
428 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
433 ast_channel_lock(bridge_channel->chan);
434 start_message = pbx_builtin_getvar_helper(bridge_channel->chan,
435 "TOUCH_MIXMONITOR_MESSAGE_START");
436 start_message = ast_strdupa(S_OR(start_message, ""));
437 stop_message = pbx_builtin_getvar_helper(bridge_channel->chan,
438 "TOUCH_MIXMONITOR_MESSAGE_STOP");
439 stop_message = ast_strdupa(S_OR(stop_message, ""));
440 ast_channel_unlock(bridge_channel->chan);
443 0 < ast_channel_audiohook_count_by_source(peer_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
444 switch (start_stop) {
445 case AUTO_MONITOR_TOGGLE:
447 stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message);
449 start_automixmonitor(bridge_channel, peer_chan, features_cfg, start_message);
452 case AUTO_MONITOR_START:
453 if (!is_monitoring) {
454 start_automixmonitor(bridge_channel, peer_chan, features_cfg, start_message);
457 ast_verb(4, "AutoMixMonitor already recording call.\n");
459 case AUTO_MONITOR_STOP:
461 stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message);
464 ast_verb(4, "AutoMixMonitor already stopped on call.\n");
469 * Fake start/stop to invoker so will think it did something but
470 * was already in that mode.
472 if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
473 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
476 if (!ast_strlen_zero(start_message)) {
477 ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
480 if (!ast_strlen_zero(stop_message)) {
481 ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
487 /*! \brief Internal built in feature for hangup */
488 static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
491 * This is very simple, we simply change the state on the
492 * bridge_channel to force the channel out of the bridge and the
493 * core takes care of the rest.
495 ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
496 AST_CAUSE_NORMAL_CLEARING);
500 static int unload_module(void)
502 ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_HANGUP);
503 ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMON);
504 ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMIXMON);
509 static int load_module(void)
511 ast_bridge_features_register(AST_BRIDGE_BUILTIN_HANGUP, feature_hangup, NULL);
512 ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMON, feature_automonitor, NULL);
513 ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMIXMON, feature_automixmonitor, NULL);
515 /* This module cannot be unloaded until shutdown */
516 ast_module_shutdown_ref(ast_module_info->self);
518 return AST_MODULE_LOAD_SUCCESS;
521 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Built in bridging features");