#include "asterisk.h"
#include <ctype.h>
#include <errno.h>
#include "asterisk/paths.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/audiohook.h"
#include "asterisk/features.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/say.h"
#include "asterisk/pbx.h"
#include "asterisk/translate.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/options.h"
Go to the source code of this file.
Data Structures | |
struct | chanspy_ds |
struct | chanspy_translation_helper |
Defines | |
#define | AST_NAME_STRLEN 256 |
Enumerations | |
enum | { OPTION_QUIET = (1 << 0), OPTION_BRIDGED = (1 << 1), OPTION_VOLUME = (1 << 2), OPTION_GROUP = (1 << 3), OPTION_RECORD = (1 << 4), OPTION_WHISPER = (1 << 5), OPTION_PRIVATE = (1 << 6), OPTION_READONLY = (1 << 7), OPTION_EXIT = (1 << 8), OPTION_ENFORCED = (1 << 9) } |
enum | { OPT_ARG_VOLUME = 0, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_ENFORCED, OPT_ARG_ARRAY_SIZE } |
Functions | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static int | channel_spy (struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, int *volfactor, int fd, const struct ast_flags *flags, char *exitcontext) |
static void | chanspy_ds_chan_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) |
static void | chanspy_ds_destroy (void *data) |
static struct chanspy_ds * | chanspy_ds_free (struct chanspy_ds *chanspy_ds) |
static int | chanspy_exec (struct ast_channel *chan, void *data) |
static int | common_exec (struct ast_channel *chan, const struct ast_flags *flags, int volfactor, const int fd, const char *mygroup, const char *myenforced, const char *spec, const char *exten, const char *context) |
static int | extenspy_exec (struct ast_channel *chan, void *data) |
static int | load_module (void) |
static struct chanspy_ds * | next_channel (struct ast_channel *chan, const struct ast_channel *last, const char *spec, const char *exten, const char *context, struct chanspy_ds *chanspy_ds) |
static struct chanspy_ds * | setup_chanspy_ds (struct ast_channel *chan, struct chanspy_ds *chanspy_ds) |
static void * | spy_alloc (struct ast_channel *chan, void *data) |
static int | spy_generate (struct ast_channel *chan, void *data, int len, int samples) |
static void | spy_release (struct ast_channel *chan, void *data) |
static int | start_spying (struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook) |
static int | unload_module (void) |
Variables | |
static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Listen to the audio of an active channel" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, } |
static const char * | app_chan = "ChanSpy" |
static const char * | app_ext = "ExtenSpy" |
static const struct ast_module_info * | ast_module_info = &__mod_info |
static struct ast_datastore_info | chanspy_ds_info |
enum { ... } | chanspy_opt_args |
enum { ... } | chanspy_opt_flags |
static const char * | desc_chan |
static const char * | desc_ext |
static int | next_unique_id_to_use = 0 |
static struct ast_app_option | spy_opts [128] = { [ 'q' ] = { .flag = OPTION_QUIET }, [ 'b' ] = { .flag = OPTION_BRIDGED }, [ 'w' ] = { .flag = OPTION_WHISPER }, [ 'W' ] = { .flag = OPTION_PRIVATE }, [ 'v' ] = { .flag = OPTION_VOLUME , .arg_index = OPT_ARG_VOLUME + 1 }, [ 'g' ] = { .flag = OPTION_GROUP , .arg_index = OPT_ARG_GROUP + 1 }, [ 'r' ] = { .flag = OPTION_RECORD , .arg_index = OPT_ARG_RECORD + 1 }, [ 'e' ] = { .flag = OPTION_ENFORCED , .arg_index = OPT_ARG_ENFORCED + 1 }, [ 'o' ] = { .flag = OPTION_READONLY }, [ 'X' ] = { .flag = OPTION_EXIT },} |
static struct ast_generator | spygen |
static const char * | tdesc = "Listen to a channel, and optionally whisper into it" |
Joshua Colp <jcolp@digium.com>
Russell Bryant <russell@digium.com>
Definition in file app_chanspy.c.
#define AST_NAME_STRLEN 256 |
anonymous enum |
OPTION_QUIET | |
OPTION_BRIDGED | |
OPTION_VOLUME | |
OPTION_GROUP | |
OPTION_RECORD | |
OPTION_WHISPER | |
OPTION_PRIVATE | |
OPTION_READONLY | |
OPTION_EXIT | |
OPTION_ENFORCED |
Definition at line 137 of file app_chanspy.c.
00137 { 00138 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */ 00139 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */ 00140 OPTION_VOLUME = (1 << 2), /* Specify initial volume */ 00141 OPTION_GROUP = (1 << 3), /* Only look at channels in group */ 00142 OPTION_RECORD = (1 << 4), 00143 OPTION_WHISPER = (1 << 5), 00144 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */ 00145 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */ 00146 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */ 00147 OPTION_ENFORCED = (1 << 9), /* Enforced mode */ 00148 } chanspy_opt_flags;
anonymous enum |
Definition at line 150 of file app_chanspy.c.
00150 { 00151 OPT_ARG_VOLUME = 0, 00152 OPT_ARG_GROUP, 00153 OPT_ARG_RECORD, 00154 OPT_ARG_ENFORCED, 00155 OPT_ARG_ARRAY_SIZE, 00156 } chanspy_opt_args;
static void __reg_module | ( | void | ) | [static] |
Definition at line 954 of file app_chanspy.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 954 of file app_chanspy.c.
static int channel_spy | ( | struct ast_channel * | chan, | |
struct chanspy_ds * | spyee_chanspy_ds, | |||
int * | volfactor, | |||
int | fd, | |||
const struct ast_flags * | flags, | |||
char * | exitcontext | |||
) | [static] |
Definition at line 257 of file app_chanspy.c.
References ast_activate_generator(), ast_audiohook_destroy(), ast_audiohook_detach(), AST_AUDIOHOOK_DIRECTION_WRITE, ast_audiohook_init(), ast_audiohook_lock, AST_AUDIOHOOK_STATUS_RUNNING, AST_AUDIOHOOK_TYPE_SPY, AST_AUDIOHOOK_TYPE_WHISPER, ast_audiohook_unlock, ast_audiohook_write_frame(), ast_channel_lock, ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_channel_unlock, ast_check_hangup(), ast_deactivate_generator(), ast_debug, AST_FRAME_DTMF, AST_FRAME_VOICE, ast_frfree, ast_goto_if_exists(), ast_mutex_lock(), ast_mutex_unlock(), ast_read(), ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_verb, ast_waitfor(), chanspy_ds::chan, chan, f, ast_channel::flags, chanspy_ds::lock, ast_channel::name, name, OPTION_EXIT, OPTION_PRIVATE, OPTION_WHISPER, pbx_builtin_setvar_helper(), spygen, and start_spying().
Referenced by common_exec().
00259 { 00260 struct chanspy_translation_helper csth; 00261 int running = 0, res, x = 0; 00262 char inp[24] = {0}; 00263 char *name; 00264 struct ast_frame *f; 00265 struct ast_silence_generator *silgen = NULL; 00266 struct ast_channel *spyee = NULL; 00267 const char *spyer_name; 00268 00269 ast_channel_lock(chan); 00270 spyer_name = ast_strdupa(chan->name); 00271 ast_channel_unlock(chan); 00272 00273 ast_mutex_lock(&spyee_chanspy_ds->lock); 00274 if (spyee_chanspy_ds->chan) { 00275 spyee = spyee_chanspy_ds->chan; 00276 ast_channel_lock(spyee); 00277 } 00278 ast_mutex_unlock(&spyee_chanspy_ds->lock); 00279 00280 if (!spyee) 00281 return 0; 00282 00283 /* We now hold the channel lock on spyee */ 00284 00285 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) { 00286 ast_channel_unlock(spyee); 00287 return 0; 00288 } 00289 00290 name = ast_strdupa(spyee->name); 00291 ast_verb(2, "Spying on channel %s\n", name); 00292 00293 memset(&csth, 0, sizeof(csth)); 00294 00295 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy"); 00296 00297 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) { 00298 ast_audiohook_destroy(&csth.spy_audiohook); 00299 ast_channel_unlock(spyee); 00300 return 0; 00301 } 00302 00303 if (ast_test_flag(flags, OPTION_WHISPER)) { 00304 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy"); 00305 start_spying(spyee, spyer_name, &csth.whisper_audiohook); 00306 } 00307 00308 ast_channel_unlock(spyee); 00309 spyee = NULL; 00310 00311 csth.volfactor = *volfactor; 00312 00313 if (csth.volfactor) { 00314 csth.spy_audiohook.options.read_volume = csth.volfactor; 00315 csth.spy_audiohook.options.write_volume = csth.volfactor; 00316 } 00317 00318 csth.fd = fd; 00319 00320 if (ast_test_flag(flags, OPTION_PRIVATE)) 00321 silgen = ast_channel_start_silence_generator(chan); 00322 else 00323 ast_activate_generator(chan, &spygen, &csth); 00324 00325 /* We can no longer rely on 'spyee' being an actual channel; 00326 it can be hung up and freed out from under us. However, the 00327 channel destructor will put NULL into our csth.spy.chan 00328 field when that happens, so that is our signal that the spyee 00329 channel has gone away. 00330 */ 00331 00332 /* Note: it is very important that the ast_waitfor() be the first 00333 condition in this expression, so that if we wait for some period 00334 of time before receiving a frame from our spying channel, we check 00335 for hangup on the spied-on channel _after_ knowing that a frame 00336 has arrived, since the spied-on channel could have gone away while 00337 we were waiting 00338 */ 00339 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { 00340 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) { 00341 running = -1; 00342 break; 00343 } 00344 00345 if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) { 00346 ast_audiohook_lock(&csth.whisper_audiohook); 00347 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); 00348 ast_audiohook_unlock(&csth.whisper_audiohook); 00349 ast_frfree(f); 00350 continue; 00351 } 00352 00353 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0; 00354 ast_frfree(f); 00355 if (!res) 00356 continue; 00357 00358 if (x == sizeof(inp)) 00359 x = 0; 00360 00361 if (res < 0) { 00362 running = -1; 00363 break; 00364 } 00365 00366 if (ast_test_flag(flags, OPTION_EXIT)) { 00367 char tmp[2]; 00368 tmp[0] = res; 00369 tmp[1] = '\0'; 00370 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) { 00371 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext); 00372 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name); 00373 running = -2; 00374 break; 00375 } else { 00376 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); 00377 } 00378 } else if (res >= '0' && res <= '9') { 00379 inp[x++] = res; 00380 } 00381 00382 if (res == '*') { 00383 running = 0; 00384 break; 00385 } else if (res == '#') { 00386 if (!ast_strlen_zero(inp)) { 00387 running = atoi(inp); 00388 break; 00389 } 00390 00391 (*volfactor)++; 00392 if (*volfactor > 4) 00393 *volfactor = -4; 00394 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor); 00395 00396 csth.volfactor = *volfactor; 00397 csth.spy_audiohook.options.read_volume = csth.volfactor; 00398 csth.spy_audiohook.options.write_volume = csth.volfactor; 00399 } 00400 } 00401 00402 if (ast_test_flag(flags, OPTION_PRIVATE)) 00403 ast_channel_stop_silence_generator(chan, silgen); 00404 else 00405 ast_deactivate_generator(chan); 00406 00407 if (ast_test_flag(flags, OPTION_WHISPER)) { 00408 ast_audiohook_lock(&csth.whisper_audiohook); 00409 ast_audiohook_detach(&csth.whisper_audiohook); 00410 ast_audiohook_unlock(&csth.whisper_audiohook); 00411 ast_audiohook_destroy(&csth.whisper_audiohook); 00412 } 00413 00414 ast_audiohook_lock(&csth.spy_audiohook); 00415 ast_audiohook_detach(&csth.spy_audiohook); 00416 ast_audiohook_unlock(&csth.spy_audiohook); 00417 ast_audiohook_destroy(&csth.spy_audiohook); 00418 00419 ast_verb(2, "Done Spying on channel %s\n", name); 00420 00421 return running; 00422 }
static void chanspy_ds_chan_fixup | ( | void * | data, | |
struct ast_channel * | old_chan, | |||
struct ast_channel * | new_chan | |||
) | [static] |
Definition at line 441 of file app_chanspy.c.
References ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, and chanspy_ds::lock.
00442 { 00443 struct chanspy_ds *chanspy_ds = data; 00444 00445 ast_mutex_lock(&chanspy_ds->lock); 00446 chanspy_ds->chan = new_chan; 00447 ast_mutex_unlock(&chanspy_ds->lock); 00448 }
static void chanspy_ds_destroy | ( | void * | data | ) | [static] |
Definition at line 428 of file app_chanspy.c.
References ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, and chanspy_ds::lock.
Referenced by chanspy_ds_free().
00429 { 00430 struct chanspy_ds *chanspy_ds = data; 00431 00432 /* Setting chan to be NULL is an atomic operation, but we don't want this 00433 * value to change while this lock is held. The lock is held elsewhere 00434 * while it performs non-atomic operations with this channel pointer */ 00435 00436 ast_mutex_lock(&chanspy_ds->lock); 00437 chanspy_ds->chan = NULL; 00438 ast_mutex_unlock(&chanspy_ds->lock); 00439 }
static struct chanspy_ds* chanspy_ds_free | ( | struct chanspy_ds * | chanspy_ds | ) | [static] |
Definition at line 456 of file app_chanspy.c.
References ast_channel_datastore_find(), ast_channel_datastore_free(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_unlock, ast_mutex_lock(), ast_mutex_unlock(), chan, chanspy_ds::chan, chanspy_ds_destroy(), chanspy_ds_info, ast_datastore::data, chanspy_ds::lock, and chanspy_ds::unique_id.
Referenced by common_exec(), and setup_chanspy_ds().
00457 { 00458 if (!chanspy_ds) 00459 return NULL; 00460 00461 ast_mutex_lock(&chanspy_ds->lock); 00462 if (chanspy_ds->chan) { 00463 struct ast_datastore *datastore; 00464 struct ast_channel *chan; 00465 00466 chan = chanspy_ds->chan; 00467 00468 ast_channel_lock(chan); 00469 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) { 00470 ast_channel_datastore_remove(chan, datastore); 00471 /* chanspy_ds->chan is NULL after this call */ 00472 chanspy_ds_destroy(datastore->data); 00473 datastore->data = NULL; 00474 ast_channel_datastore_free(datastore); 00475 } 00476 ast_channel_unlock(chan); 00477 } 00478 ast_mutex_unlock(&chanspy_ds->lock); 00479 00480 return NULL; 00481 }
static int chanspy_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 778 of file app_chanspy.c.
References AST_APP_ARG, ast_app_parse_options(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_DECLARE_APP_ARGS, AST_FILE_MODE, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_set_flag, ast_set_write_format(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_test_flag, chan, common_exec(), ast_flags::flags, LOG_ERROR, LOG_NOTICE, LOG_WARNING, OPT_ARG_ARRAY_SIZE, OPT_ARG_ENFORCED, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_VOLUME, OPTION_ENFORCED, OPTION_GROUP, OPTION_PRIVATE, OPTION_RECORD, OPTION_VOLUME, OPTION_WHISPER, spy_opts, and ast_channel::writeformat.
Referenced by load_module().
00779 { 00780 char *myenforced = NULL; 00781 char *mygroup = NULL; 00782 char *recbase = NULL; 00783 int fd = 0; 00784 struct ast_flags flags; 00785 int oldwf = 0; 00786 int volfactor = 0; 00787 int res; 00788 AST_DECLARE_APP_ARGS(args, 00789 AST_APP_ARG(spec); 00790 AST_APP_ARG(options); 00791 ); 00792 char *opts[OPT_ARG_ARRAY_SIZE]; 00793 00794 data = ast_strdupa(data); 00795 AST_STANDARD_APP_ARGS(args, data); 00796 00797 if (args.spec && !strcmp(args.spec, "all")) 00798 args.spec = NULL; 00799 00800 if (args.options) { 00801 ast_app_parse_options(spy_opts, &flags, opts, args.options); 00802 if (ast_test_flag(&flags, OPTION_GROUP)) 00803 mygroup = opts[OPT_ARG_GROUP]; 00804 00805 if (ast_test_flag(&flags, OPTION_RECORD) && 00806 !(recbase = opts[OPT_ARG_RECORD])) 00807 recbase = "chanspy"; 00808 00809 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { 00810 int vol; 00811 00812 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4)) 00813 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); 00814 else 00815 volfactor = vol; 00816 } 00817 00818 if (ast_test_flag(&flags, OPTION_PRIVATE)) 00819 ast_set_flag(&flags, OPTION_WHISPER); 00820 00821 if (ast_test_flag(&flags, OPTION_ENFORCED)) 00822 myenforced = opts[OPT_ARG_ENFORCED]; 00823 00824 } else 00825 ast_clear_flag(&flags, AST_FLAGS_ALL); 00826 00827 oldwf = chan->writeformat; 00828 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { 00829 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00830 return -1; 00831 } 00832 00833 if (recbase) { 00834 char filename[PATH_MAX]; 00835 00836 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); 00837 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) { 00838 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); 00839 fd = 0; 00840 } 00841 } 00842 00843 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL); 00844 00845 if (fd) 00846 close(fd); 00847 00848 if (oldwf && ast_set_write_format(chan, oldwf) < 0) 00849 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00850 00851 return res; 00852 }
static int common_exec | ( | struct ast_channel * | chan, | |
const struct ast_flags * | flags, | |||
int | volfactor, | |||
const int | fd, | |||
const char * | mygroup, | |||
const char * | myenforced, | |||
const char * | spec, | |||
const char * | exten, | |||
const char * | context | |||
) | [static] |
Definition at line 536 of file app_chanspy.c.
References ast_channel::_state, ARRAY_LEN, ast_answer(), ast_app_separate_args(), ast_atomic_fetchadd_int(), ast_bridged_channel(), ast_channel_lock, AST_CHANNEL_NAME, ast_channel_setoption(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_copy_string(), ast_debug, ast_fileexists(), AST_FLAG_SPYING, ast_get_channel_by_name_prefix_locked(), ast_goto_if_exists(), AST_MAX_CONTEXT, ast_mutex_destroy(), ast_mutex_init(), ast_mutex_lock(), ast_mutex_unlock(), AST_NAME_STRLEN, AST_OPTION_TXGAIN, ast_say_character_str(), ast_say_digits(), ast_set_flag, AST_STATE_UP, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitfordigit(), ast_waitstream(), chan, channel_spy(), chanspy_ds_free(), ast_channel::context, exitcontext, ext, ast_channel::flags, chanspy_ds::lock, ast_channel::macrocontext, ast_channel::next, next_channel(), num, OPTION_BRIDGED, OPTION_EXIT, OPTION_QUIET, pbx_builtin_getvar_helper(), s, setup_chanspy_ds(), strcasestr(), and chanspy_ds::unique_id.
Referenced by chanspy_exec(), and extenspy_exec().
00539 { 00540 char nameprefix[AST_NAME_STRLEN]; 00541 char peer_name[AST_NAME_STRLEN + 5]; 00542 char exitcontext[AST_MAX_CONTEXT] = ""; 00543 signed char zero_volume = 0; 00544 int waitms; 00545 int res; 00546 char *ptr; 00547 int num; 00548 int num_spyed_upon = 1; 00549 struct chanspy_ds chanspy_ds = { 0, }; 00550 00551 if (ast_test_flag(flags, OPTION_EXIT)) { 00552 const char *c; 00553 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) 00554 ast_copy_string(exitcontext, c, sizeof(exitcontext)); 00555 else if (!ast_strlen_zero(chan->macrocontext)) 00556 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext)); 00557 else 00558 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext)); 00559 } 00560 00561 ast_mutex_init(&chanspy_ds.lock); 00562 00563 snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1)); 00564 00565 if (chan->_state != AST_STATE_UP) 00566 ast_answer(chan); 00567 00568 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ 00569 00570 waitms = 100; 00571 00572 for (;;) { 00573 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL; 00574 struct ast_channel *prev = NULL, *peer = NULL; 00575 00576 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { 00577 res = ast_streamfile(chan, "beep", chan->language); 00578 if (!res) 00579 res = ast_waitstream(chan, ""); 00580 else if (res < 0) { 00581 ast_clear_flag(chan, AST_FLAG_SPYING); 00582 break; 00583 } 00584 if (!ast_strlen_zero(exitcontext)) { 00585 char tmp[2]; 00586 tmp[0] = res; 00587 tmp[1] = '\0'; 00588 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) 00589 goto exit; 00590 else 00591 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); 00592 } 00593 } 00594 00595 res = ast_waitfordigit(chan, waitms); 00596 if (res < 0) { 00597 ast_clear_flag(chan, AST_FLAG_SPYING); 00598 break; 00599 } 00600 if (!ast_strlen_zero(exitcontext)) { 00601 char tmp[2]; 00602 tmp[0] = res; 00603 tmp[1] = '\0'; 00604 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) 00605 goto exit; 00606 else 00607 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); 00608 } 00609 00610 /* reset for the next loop around, unless overridden later */ 00611 waitms = 100; 00612 num_spyed_upon = 0; 00613 00614 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds); 00615 peer_chanspy_ds; 00616 chanspy_ds_free(peer_chanspy_ds), prev = peer, 00617 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 00618 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) { 00619 int igrp = !mygroup; 00620 int ienf = !myenforced; 00621 char *s; 00622 00623 peer = peer_chanspy_ds->chan; 00624 00625 ast_mutex_unlock(&peer_chanspy_ds->lock); 00626 00627 if (peer == prev) { 00628 ast_channel_unlock(peer); 00629 chanspy_ds_free(peer_chanspy_ds); 00630 break; 00631 } 00632 00633 if (ast_check_hangup(chan)) { 00634 ast_channel_unlock(peer); 00635 chanspy_ds_free(peer_chanspy_ds); 00636 break; 00637 } 00638 00639 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) { 00640 ast_channel_unlock(peer); 00641 continue; 00642 } 00643 00644 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) { 00645 ast_channel_unlock(peer); 00646 continue; 00647 } 00648 00649 if (mygroup) { 00650 int num_groups = 0; 00651 char dup_group[512]; 00652 char *groups[25]; 00653 const char *group; 00654 int x; 00655 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) { 00656 ast_copy_string(dup_group, group, sizeof(dup_group)); 00657 num_groups = ast_app_separate_args(dup_group, ':', groups, 00658 ARRAY_LEN(groups)); 00659 } 00660 00661 for (x = 0; x < num_groups; x++) { 00662 if (!strcmp(mygroup, groups[x])) { 00663 igrp = 1; 00664 break; 00665 } 00666 } 00667 } 00668 00669 if (!igrp) { 00670 ast_channel_unlock(peer); 00671 continue; 00672 } 00673 00674 if (myenforced) { 00675 char ext[AST_CHANNEL_NAME + 3]; 00676 char buffer[512]; 00677 char *end; 00678 00679 snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced); 00680 00681 ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1); 00682 if ((end = strchr(ext, '-'))) { 00683 *end++ = ':'; 00684 *end = '\0'; 00685 } 00686 00687 ext[0] = ':'; 00688 00689 if (strcasestr(buffer, ext)) { 00690 ienf = 1; 00691 } 00692 } 00693 00694 if (!ienf) { 00695 continue; 00696 } 00697 00698 strcpy(peer_name, "spy-"); 00699 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1); 00700 ptr = strchr(peer_name, '/'); 00701 *ptr++ = '\0'; 00702 00703 for (s = peer_name; s < ptr; s++) 00704 *s = tolower(*s); 00705 /* We have to unlock the peer channel here to avoid a deadlock. 00706 * So, when we need to dereference it again, we have to lock the 00707 * datastore and get the pointer from there to see if the channel 00708 * is still valid. */ 00709 ast_channel_unlock(peer); 00710 00711 if (!ast_test_flag(flags, OPTION_QUIET)) { 00712 if (ast_fileexists(peer_name, NULL, NULL) != -1) { 00713 res = ast_streamfile(chan, peer_name, chan->language); 00714 if (!res) 00715 res = ast_waitstream(chan, ""); 00716 if (res) { 00717 chanspy_ds_free(peer_chanspy_ds); 00718 break; 00719 } 00720 } else 00721 res = ast_say_character_str(chan, peer_name, "", chan->language); 00722 if ((num = atoi(ptr))) 00723 ast_say_digits(chan, atoi(ptr), "", chan->language); 00724 } 00725 00726 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext); 00727 num_spyed_upon++; 00728 00729 if (res == -1) { 00730 chanspy_ds_free(peer_chanspy_ds); 00731 goto exit; 00732 } else if (res == -2) { 00733 res = 0; 00734 chanspy_ds_free(peer_chanspy_ds); 00735 goto exit; 00736 } else if (res > 1 && spec) { 00737 struct ast_channel *next; 00738 00739 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); 00740 00741 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) { 00742 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds); 00743 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds); 00744 } else { 00745 /* stay on this channel, if it is still valid */ 00746 00747 ast_mutex_lock(&peer_chanspy_ds->lock); 00748 if (peer_chanspy_ds->chan) { 00749 ast_channel_lock(peer_chanspy_ds->chan); 00750 next_chanspy_ds = peer_chanspy_ds; 00751 peer_chanspy_ds = NULL; 00752 } else { 00753 /* the channel is gone */ 00754 ast_mutex_unlock(&peer_chanspy_ds->lock); 00755 next_chanspy_ds = NULL; 00756 } 00757 } 00758 00759 peer = NULL; 00760 } 00761 } 00762 if (res == -1 || ast_check_hangup(chan)) 00763 break; 00764 } 00765 exit: 00766 00767 ast_clear_flag(chan, AST_FLAG_SPYING); 00768 00769 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); 00770 00771 ast_mutex_lock(&chanspy_ds.lock); 00772 ast_mutex_unlock(&chanspy_ds.lock); 00773 ast_mutex_destroy(&chanspy_ds.lock); 00774 00775 return res; 00776 }
static int extenspy_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 854 of file app_chanspy.c.
References AST_APP_ARG, ast_app_parse_options(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_DECLARE_APP_ARGS, AST_FILE_MODE, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_set_flag, ast_set_write_format(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_test_flag, chan, common_exec(), ast_channel::context, context, exten, ast_flags::flags, LOG_ERROR, LOG_NOTICE, LOG_WARNING, OPT_ARG_ARRAY_SIZE, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_VOLUME, OPTION_GROUP, OPTION_PRIVATE, OPTION_RECORD, OPTION_VOLUME, OPTION_WHISPER, spy_opts, and ast_channel::writeformat.
Referenced by load_module().
00855 { 00856 char *ptr, *exten = NULL; 00857 char *mygroup = NULL; 00858 char *recbase = NULL; 00859 int fd = 0; 00860 struct ast_flags flags; 00861 int oldwf = 0; 00862 int volfactor = 0; 00863 int res; 00864 AST_DECLARE_APP_ARGS(args, 00865 AST_APP_ARG(context); 00866 AST_APP_ARG(options); 00867 ); 00868 00869 data = ast_strdupa(data); 00870 00871 AST_STANDARD_APP_ARGS(args, data); 00872 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) { 00873 exten = args.context; 00874 *ptr++ = '\0'; 00875 args.context = ptr; 00876 } 00877 00878 if (ast_strlen_zero(args.context)) 00879 args.context = ast_strdupa(chan->context); 00880 00881 if (args.options) { 00882 char *opts[OPT_ARG_ARRAY_SIZE]; 00883 00884 ast_app_parse_options(spy_opts, &flags, opts, args.options); 00885 if (ast_test_flag(&flags, OPTION_GROUP)) 00886 mygroup = opts[OPT_ARG_GROUP]; 00887 00888 if (ast_test_flag(&flags, OPTION_RECORD) && 00889 !(recbase = opts[OPT_ARG_RECORD])) 00890 recbase = "chanspy"; 00891 00892 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { 00893 int vol; 00894 00895 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4)) 00896 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); 00897 else 00898 volfactor = vol; 00899 } 00900 00901 if (ast_test_flag(&flags, OPTION_PRIVATE)) 00902 ast_set_flag(&flags, OPTION_WHISPER); 00903 } else 00904 ast_clear_flag(&flags, AST_FLAGS_ALL); 00905 00906 oldwf = chan->writeformat; 00907 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { 00908 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00909 return -1; 00910 } 00911 00912 if (recbase) { 00913 char filename[PATH_MAX]; 00914 00915 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); 00916 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) { 00917 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); 00918 fd = 0; 00919 } 00920 } 00921 00922 00923 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context); 00924 00925 if (fd) 00926 close(fd); 00927 00928 if (oldwf && ast_set_write_format(chan, oldwf) < 0) 00929 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00930 00931 return res; 00932 }
static int load_module | ( | void | ) | [static] |
Definition at line 944 of file app_chanspy.c.
References ast_register_application, chanspy_exec(), and extenspy_exec().
00945 { 00946 int res = 0; 00947 00948 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan); 00949 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext); 00950 00951 return res; 00952 }
static struct chanspy_ds* next_channel | ( | struct ast_channel * | chan, | |
const struct ast_channel * | last, | |||
const char * | spec, | |||
const char * | exten, | |||
const char * | context, | |||
struct chanspy_ds * | chanspy_ds | |||
) | [static] |
Definition at line 504 of file app_chanspy.c.
References ast_channel_unlock, ast_channel_walk_locked(), ast_strlen_zero(), ast_walk_channel_by_exten_locked(), ast_walk_channel_by_name_prefix_locked(), chan, last, ast_channel::name, ast_channel::next, and setup_chanspy_ds().
Referenced by common_exec().
00507 { 00508 struct ast_channel *next; 00509 const size_t pseudo_len = strlen("DAHDI/pseudo"); 00510 00511 redo: 00512 if (!ast_strlen_zero(spec)) 00513 next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec)); 00514 00515 else if (!ast_strlen_zero(exten)) 00516 next = ast_walk_channel_by_exten_locked(last, exten, context); 00517 else 00518 next = ast_channel_walk_locked(last); 00519 00520 if (!next) 00521 return NULL; 00522 00523 if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) { 00524 last = next; 00525 ast_channel_unlock(next); 00526 goto redo; 00527 } else if (next == chan) { 00528 last = next; 00529 ast_channel_unlock(next); 00530 goto redo; 00531 } 00532 00533 return setup_chanspy_ds(next, chanspy_ds); 00534 }
static struct chanspy_ds* setup_chanspy_ds | ( | struct ast_channel * | chan, | |
struct chanspy_ds * | chanspy_ds | |||
) | [static] |
Definition at line 484 of file app_chanspy.c.
References ast_channel_datastore_add(), ast_channel_datastore_alloc(), ast_channel_unlock, ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, chan, chanspy_ds_free(), chanspy_ds_info, ast_datastore::data, chanspy_ds::lock, and chanspy_ds::unique_id.
Referenced by common_exec(), and next_channel().
00485 { 00486 struct ast_datastore *datastore = NULL; 00487 00488 ast_mutex_lock(&chanspy_ds->lock); 00489 00490 if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) { 00491 ast_mutex_unlock(&chanspy_ds->lock); 00492 chanspy_ds = chanspy_ds_free(chanspy_ds); 00493 ast_channel_unlock(chan); 00494 return NULL; 00495 } 00496 00497 chanspy_ds->chan = chan; 00498 datastore->data = chanspy_ds; 00499 ast_channel_datastore_add(chan, datastore); 00500 00501 return chanspy_ds; 00502 }
static void* spy_alloc | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 181 of file app_chanspy.c.
00182 { 00183 /* just store the data pointer in the channel structure */ 00184 return data; 00185 }
static int spy_generate | ( | struct ast_channel * | chan, | |
void * | data, | |||
int | len, | |||
int | samples | |||
) | [static] |
Definition at line 192 of file app_chanspy.c.
References AST_AUDIOHOOK_DIRECTION_BOTH, ast_audiohook_lock, ast_audiohook_read_frame(), AST_AUDIOHOOK_STATUS_RUNNING, ast_audiohook_unlock, AST_FORMAT_SLINEAR, ast_frfree, AST_LIST_NEXT, ast_log(), ast_write(), chan, ast_frame::data, ast_frame::datalen, errno, f, chanspy_translation_helper::fd, ast_frame::frame_list, LOG_WARNING, chanspy_translation_helper::spy_audiohook, and ast_audiohook::status.
00193 { 00194 struct chanspy_translation_helper *csth = data; 00195 struct ast_frame *f, *cur; 00196 00197 ast_audiohook_lock(&csth->spy_audiohook); 00198 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) { 00199 /* Channel is already gone more than likely */ 00200 ast_audiohook_unlock(&csth->spy_audiohook); 00201 return -1; 00202 } 00203 00204 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR); 00205 00206 ast_audiohook_unlock(&csth->spy_audiohook); 00207 00208 if (!f) 00209 return 0; 00210 00211 for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) { 00212 if (ast_write(chan, cur)) { 00213 ast_frfree(f); 00214 return -1; 00215 } 00216 00217 if (csth->fd) { 00218 if (write(csth->fd, cur->data, cur->datalen) < 0) { 00219 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); 00220 } 00221 } 00222 } 00223 00224 ast_frfree(f); 00225 00226 return 0; 00227 }
static void spy_release | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
static int start_spying | ( | struct ast_channel * | chan, | |
const char * | spychan_name, | |||
struct ast_audiohook * | audiohook | |||
) | [static] |
Definition at line 235 of file app_chanspy.c.
References ast_audiohook_attach(), AST_AUDIOHOOK_SMALL_QUEUE, AST_AUDIOHOOK_TRIGGER_SYNC, ast_bridged_channel(), AST_FLAG_NBRIDGE, ast_log(), ast_set_flag, ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, ast_test_flag, chan, LOG_NOTICE, and ast_channel::name.
Referenced by channel_spy().
00236 { 00237 int res = 0; 00238 struct ast_channel *peer = NULL; 00239 00240 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name); 00241 00242 ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE); 00243 res = ast_audiohook_attach(chan, audiohook); 00244 00245 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 00246 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); 00247 } 00248 return res; 00249 }
static int unload_module | ( | void | ) | [static] |
Definition at line 934 of file app_chanspy.c.
References ast_unregister_application().
00935 { 00936 int res = 0; 00937 00938 res |= ast_unregister_application(app_chan); 00939 res |= ast_unregister_application(app_ext); 00940 00941 return res; 00942 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Listen to the audio of an active channel" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, } [static] |
Definition at line 954 of file app_chanspy.c.
const char* app_chan = "ChanSpy" [static] |
Definition at line 56 of file app_chanspy.c.
const char* app_ext = "ExtenSpy" [static] |
Definition at line 100 of file app_chanspy.c.
const struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 954 of file app_chanspy.c.
struct ast_datastore_info chanspy_ds_info [static] |
Initial value:
{ .type = "chanspy", .destroy = chanspy_ds_destroy, .chan_fixup = chanspy_ds_chan_fixup, }
Definition at line 450 of file app_chanspy.c.
Referenced by chanspy_ds_free(), and setup_chanspy_ds().
enum { ... } chanspy_opt_args |
enum { ... } chanspy_opt_flags |
const char* desc_chan [static] |
Definition at line 57 of file app_chanspy.c.
const char* desc_ext [static] |
Definition at line 101 of file app_chanspy.c.
int next_unique_id_to_use = 0 [static] |
Definition at line 171 of file app_chanspy.c.
struct ast_app_option spy_opts[128] = { [ 'q' ] = { .flag = OPTION_QUIET }, [ 'b' ] = { .flag = OPTION_BRIDGED }, [ 'w' ] = { .flag = OPTION_WHISPER }, [ 'W' ] = { .flag = OPTION_PRIVATE }, [ 'v' ] = { .flag = OPTION_VOLUME , .arg_index = OPT_ARG_VOLUME + 1 }, [ 'g' ] = { .flag = OPTION_GROUP , .arg_index = OPT_ARG_GROUP + 1 }, [ 'r' ] = { .flag = OPTION_RECORD , .arg_index = OPT_ARG_RECORD + 1 }, [ 'e' ] = { .flag = OPTION_ENFORCED , .arg_index = OPT_ARG_ENFORCED + 1 }, [ 'o' ] = { .flag = OPTION_READONLY }, [ 'X' ] = { .flag = OPTION_EXIT },} [static] |
struct ast_generator spygen [static] |
Initial value:
{ .alloc = spy_alloc, .release = spy_release, .generate = spy_generate, }
Definition at line 229 of file app_chanspy.c.
Referenced by channel_spy().
const char* tdesc = "Listen to a channel, and optionally whisper into it" [static] |
Definition at line 55 of file app_chanspy.c.