#include "asterisk.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/audiohook.h"
#include "asterisk/features.h"
#include "asterisk/options.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"
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) } |
enum | { OPT_ARG_VOLUME = 0, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_ARRAY_SIZE } |
Functions | |
AST_APP_OPTIONS (spy_opts,{AST_APP_OPTION('q', OPTION_QUIET), AST_APP_OPTION('b', OPTION_BRIDGED), AST_APP_OPTION('w', OPTION_WHISPER), AST_APP_OPTION('W', OPTION_PRIVATE), AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME), AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP), AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),}) | |
AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY,"Listen to the audio of an active channel") | |
static int | channel_spy (struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, int *volfactor, int fd, const struct ast_flags *flags) |
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 *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 const char * | app_chan = "ChanSpy" |
static const char * | app_ext = "ExtenSpy" |
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_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 |
Definition at line 122 of file app_chanspy.c.
00122 { 00123 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */ 00124 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */ 00125 OPTION_VOLUME = (1 << 2), /* Specify initial volume */ 00126 OPTION_GROUP = (1 << 3), /* Only look at channels in group */ 00127 OPTION_RECORD = (1 << 4), 00128 OPTION_WHISPER = (1 << 5), 00129 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */ 00130 } chanspy_opt_flags;
anonymous enum |
Definition at line 132 of file app_chanspy.c.
00132 { 00133 OPT_ARG_VOLUME = 0, 00134 OPT_ARG_GROUP, 00135 OPT_ARG_RECORD, 00136 OPT_ARG_ARRAY_SIZE, 00137 } chanspy_opt_args;
AST_APP_OPTIONS | ( | spy_opts | ) |
AST_MODULE_INFO_STANDARD | ( | ASTERISK_GPL_KEY | , | |
"Listen to the audio of an active channel" | ||||
) |
static int channel_spy | ( | struct ast_channel * | chan, | |
struct chanspy_ds * | spyee_chanspy_ds, | |||
int * | volfactor, | |||
int | fd, | |||
const struct ast_flags * | flags | |||
) | [static] |
Definition at line 228 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_FRAME_DTMF, AST_FRAME_VOICE, ast_frfree, ast_mutex_lock(), ast_mutex_unlock(), ast_read(), ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_verbose(), ast_waitfor(), chanspy_ds::chan, f, ast_channel::flags, chanspy_ds::lock, name, OPTION_PRIVATE, option_verbose, OPTION_WHISPER, spygen, start_spying(), VERBOSE_PREFIX_2, and VERBOSE_PREFIX_3.
Referenced by common_exec().
00230 { 00231 struct chanspy_translation_helper csth; 00232 int running = 0, res, x = 0; 00233 char inp[24] = {0}; 00234 char *name; 00235 struct ast_frame *f; 00236 struct ast_silence_generator *silgen = NULL; 00237 struct ast_channel *spyee = NULL; 00238 const char *spyer_name; 00239 00240 ast_channel_lock(chan); 00241 spyer_name = ast_strdupa(chan->name); 00242 ast_channel_unlock(chan); 00243 00244 ast_mutex_lock(&spyee_chanspy_ds->lock); 00245 if (spyee_chanspy_ds->chan) { 00246 spyee = spyee_chanspy_ds->chan; 00247 ast_channel_lock(spyee); 00248 } 00249 ast_mutex_unlock(&spyee_chanspy_ds->lock); 00250 00251 if (!spyee) 00252 return 0; 00253 00254 /* We now hold the channel lock on spyee */ 00255 00256 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) { 00257 ast_channel_unlock(spyee); 00258 return 0; 00259 } 00260 00261 name = ast_strdupa(spyee->name); 00262 if (option_verbose >= 2) 00263 ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name); 00264 00265 memset(&csth, 0, sizeof(csth)); 00266 00267 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy"); 00268 00269 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) { 00270 ast_audiohook_destroy(&csth.spy_audiohook); 00271 ast_channel_unlock(spyee); 00272 return 0; 00273 } 00274 00275 if (ast_test_flag(flags, OPTION_WHISPER)) { 00276 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy"); 00277 start_spying(spyee, spyer_name, &csth.whisper_audiohook); 00278 } 00279 00280 ast_channel_unlock(spyee); 00281 spyee = NULL; 00282 00283 csth.volfactor = *volfactor; 00284 00285 if (csth.volfactor) { 00286 csth.spy_audiohook.options.read_volume = csth.volfactor; 00287 csth.spy_audiohook.options.write_volume = csth.volfactor; 00288 } 00289 00290 csth.fd = fd; 00291 00292 if (ast_test_flag(flags, OPTION_PRIVATE)) 00293 silgen = ast_channel_start_silence_generator(chan); 00294 else 00295 ast_activate_generator(chan, &spygen, &csth); 00296 00297 /* We can no longer rely on 'spyee' being an actual channel; 00298 it can be hung up and freed out from under us. However, the 00299 channel destructor will put NULL into our csth.spy.chan 00300 field when that happens, so that is our signal that the spyee 00301 channel has gone away. 00302 */ 00303 00304 /* Note: it is very important that the ast_waitfor() be the first 00305 condition in this expression, so that if we wait for some period 00306 of time before receiving a frame from our spying channel, we check 00307 for hangup on the spied-on channel _after_ knowing that a frame 00308 has arrived, since the spied-on channel could have gone away while 00309 we were waiting 00310 */ 00311 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { 00312 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) { 00313 running = -1; 00314 break; 00315 } 00316 00317 if (ast_test_flag(flags, OPTION_WHISPER) && (f->frametype == AST_FRAME_VOICE)) { 00318 ast_audiohook_lock(&csth.whisper_audiohook); 00319 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); 00320 ast_audiohook_unlock(&csth.whisper_audiohook); 00321 ast_frfree(f); 00322 continue; 00323 } 00324 00325 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0; 00326 ast_frfree(f); 00327 if (!res) 00328 continue; 00329 00330 if (x == sizeof(inp)) 00331 x = 0; 00332 00333 if (res < 0) { 00334 running = -1; 00335 break; 00336 } 00337 00338 if (res == '*') { 00339 running = 0; 00340 break; 00341 } else if (res == '#') { 00342 if (!ast_strlen_zero(inp)) { 00343 running = atoi(inp); 00344 break; 00345 } 00346 00347 (*volfactor)++; 00348 if (*volfactor > 4) 00349 *volfactor = -4; 00350 if (option_verbose > 2) 00351 ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor); 00352 csth.volfactor = *volfactor; 00353 csth.spy_audiohook.options.read_volume = csth.volfactor; 00354 csth.spy_audiohook.options.write_volume = csth.volfactor; 00355 } else if (res >= '0' && res <= '9') { 00356 inp[x++] = res; 00357 } 00358 } 00359 00360 if (ast_test_flag(flags, OPTION_PRIVATE)) 00361 ast_channel_stop_silence_generator(chan, silgen); 00362 else 00363 ast_deactivate_generator(chan); 00364 00365 if (ast_test_flag(flags, OPTION_WHISPER)) { 00366 ast_audiohook_lock(&csth.whisper_audiohook); 00367 ast_audiohook_detach(&csth.whisper_audiohook); 00368 ast_audiohook_unlock(&csth.whisper_audiohook); 00369 ast_audiohook_destroy(&csth.whisper_audiohook); 00370 } 00371 00372 ast_audiohook_lock(&csth.spy_audiohook); 00373 ast_audiohook_detach(&csth.spy_audiohook); 00374 ast_audiohook_unlock(&csth.spy_audiohook); 00375 ast_audiohook_destroy(&csth.spy_audiohook); 00376 00377 if (option_verbose >= 2) 00378 ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name); 00379 00380 return running; 00381 }
static void chanspy_ds_chan_fixup | ( | void * | data, | |
struct ast_channel * | old_chan, | |||
struct ast_channel * | new_chan | |||
) | [static] |
Definition at line 400 of file app_chanspy.c.
References ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, and chanspy_ds::lock.
00401 { 00402 struct chanspy_ds *chanspy_ds = data; 00403 00404 ast_mutex_lock(&chanspy_ds->lock); 00405 chanspy_ds->chan = new_chan; 00406 ast_mutex_unlock(&chanspy_ds->lock); 00407 }
static void chanspy_ds_destroy | ( | void * | data | ) | [static] |
Definition at line 387 of file app_chanspy.c.
References ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, and chanspy_ds::lock.
Referenced by chanspy_ds_free().
00388 { 00389 struct chanspy_ds *chanspy_ds = data; 00390 00391 /* Setting chan to be NULL is an atomic operation, but we don't want this 00392 * value to change while this lock is held. The lock is held elsewhere 00393 * while it performs non-atomic operations with this channel pointer */ 00394 00395 ast_mutex_lock(&chanspy_ds->lock); 00396 chanspy_ds->chan = NULL; 00397 ast_mutex_unlock(&chanspy_ds->lock); 00398 }
static struct chanspy_ds* chanspy_ds_free | ( | struct chanspy_ds * | chanspy_ds | ) | [static] |
Definition at line 415 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(), 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().
00416 { 00417 if (!chanspy_ds) 00418 return NULL; 00419 00420 ast_mutex_lock(&chanspy_ds->lock); 00421 if (chanspy_ds->chan) { 00422 struct ast_datastore *datastore; 00423 struct ast_channel *chan; 00424 00425 chan = chanspy_ds->chan; 00426 00427 ast_channel_lock(chan); 00428 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) { 00429 ast_channel_datastore_remove(chan, datastore); 00430 /* chanspy_ds->chan is NULL after this call */ 00431 chanspy_ds_destroy(datastore->data); 00432 datastore->data = NULL; 00433 ast_channel_datastore_free(datastore); 00434 } 00435 ast_channel_unlock(chan); 00436 } 00437 ast_mutex_unlock(&chanspy_ds->lock); 00438 00439 return NULL; 00440 }
static int chanspy_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 684 of file app_chanspy.c.
References ast_app_parse_options(), ast_app_separate_args(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_module_user_add, ast_module_user_remove, ast_set_flag, ast_set_write_format(), ast_strdupa, ast_strlen_zero(), ast_test_flag, common_exec(), 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, and ast_channel::writeformat.
Referenced by load_module().
00685 { 00686 struct ast_module_user *u; 00687 char *options = NULL; 00688 char *spec = NULL; 00689 char *argv[2]; 00690 char *mygroup = NULL; 00691 char *recbase = NULL; 00692 int fd = 0; 00693 struct ast_flags flags; 00694 int oldwf = 0; 00695 int argc = 0; 00696 int volfactor = 0; 00697 int res; 00698 00699 data = ast_strdupa(data); 00700 00701 u = ast_module_user_add(chan); 00702 00703 if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) { 00704 spec = argv[0]; 00705 if (argc > 1) 00706 options = argv[1]; 00707 00708 if (ast_strlen_zero(spec) || !strcmp(spec, "all")) 00709 spec = NULL; 00710 } 00711 00712 if (options) { 00713 char *opts[OPT_ARG_ARRAY_SIZE]; 00714 00715 ast_app_parse_options(spy_opts, &flags, opts, options); 00716 if (ast_test_flag(&flags, OPTION_GROUP)) 00717 mygroup = opts[OPT_ARG_GROUP]; 00718 00719 if (ast_test_flag(&flags, OPTION_RECORD) && 00720 !(recbase = opts[OPT_ARG_RECORD])) 00721 recbase = "chanspy"; 00722 00723 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { 00724 int vol; 00725 00726 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4)) 00727 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); 00728 else 00729 volfactor = vol; 00730 } 00731 00732 if (ast_test_flag(&flags, OPTION_PRIVATE)) 00733 ast_set_flag(&flags, OPTION_WHISPER); 00734 } else 00735 ast_clear_flag(&flags, AST_FLAGS_ALL); 00736 00737 oldwf = chan->writeformat; 00738 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { 00739 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00740 ast_module_user_remove(u); 00741 return -1; 00742 } 00743 00744 if (recbase) { 00745 char filename[PATH_MAX]; 00746 00747 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); 00748 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) { 00749 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); 00750 fd = 0; 00751 } 00752 } 00753 00754 res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL); 00755 00756 if (fd) 00757 close(fd); 00758 00759 if (oldwf && ast_set_write_format(chan, oldwf) < 0) 00760 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00761 00762 ast_module_user_remove(u); 00763 00764 return res; 00765 }
static int common_exec | ( | struct ast_channel * | chan, | |
const struct ast_flags * | flags, | |||
int | volfactor, | |||
const int | fd, | |||
const char * | mygroup, | |||
const char * | spec, | |||
const char * | exten, | |||
const char * | context | |||
) | [static] |
Definition at line 500 of file app_chanspy.c.
References ast_channel::_state, ast_answer(), ast_app_separate_args(), ast_bridged_channel(), ast_channel_lock, ast_channel_setoption(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_fileexists(), AST_FLAG_SPYING, ast_get_channel_by_name_prefix_locked(), 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_test_flag, ast_waitfordigit(), ast_waitstream(), chanspy_ds::chan, channel_spy(), chanspy_ds_free(), ast_channel::flags, group, chanspy_ds::lock, next_channel(), OPTION_BRIDGED, OPTION_QUIET, pbx_builtin_getvar_helper(), s, setup_chanspy_ds(), and chanspy_ds::unique_id.
Referenced by chanspy_exec(), and extenspy_exec().
00503 { 00504 char nameprefix[AST_NAME_STRLEN]; 00505 char peer_name[AST_NAME_STRLEN + 5]; 00506 signed char zero_volume = 0; 00507 int waitms; 00508 int res; 00509 char *ptr; 00510 int num; 00511 int num_spyed_upon = 1; 00512 struct chanspy_ds chanspy_ds; 00513 00514 ast_mutex_init(&chanspy_ds.lock); 00515 00516 snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1)); 00517 00518 if (chan->_state != AST_STATE_UP) 00519 ast_answer(chan); 00520 00521 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ 00522 00523 waitms = 100; 00524 00525 for (;;) { 00526 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL; 00527 struct ast_channel *prev = NULL, *peer = NULL; 00528 00529 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { 00530 res = ast_streamfile(chan, "beep", chan->language); 00531 if (!res) 00532 res = ast_waitstream(chan, ""); 00533 else if (res < 0) { 00534 ast_clear_flag(chan, AST_FLAG_SPYING); 00535 break; 00536 } 00537 } 00538 00539 res = ast_waitfordigit(chan, waitms); 00540 if (res < 0) { 00541 ast_clear_flag(chan, AST_FLAG_SPYING); 00542 break; 00543 } 00544 00545 /* reset for the next loop around, unless overridden later */ 00546 waitms = 100; 00547 num_spyed_upon = 0; 00548 00549 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds); 00550 peer_chanspy_ds; 00551 chanspy_ds_free(peer_chanspy_ds), prev = peer, 00552 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 00553 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) { 00554 const char *group; 00555 int igrp = !mygroup; 00556 char *groups[25]; 00557 int num_groups = 0; 00558 char dup_group[512]; 00559 int x; 00560 char *s; 00561 00562 peer = peer_chanspy_ds->chan; 00563 00564 ast_mutex_unlock(&peer_chanspy_ds->lock); 00565 00566 if (peer == prev) { 00567 ast_channel_unlock(peer); 00568 chanspy_ds_free(peer_chanspy_ds); 00569 break; 00570 } 00571 00572 if (ast_check_hangup(chan)) { 00573 ast_channel_unlock(peer); 00574 chanspy_ds_free(peer_chanspy_ds); 00575 break; 00576 } 00577 00578 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) { 00579 ast_channel_unlock(peer); 00580 continue; 00581 } 00582 00583 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) { 00584 ast_channel_unlock(peer); 00585 continue; 00586 } 00587 00588 if (mygroup) { 00589 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) { 00590 ast_copy_string(dup_group, group, sizeof(dup_group)); 00591 num_groups = ast_app_separate_args(dup_group, ':', groups, 00592 sizeof(groups) / sizeof(groups[0])); 00593 } 00594 00595 for (x = 0; x < num_groups; x++) { 00596 if (!strcmp(mygroup, groups[x])) { 00597 igrp = 1; 00598 break; 00599 } 00600 } 00601 } 00602 00603 if (!igrp) { 00604 ast_channel_unlock(peer); 00605 continue; 00606 } 00607 00608 strcpy(peer_name, "spy-"); 00609 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1); 00610 ptr = strchr(peer_name, '/'); 00611 *ptr++ = '\0'; 00612 00613 for (s = peer_name; s < ptr; s++) 00614 *s = tolower(*s); 00615 00616 /* We have to unlock the peer channel here to avoid a deadlock. 00617 * So, when we need to dereference it again, we have to lock the 00618 * datastore and get the pointer from there to see if the channel 00619 * is still valid. */ 00620 ast_channel_unlock(peer); 00621 00622 if (!ast_test_flag(flags, OPTION_QUIET)) { 00623 if (ast_fileexists(peer_name, NULL, NULL) != -1) { 00624 res = ast_streamfile(chan, peer_name, chan->language); 00625 if (!res) 00626 res = ast_waitstream(chan, ""); 00627 if (res) { 00628 chanspy_ds_free(peer_chanspy_ds); 00629 break; 00630 } 00631 } else 00632 res = ast_say_character_str(chan, peer_name, "", chan->language); 00633 if ((num = atoi(ptr))) 00634 ast_say_digits(chan, atoi(ptr), "", chan->language); 00635 } 00636 00637 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags); 00638 num_spyed_upon++; 00639 00640 if (res == -1) { 00641 chanspy_ds_free(peer_chanspy_ds); 00642 break; 00643 } else if (res > 1 && spec) { 00644 struct ast_channel *next; 00645 00646 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); 00647 00648 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) { 00649 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds); 00650 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds); 00651 } else { 00652 /* stay on this channel, if it is still valid */ 00653 00654 ast_mutex_lock(&peer_chanspy_ds->lock); 00655 if (peer_chanspy_ds->chan) { 00656 ast_channel_lock(peer_chanspy_ds->chan); 00657 next_chanspy_ds = peer_chanspy_ds; 00658 peer_chanspy_ds = NULL; 00659 } else { 00660 /* the channel is gone */ 00661 ast_mutex_unlock(&peer_chanspy_ds->lock); 00662 next_chanspy_ds = NULL; 00663 } 00664 } 00665 00666 peer = NULL; 00667 } 00668 } 00669 if (res == -1 || ast_check_hangup(chan)) 00670 break; 00671 } 00672 00673 ast_clear_flag(chan, AST_FLAG_SPYING); 00674 00675 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); 00676 00677 ast_mutex_lock(&chanspy_ds.lock); 00678 ast_mutex_unlock(&chanspy_ds.lock); 00679 ast_mutex_destroy(&chanspy_ds.lock); 00680 00681 return res; 00682 }
static int extenspy_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 767 of file app_chanspy.c.
References ast_app_parse_options(), ast_app_separate_args(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_module_user_add, ast_module_user_remove, ast_set_flag, ast_set_write_format(), ast_strdupa, ast_strlen_zero(), ast_test_flag, 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, strsep(), and ast_channel::writeformat.
Referenced by load_module().
00768 { 00769 struct ast_module_user *u; 00770 char *options = NULL; 00771 char *exten = NULL; 00772 char *context = NULL; 00773 char *argv[2]; 00774 char *mygroup = NULL; 00775 char *recbase = NULL; 00776 int fd = 0; 00777 struct ast_flags flags; 00778 int oldwf = 0; 00779 int argc = 0; 00780 int volfactor = 0; 00781 int res; 00782 00783 data = ast_strdupa(data); 00784 00785 u = ast_module_user_add(chan); 00786 00787 if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) { 00788 context = argv[0]; 00789 if (!ast_strlen_zero(argv[0])) 00790 exten = strsep(&context, "@"); 00791 if (ast_strlen_zero(context)) 00792 context = ast_strdupa(chan->context); 00793 if (argc > 1) 00794 options = argv[1]; 00795 } 00796 00797 if (options) { 00798 char *opts[OPT_ARG_ARRAY_SIZE]; 00799 00800 ast_app_parse_options(spy_opts, &flags, opts, options); 00801 if (ast_test_flag(&flags, OPTION_GROUP)) 00802 mygroup = opts[OPT_ARG_GROUP]; 00803 00804 if (ast_test_flag(&flags, OPTION_RECORD) && 00805 !(recbase = opts[OPT_ARG_RECORD])) 00806 recbase = "chanspy"; 00807 00808 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { 00809 int vol; 00810 00811 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4)) 00812 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); 00813 else 00814 volfactor = vol; 00815 } 00816 00817 if (ast_test_flag(&flags, OPTION_PRIVATE)) 00818 ast_set_flag(&flags, OPTION_WHISPER); 00819 } else 00820 ast_clear_flag(&flags, AST_FLAGS_ALL); 00821 00822 oldwf = chan->writeformat; 00823 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { 00824 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00825 ast_module_user_remove(u); 00826 return -1; 00827 } 00828 00829 if (recbase) { 00830 char filename[PATH_MAX]; 00831 00832 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); 00833 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) { 00834 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); 00835 fd = 0; 00836 } 00837 } 00838 00839 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context); 00840 00841 if (fd) 00842 close(fd); 00843 00844 if (oldwf && ast_set_write_format(chan, oldwf) < 0) 00845 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00846 00847 ast_module_user_remove(u); 00848 00849 return res; 00850 }
static int load_module | ( | void | ) | [static] |
Definition at line 864 of file app_chanspy.c.
References ast_register_application(), chanspy_exec(), and extenspy_exec().
00865 { 00866 int res = 0; 00867 00868 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan); 00869 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext); 00870 00871 return res; 00872 }
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 463 of file app_chanspy.c.
References AST_CHANNEL_NAME, ast_channel_unlock, ast_channel_walk_locked(), ast_walk_channel_by_exten_locked(), ast_walk_channel_by_name_prefix_locked(), dahdi_chan_name, dahdi_chan_name_len, last, and setup_chanspy_ds().
Referenced by common_exec().
00466 { 00467 struct ast_channel *this; 00468 char channel_name[AST_CHANNEL_NAME]; 00469 static size_t PSEUDO_CHAN_LEN = 0; 00470 00471 if (!PSEUDO_CHAN_LEN) { 00472 PSEUDO_CHAN_LEN = *dahdi_chan_name_len + strlen("/pseudo"); 00473 } 00474 00475 redo: 00476 if (spec) 00477 this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec)); 00478 else if (exten) 00479 this = ast_walk_channel_by_exten_locked(last, exten, context); 00480 else 00481 this = ast_channel_walk_locked(last); 00482 00483 if (!this) 00484 return NULL; 00485 00486 snprintf(channel_name, AST_CHANNEL_NAME, "%s/pseudo", dahdi_chan_name); 00487 if (!strncmp(this->name, channel_name, PSEUDO_CHAN_LEN)) { 00488 last = this; 00489 ast_channel_unlock(this); 00490 goto redo; 00491 } else if (this == chan) { 00492 last = this; 00493 ast_channel_unlock(this); 00494 goto redo; 00495 } 00496 00497 return setup_chanspy_ds(this, chanspy_ds); 00498 }
static struct chanspy_ds* setup_chanspy_ds | ( | struct ast_channel * | chan, | |
struct chanspy_ds * | chanspy_ds | |||
) | [static] |
Definition at line 443 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, chanspy_ds_free(), chanspy_ds_info, ast_datastore::data, chanspy_ds::lock, and chanspy_ds::unique_id.
Referenced by common_exec(), and next_channel().
00444 { 00445 struct ast_datastore *datastore = NULL; 00446 00447 ast_mutex_lock(&chanspy_ds->lock); 00448 00449 if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) { 00450 ast_mutex_unlock(&chanspy_ds->lock); 00451 chanspy_ds = chanspy_ds_free(chanspy_ds); 00452 ast_channel_unlock(chan); 00453 return NULL; 00454 } 00455 00456 chanspy_ds->chan = chan; 00457 datastore->data = chanspy_ds; 00458 ast_channel_datastore_add(chan, datastore); 00459 00460 return chanspy_ds; 00461 }
static void* spy_alloc | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 159 of file app_chanspy.c.
00160 { 00161 /* just store the data pointer in the channel structure */ 00162 return data; 00163 }
static int spy_generate | ( | struct ast_channel * | chan, | |
void * | data, | |||
int | len, | |||
int | samples | |||
) | [static] |
Definition at line 170 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_write(), f, chanspy_translation_helper::fd, chanspy_translation_helper::spy_audiohook, and ast_audiohook::status.
00171 { 00172 struct chanspy_translation_helper *csth = data; 00173 struct ast_frame *f; 00174 00175 ast_audiohook_lock(&csth->spy_audiohook); 00176 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) { 00177 ast_audiohook_unlock(&csth->spy_audiohook); 00178 return -1; 00179 } 00180 00181 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR); 00182 00183 ast_audiohook_unlock(&csth->spy_audiohook); 00184 00185 if (!f) 00186 return 0; 00187 00188 if (ast_write(chan, f)) { 00189 ast_frfree(f); 00190 return -1; 00191 } 00192 00193 if (csth->fd) 00194 write(csth->fd, f->data, f->datalen); 00195 00196 ast_frfree(f); 00197 00198 return 0; 00199 }
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 207 of file app_chanspy.c.
References ast_audiohook_attach(), ast_bridged_channel(), AST_FLAG_NBRIDGE, ast_log(), ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, ast_test_flag, and LOG_NOTICE.
Referenced by channel_spy().
00208 { 00209 int res; 00210 struct ast_channel *peer; 00211 00212 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name); 00213 00214 res = ast_audiohook_attach(chan, audiohook); 00215 00216 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 00217 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); 00218 } 00219 return res; 00220 }
static int unload_module | ( | void | ) | [static] |
Definition at line 852 of file app_chanspy.c.
References ast_module_user_hangup_all, and ast_unregister_application().
00853 { 00854 int res = 0; 00855 00856 res |= ast_unregister_application(app_chan); 00857 res |= ast_unregister_application(app_ext); 00858 00859 ast_module_user_hangup_all(); 00860 00861 return res; 00862 }
const char* app_chan = "ChanSpy" [static] |
Definition at line 63 of file app_chanspy.c.
const char* app_ext = "ExtenSpy" [static] |
Definition at line 94 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 409 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 64 of file app_chanspy.c.
const char* desc_ext [static] |
Definition at line 95 of file app_chanspy.c.
int next_unique_id_to_use = 0 [static] |
Definition at line 149 of file app_chanspy.c.
struct ast_generator spygen [static] |
Initial value:
{ .alloc = spy_alloc, .release = spy_release, .generate = spy_generate, }
Definition at line 201 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 62 of file app_chanspy.c.