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