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