Asterisk - The Open Source Telephony Project GIT-master-70eff7f
Loading...
Searching...
No Matches
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"
51#include "asterisk/mixmonitor.h"
52#include "asterisk/audiohook.h"
53#include "asterisk/causes.h"
54#include "asterisk/beep.h"
55
61
62static 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) {
74 } else {
75 *res = SET_TOUCH_SUCCESS;
76 }
77 }
78}
79
80static 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, char **touch_monitor_options)
81{
83 const char *var_format;
84 const char *var_monitor;
85 const char *var_prefix;
86 const char *var_beep;
87 const char *var_options;
88
90
91 var_format = "TOUCH_MIXMONITOR_FORMAT";
92 var_monitor = "TOUCH_MIXMONITOR";
93 var_prefix = "TOUCH_MIXMONITOR_PREFIX";
94 var_beep = "TOUCH_MIXMONITOR_BEEP";
95 var_options = "TOUCH_MIXMONITOR_OPTIONS";
96
97 set_touch_variable(&res, chan, var_format, touch_format);
98 set_touch_variable(&res, chan, var_monitor, touch_monitor);
99 set_touch_variable(&res, chan, var_prefix, touch_monitor_prefix);
100 set_touch_variable(&res, chan, var_beep, touch_monitor_beep);
101 set_touch_variable(&res, chan, var_options, touch_monitor_options);
102
103 return res;
104}
105
106
107static 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)
108{
109 ast_verb(4, "AutoMixMonitor used to stop recording call.\n");
110
111 if (ast_stop_mixmonitor(peer_chan, NULL)) {
112 ast_verb(4, "Failed to stop AutoMixMonitor for %s.\n", ast_channel_name(bridge_channel->chan));
113 if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
114 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
115 }
116 return;
117 }
118
119 if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
120 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
121 ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
122 }
123
124 if (!ast_strlen_zero(stop_message)) {
125 ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
126 ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL);
127 }
128}
129
130static 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)
131{
132 char *touch_filename, mix_options[32] = "b";
133 size_t len;
134 int x;
135 enum set_touch_variables_res set_touch_res;
136
137 RAII_VAR(char *, touch_format, NULL, ast_free);
138 RAII_VAR(char *, touch_monitor, NULL, ast_free);
139 RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);
140 RAII_VAR(char *, touch_monitor_beep, NULL, ast_free);
141 RAII_VAR(char *, touch_monitor_options, NULL, ast_free);
142
143 set_touch_res = set_touch_variables(bridge_channel->chan, &touch_format,
144 &touch_monitor, &touch_monitor_prefix, &touch_monitor_beep, &touch_monitor_options);
145 switch (set_touch_res) {
147 break;
148 case SET_TOUCH_UNSET:
149 set_touch_res = set_touch_variables(peer_chan, &touch_format, &touch_monitor,
150 &touch_monitor_prefix, &touch_monitor_beep, &touch_monitor_options);
151 if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
152 return;
153 }
154 break;
156 return;
157 }
158
159 if (!ast_strlen_zero(touch_monitor)) {
160 len = strlen(touch_monitor) + 50;
161 touch_filename = ast_alloca(len);
162 snprintf(touch_filename, len, "%s-%ld-%s.%s",
163 S_OR(touch_monitor_prefix, "auto"),
164 (long) time(NULL),
165 touch_monitor,
166 S_OR(touch_format, "wav"));
167 } else {
168 char *caller_chan_id;
169 char *peer_chan_id;
170
171 caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid,
172 ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan)));
173 peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid,
174 ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan)));
175 len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50;
176 touch_filename = ast_alloca(len);
177 snprintf(touch_filename, len, "%s-%ld-%s-%s.%s",
178 S_OR(touch_monitor_prefix, "auto"),
179 (long) time(NULL),
180 caller_chan_id,
181 peer_chan_id,
182 S_OR(touch_format, "wav"));
183 }
184
185 for (x = 0; x < strlen(touch_filename); x++) {
186 if (touch_filename[x] == '/') {
187 touch_filename[x] = '-';
188 }
189 }
190
191 ast_verb(4, "AutoMixMonitor used to record call. Filename: %s\n", touch_filename);
192
193 if (!ast_strlen_zero(touch_monitor_beep)) {
194 unsigned int interval = 15;
195 if (sscanf(touch_monitor_beep, "%30u", &interval) != 1) {
196 ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n",
197 touch_monitor_beep, interval);
198 }
199
200 if (interval < 5) {
201 interval = 5;
202 ast_log(LOG_WARNING, "Interval '%s' too small for periodic beep. Using minimum of %u\n",
203 touch_monitor_beep, interval);
204 }
205 snprintf(mix_options, sizeof(mix_options), "bB(%d)", interval);
206 }
207
208 if (!ast_strlen_zero(touch_monitor_options)) {
209 snprintf(mix_options, sizeof(mix_options), "%s", touch_monitor_options);
210 if (!ast_strlen_zero(touch_monitor_beep)) {
211 ast_log(LOG_WARNING, "Both TOUCH_MIXMONITOR_BEEP and TOUCH_MIXMONITOR_OPTIONS are set. Ignoring TOUCH_MIXMONITOR_BEEP.\n");
212 }
213 }
214
215 if (ast_start_mixmonitor(peer_chan, touch_filename, mix_options)) {
216 ast_verb(4, "AutoMixMonitor feature was tried by '%s' but MixMonitor failed to start.\n",
217 ast_channel_name(bridge_channel->chan));
218
219 if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
220 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
221 }
222 return;
223 }
224
225 if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
226 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
227 ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
228 }
229
230 if (!ast_strlen_zero(start_message)) {
231 ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
232 ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL);
233 }
234
235 pbx_builtin_setvar_helper(bridge_channel->chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
236 pbx_builtin_setvar_helper(peer_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
237}
238
239static int feature_automixmonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
240{
241 static const char *mixmonitor_spy_type = "MixMonitor";
242 const char *stop_message;
243 const char *start_message;
246 int is_monitoring;
247
248 RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup);
249 RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup);
250
251 ast_channel_lock(bridge_channel->chan);
252 features_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
253 ast_channel_unlock(bridge_channel->chan);
254 ast_bridge_channel_lock_bridge(bridge_channel);
255 peer_chan = ast_bridge_peer_nolock(bridge_channel->bridge, bridge_channel->chan);
256 ast_bridge_unlock(bridge_channel->bridge);
257
258 if (!peer_chan) {
259 ast_verb(4, "Cannot start AutoMixMonitor for %s - cannot determine peer in bridge.\n",
260 ast_channel_name(bridge_channel->chan));
261 if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
262 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
263 }
264 return 0;
265 }
266
267 ast_channel_lock(bridge_channel->chan);
268 start_message = pbx_builtin_getvar_helper(bridge_channel->chan,
269 "TOUCH_MIXMONITOR_MESSAGE_START");
270 start_message = ast_strdupa(S_OR(start_message, ""));
271 stop_message = pbx_builtin_getvar_helper(bridge_channel->chan,
272 "TOUCH_MIXMONITOR_MESSAGE_STOP");
273 stop_message = ast_strdupa(S_OR(stop_message, ""));
274 ast_channel_unlock(bridge_channel->chan);
275
276 is_monitoring =
278 switch (start_stop) {
280 if (is_monitoring) {
281 stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message);
282 } else {
283 start_automixmonitor(bridge_channel, peer_chan, features_cfg, start_message);
284 }
285 return 0;
287 if (!is_monitoring) {
288 start_automixmonitor(bridge_channel, peer_chan, features_cfg, start_message);
289 return 0;
290 }
291 ast_verb(4, "AutoMixMonitor already recording call.\n");
292 break;
294 if (is_monitoring) {
295 stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message);
296 return 0;
297 }
298 ast_verb(4, "AutoMixMonitor already stopped on call.\n");
299 break;
300 }
301
302 /*
303 * Fake start/stop to invoker so will think it did something but
304 * was already in that mode.
305 */
306 if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
307 ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
308 }
309 if (is_monitoring) {
310 if (!ast_strlen_zero(start_message)) {
311 ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
312 }
313 } else {
314 if (!ast_strlen_zero(stop_message)) {
315 ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
316 }
317 }
318 return 0;
319}
320
321/*! \brief Internal built in feature for hangup */
322static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
323{
324 /*
325 * This is very simple, we simply change the state on the
326 * bridge_channel to force the channel out of the bridge and the
327 * core takes care of the rest.
328 */
331 return 0;
332}
333
341
352
353AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Built in bridging features",
354 .support_level = AST_MODULE_SUPPORT_CORE,
355 .load = load_module,
356 .unload = unload_module,
static const char *const mixmonitor_spy_type
ast_mutex_t lock
Definition app_sla.c:337
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition astmm.h:288
#define ast_free(a)
Definition astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
#define ast_log
Definition astobj2.c:42
#define ao2_cleanup(obj)
Definition astobj2.h:1934
Audiohooks Architecture.
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:1392
@ AST_AUDIOHOOK_TYPE_SPY
Definition audiohook.h:36
Periodic beeps into the audio of a call.
Bridging API.
#define ast_bridge_unlock(bridge)
Unlock the bridge.
Definition bridge.h:491
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:4256
static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
Internal built in feature for hangup.
static void set_touch_variable(enum set_touch_variables_res *res, struct ast_channel *chan, const char *var_name, char **touch)
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)
static int feature_automixmonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
set_touch_variables_res
@ SET_TOUCH_SUCCESS
@ SET_TOUCH_ALLOC_FAILURE
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, char **touch_monitor_options)
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)
static int load_module(void)
static int unload_module(void)
void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
Lock the bridge associated with the bridge channel.
@ BRIDGE_CHANNEL_STATE_END
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.
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.
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).
int ast_bridge_features_unregister(enum ast_bridge_builtin_feature feature)
Unregister a handler for a built in feature.
Definition bridge.c:3287
@ AST_BRIDGE_BUILTIN_AUTOMIXMON
@ AST_BRIDGE_BUILTIN_HANGUP
ast_bridge_features_monitor
@ AUTO_MONITOR_START
@ AUTO_MONITOR_STOP
@ AUTO_MONITOR_TOGGLE
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:3271
Channel Bridging API.
Internal Asterisk hangup causes.
#define AST_CAUSE_NORMAL_CLEARING
Definition causes.h:106
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
#define ast_channel_lock(chan)
Definition channel.h:2983
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition channel.h:3030
#define ast_channel_unlock(chan)
Definition channel.h:2984
Generic File Format Support. Should be included by clients of the file handling routines....
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
struct ast_features_general_config * ast_get_chan_features_general_config(struct ast_channel *chan)
Get the general configuration options for a channel.
Asterisk internal frame definitions.
#define ast_verb(level,...)
#define LOG_WARNING
#define SCOPED_CHANNELLOCK(varname, chan)
scoped lock specialization for channels.
Definition lock.h:626
loadable MixMonitor functionality
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
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
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition module.h:329
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
Definition module.h:478
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODULE_SUPPORT_CORE
Definition module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
Call Parking API.
Core PBX routines and definitions.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
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.
#define NULL
Definition resample.c:96
#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
#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
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
Structure that contains information regarding a channel in a bridge.
struct ast_bridge * bridge
Bridge this channel is participating in.
struct ast_channel * chan
enum ast_bridge_features_monitor start_stop
Main Channel structure associated with a channel.
General features configuration items.
struct ast_module * self
Definition module.h:356
struct ast_party_id id
Caller party ID.
Definition channel.h:422
struct ast_party_number number
Subscriber phone number.
Definition channel.h:344
unsigned char valid
TRUE if the number information is valid/present.
Definition channel.h:299
char * str
Subscriber phone number (Malloced)
Definition channel.h:293
Number structure.
static struct test_options options
#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:981