Asterisk - The Open Source Telephony Project  21.4.1
bridge_builtin_features.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2009, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
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.
13  *
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.
17  */
18 
19 /*! \file
20  *
21  * \brief Built in bridging features
22  *
23  * \author Joshua Colp <jcolp@digium.com>
24  *
25  * \ingroup bridges
26  */
27 
28 /*** MODULEINFO
29  <support_level>core</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 
40 #include "asterisk/module.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/bridge.h"
44 #include "asterisk/frame.h"
45 #include "asterisk/file.h"
46 #include "asterisk/app.h"
47 #include "asterisk/astobj2.h"
48 #include "asterisk/pbx.h"
49 #include "asterisk/parking.h"
50 #include "asterisk/features_config.h"
51 #include "asterisk/mixmonitor.h"
52 #include "asterisk/audiohook.h"
53 #include "asterisk/causes.h"
54 #include "asterisk/beep.h"
55 
56 enum set_touch_variables_res {
57  SET_TOUCH_SUCCESS,
58  SET_TOUCH_UNSET,
59  SET_TOUCH_ALLOC_FAILURE,
60 };
61 
62 static void set_touch_variable(enum set_touch_variables_res *res, struct ast_channel *chan, const char *var_name, char **touch)
63 {
64  const char *c_touch;
65 
66  if (*res == SET_TOUCH_ALLOC_FAILURE) {
67  return;
68  }
69  c_touch = pbx_builtin_getvar_helper(chan, var_name);
70  if (!ast_strlen_zero(c_touch)) {
71  *touch = ast_strdup(c_touch);
72  if (!*touch) {
73  *res = SET_TOUCH_ALLOC_FAILURE;
74  } else {
75  *res = SET_TOUCH_SUCCESS;
76  }
77  }
78 }
79 
80 static enum set_touch_variables_res set_touch_variables(struct ast_channel *chan, char **touch_format, char **touch_monitor, char **touch_monitor_prefix, char **touch_monitor_beep)
81 {
82  enum set_touch_variables_res res = SET_TOUCH_UNSET;
83  const char *var_format;
84  const char *var_monitor;
85  const char *var_prefix;
86  const char *var_beep;
87 
88  SCOPED_CHANNELLOCK(lock, chan);
89 
90  var_format = "TOUCH_MIXMONITOR_FORMAT";
91  var_monitor = "TOUCH_MIXMONITOR";
92  var_prefix = "TOUCH_MIXMONITOR_PREFIX";
93  var_beep = "TOUCH_MIXMONITOR_BEEP";
94 
95  set_touch_variable(&res, chan, var_format, touch_format);
96  set_touch_variable(&res, chan, var_monitor, touch_monitor);
97  set_touch_variable(&res, chan, var_prefix, touch_monitor_prefix);
98  set_touch_variable(&res, chan, var_beep, touch_monitor_beep);
99 
100  return res;
101 }
102 
103 
104 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)
105 {
106  ast_verb(4, "AutoMixMonitor used to stop recording call.\n");
107 
108  if (ast_stop_mixmonitor(peer_chan, NULL)) {
109  ast_verb(4, "Failed to stop AutoMixMonitor for %s.\n", ast_channel_name(bridge_channel->chan));
110  if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
111  ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
112  }
113  return;
114  }
115 
116  if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
117  ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
118  ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
119  }
120 
121  if (!ast_strlen_zero(stop_message)) {
122  ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
123  ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL);
124  }
125 }
126 
127 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)
128 {
129  char *touch_filename, mix_options[32] = "b";
130  size_t len;
131  int x;
132  enum set_touch_variables_res set_touch_res;
133 
134  RAII_VAR(char *, touch_format, NULL, ast_free);
135  RAII_VAR(char *, touch_monitor, NULL, ast_free);
136  RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);
137  RAII_VAR(char *, touch_monitor_beep, NULL, ast_free);
138 
139  set_touch_res = set_touch_variables(bridge_channel->chan, &touch_format,
140  &touch_monitor, &touch_monitor_prefix, &touch_monitor_beep);
141  switch (set_touch_res) {
142  case SET_TOUCH_SUCCESS:
143  break;
144  case SET_TOUCH_UNSET:
145  set_touch_res = set_touch_variables(peer_chan, &touch_format, &touch_monitor,
146  &touch_monitor_prefix, &touch_monitor_beep);
147  if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
148  return;
149  }
150  break;
151  case SET_TOUCH_ALLOC_FAILURE:
152  return;
153  }
154 
155  if (!ast_strlen_zero(touch_monitor)) {
156  len = strlen(touch_monitor) + 50;
157  touch_filename = ast_alloca(len);
158  snprintf(touch_filename, len, "%s-%ld-%s.%s",
159  S_OR(touch_monitor_prefix, "auto"),
160  (long) time(NULL),
161  touch_monitor,
162  S_OR(touch_format, "wav"));
163  } else {
164  char *caller_chan_id;
165  char *peer_chan_id;
166 
167  caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid,
168  ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan)));
169  peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid,
170  ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan)));
171  len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50;
172  touch_filename = ast_alloca(len);
173  snprintf(touch_filename, len, "%s-%ld-%s-%s.%s",
174  S_OR(touch_monitor_prefix, "auto"),
175  (long) time(NULL),
176  caller_chan_id,
177  peer_chan_id,
178  S_OR(touch_format, "wav"));
179  }
180 
181  for (x = 0; x < strlen(touch_filename); x++) {
182  if (touch_filename[x] == '/') {
183  touch_filename[x] = '-';
184  }
185  }
186 
187  ast_verb(4, "AutoMixMonitor used to record call. Filename: %s\n", touch_filename);
188 
189  if (!ast_strlen_zero(touch_monitor_beep)) {
190  unsigned int interval = 15;
191  if (sscanf(touch_monitor_beep, "%30u", &interval) != 1) {
192  ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n",
193  touch_monitor_beep, interval);
194  }
195 
196  if (interval < 5) {
197  interval = 5;
198  ast_log(LOG_WARNING, "Interval '%s' too small for periodic beep. Using minimum of %u\n",
199  touch_monitor_beep, interval);
200  }
201  snprintf(mix_options, sizeof(mix_options), "bB(%d)", interval);
202  }
203 
204  if (ast_start_mixmonitor(peer_chan, touch_filename, mix_options)) {
205  ast_verb(4, "AutoMixMonitor feature was tried by '%s' but MixMonitor failed to start.\n",
206  ast_channel_name(bridge_channel->chan));
207 
208  if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
209  ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
210  }
211  return;
212  }
213 
214  if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
215  ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
216  ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
217  }
218 
219  if (!ast_strlen_zero(start_message)) {
220  ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
221  ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL);
222  }
223 
224  pbx_builtin_setvar_helper(bridge_channel->chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
225  pbx_builtin_setvar_helper(peer_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
226 }
227 
228 static int feature_automixmonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
229 {
230  static const char *mixmonitor_spy_type = "MixMonitor";
231  const char *stop_message;
232  const char *start_message;
233  struct ast_bridge_features_automixmonitor *options = hook_pvt;
235  int is_monitoring;
236 
237  RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup);
238  RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup);
239 
240  ast_channel_lock(bridge_channel->chan);
241  features_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
242  ast_channel_unlock(bridge_channel->chan);
243  ast_bridge_channel_lock_bridge(bridge_channel);
244  peer_chan = ast_bridge_peer_nolock(bridge_channel->bridge, bridge_channel->chan);
245  ast_bridge_unlock(bridge_channel->bridge);
246 
247  if (!peer_chan) {
248  ast_verb(4, "Cannot start AutoMixMonitor for %s - cannot determine peer in bridge.\n",
249  ast_channel_name(bridge_channel->chan));
250  if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
251  ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
252  }
253  return 0;
254  }
255 
256  ast_channel_lock(bridge_channel->chan);
257  start_message = pbx_builtin_getvar_helper(bridge_channel->chan,
258  "TOUCH_MIXMONITOR_MESSAGE_START");
259  start_message = ast_strdupa(S_OR(start_message, ""));
260  stop_message = pbx_builtin_getvar_helper(bridge_channel->chan,
261  "TOUCH_MIXMONITOR_MESSAGE_STOP");
262  stop_message = ast_strdupa(S_OR(stop_message, ""));
263  ast_channel_unlock(bridge_channel->chan);
264 
265  is_monitoring =
266  0 < ast_channel_audiohook_count_by_source(peer_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
267  switch (start_stop) {
268  case AUTO_MONITOR_TOGGLE:
269  if (is_monitoring) {
270  stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message);
271  } else {
272  start_automixmonitor(bridge_channel, peer_chan, features_cfg, start_message);
273  }
274  return 0;
275  case AUTO_MONITOR_START:
276  if (!is_monitoring) {
277  start_automixmonitor(bridge_channel, peer_chan, features_cfg, start_message);
278  return 0;
279  }
280  ast_verb(4, "AutoMixMonitor already recording call.\n");
281  break;
282  case AUTO_MONITOR_STOP:
283  if (is_monitoring) {
284  stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message);
285  return 0;
286  }
287  ast_verb(4, "AutoMixMonitor already stopped on call.\n");
288  break;
289  }
290 
291  /*
292  * Fake start/stop to invoker so will think it did something but
293  * was already in that mode.
294  */
295  if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
296  ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
297  }
298  if (is_monitoring) {
299  if (!ast_strlen_zero(start_message)) {
300  ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
301  }
302  } else {
303  if (!ast_strlen_zero(stop_message)) {
304  ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
305  }
306  }
307  return 0;
308 }
309 
310 /*! \brief Internal built in feature for hangup */
311 static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
312 {
313  /*
314  * This is very simple, we simply change the state on the
315  * bridge_channel to force the channel out of the bridge and the
316  * core takes care of the rest.
317  */
319  AST_CAUSE_NORMAL_CLEARING);
320  return 0;
321 }
322 
323 static int unload_module(void)
324 {
327 
328  return 0;
329 }
330 
331 static int load_module(void)
332 {
334  ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMIXMON, feature_automixmonitor, NULL);
335 
336  /* This module cannot be unloaded until shutdown */
338 
340 }
341 
342 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Built in bridging features",
343  .support_level = AST_MODULE_SUPPORT_CORE,
344  .load = load_module,
345  .unload = unload_module,
346 );
Main Channel structure associated with a channel.
char * str
Subscriber phone number (Malloced)
Definition: channel.h:291
Asterisk main include file. File version handling, generic pbx functions.
Call Parking API.
Audiohooks Architecture.
struct ast_channel * ast_bridge_peer_nolock(struct ast_bridge *bridge, struct ast_channel *chan)
Get the channel's bridge peer only if the bridge is two-party.
Definition: bridge.c:4047
General features configuration items.
ast_bridge_features_monitor
int ast_start_mixmonitor(struct ast_channel *chan, const char *filename, const char *options)
Start a mixmonitor on a channel with the given parameters.
Definition: mixmonitor.c:74
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
Periodic beeps into the audio of a call.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
struct ast_bridge * bridge
Bridge this channel is participating in.
int ast_bridge_features_unregister(enum ast_bridge_builtin_feature feature)
Unregister a handler for a built in feature.
Definition: bridge.c:3078
Number structure.
Definition: app_followme.c:154
struct ast_party_id id
Caller party ID.
Definition: channel.h:420
int ast_channel_audiohook_count_by_source(struct ast_channel *chan, const char *source, enum ast_audiohook_type type)
Find out how many audiohooks from a certain source exist on a given channel, regardless of status...
Definition: audiohook.c:1109
struct ast_module * self
Definition: module.h:356
ast_mutex_t lock
#define SCOPED_CHANNELLOCK(varname, chan)
scoped lock specialization for channels.
Definition: lock.h:619
General Asterisk PBX channel definitions.
static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
Internal built in feature for hangup.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
Channel Bridging API.
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:2969
Asterisk internal frame definitions.
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
Core PBX routines and definitions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
Write a bridge action play file frame into the bridge.
int ast_stop_mixmonitor(struct ast_channel *chan, const char *mixmon_id)
Stop a mixmonitor on a channel with the given parameters.
Definition: mixmonitor.c:86
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
Definition: module.h:478
int ast_bridge_features_register(enum ast_bridge_builtin_feature feature, ast_bridge_hook_callback callback, const char *dtmf)
Register a handler for a built in feature.
Definition: bridge.c:3062
void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause)
Set bridge channel state to leave bridge (if not leaving already).
#define ast_bridge_unlock(bridge)
Unlock the bridge.
Definition: bridge.h:481
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
struct ast_channel * chan
Structure that contains information regarding a channel in a bridge.
void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
Lock the bridge associated with the bridge channel.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
Queue a bridge action play file frame onto the bridge channel.
Internal Asterisk hangup causes.
loadable MixMonitor functionality
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Bridging API.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
enum ast_bridge_features_monitor start_stop
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:297
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:342