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