ChanSpy: Listen in on any channel. More...
#include "asterisk.h"#include <ctype.h>#include <errno.h>#include "asterisk/paths.h"#include "asterisk/file.h"#include "asterisk/channel.h"#include "asterisk/audiohook.h"#include "asterisk/features.h"#include "asterisk/app.h"#include "asterisk/utils.h"#include "asterisk/say.h"#include "asterisk/pbx.h"#include "asterisk/translate.h"#include "asterisk/manager.h"#include "asterisk/module.h"#include "asterisk/lock.h"#include "asterisk/options.h"
Go to the source code of this file.
Data Structures | |
| struct | chanspy_ds |
| struct | chanspy_translation_helper |
Defines | |
| #define | AST_NAME_STRLEN 256 |
| #define | NUM_SPYGROUPS 128 |
Enumerations | |
| enum | { OPTION_QUIET = (1 << 0), OPTION_BRIDGED = (1 << 1), OPTION_VOLUME = (1 << 2), OPTION_GROUP = (1 << 3), OPTION_RECORD = (1 << 4), OPTION_WHISPER = (1 << 5), OPTION_PRIVATE = (1 << 6), OPTION_READONLY = (1 << 7), OPTION_EXIT = (1 << 8), OPTION_ENFORCED = (1 << 9), OPTION_NOTECH = (1 << 10), OPTION_BARGE = (1 << 11), OPTION_NAME = (1 << 12), OPTION_DTMF_SWITCH_MODES = (1 << 13) } |
| enum | { OPT_ARG_VOLUME = 0, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_ENFORCED, OPT_ARG_NAME, OPT_ARG_ARRAY_SIZE } |
Functions | |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static void | change_spy_mode (const char digit, struct ast_flags *flags) |
| static int | channel_spy (struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, int *volfactor, int fd, struct ast_flags *flags, char *exitcontext) |
| static void | chanspy_ds_chan_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) |
| static void | chanspy_ds_destroy (void *data) |
| static struct chanspy_ds * | chanspy_ds_free (struct chanspy_ds *chanspy_ds) |
| static int | chanspy_exec (struct ast_channel *chan, void *data) |
| static int | common_exec (struct ast_channel *chan, struct ast_flags *flags, int volfactor, const int fd, const char *mygroup, const char *myenforced, const char *spec, const char *exten, const char *context, const char *mailbox, const char *name_context) |
| static int | extenspy_exec (struct ast_channel *chan, void *data) |
| static int | load_module (void) |
| static struct chanspy_ds * | next_channel (struct ast_channel *chan, const struct ast_channel *last, const char *spec, const char *exten, const char *context, struct chanspy_ds *chanspy_ds) |
| static struct chanspy_ds * | setup_chanspy_ds (struct ast_channel *chan, struct chanspy_ds *chanspy_ds) |
| static void * | spy_alloc (struct ast_channel *chan, void *data) |
| static int | spy_generate (struct ast_channel *chan, void *data, int len, int samples) |
| static void | spy_release (struct ast_channel *chan, void *data) |
| static int | start_spying (struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook) |
| static int | unload_module (void) |
Variables | |
| static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Listen to the audio of an active channel" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, } |
| static const char * | app_chan = "ChanSpy" |
| static const char * | app_ext = "ExtenSpy" |
| static 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 int | next_unique_id_to_use = 0 |
| static struct ast_app_option | spy_opts [128] = { [ 'q' ] = { .flag = OPTION_QUIET }, [ 'b' ] = { .flag = OPTION_BRIDGED }, [ 'B' ] = { .flag = OPTION_BARGE }, [ 'w' ] = { .flag = OPTION_WHISPER }, [ 'W' ] = { .flag = OPTION_PRIVATE }, [ 'v' ] = { .flag = OPTION_VOLUME , .arg_index = OPT_ARG_VOLUME + 1 }, [ 'g' ] = { .flag = OPTION_GROUP , .arg_index = OPT_ARG_GROUP + 1 }, [ 'r' ] = { .flag = OPTION_RECORD , .arg_index = OPT_ARG_RECORD + 1 }, [ 'e' ] = { .flag = OPTION_ENFORCED , .arg_index = OPT_ARG_ENFORCED + 1 }, [ 'o' ] = { .flag = OPTION_READONLY }, [ 'X' ] = { .flag = OPTION_EXIT }, [ 's' ] = { .flag = OPTION_NOTECH }, [ 'n' ] = { .flag = OPTION_NAME , .arg_index = OPT_ARG_NAME + 1 }, [ 'd' ] = { .flag = OPTION_DTMF_SWITCH_MODES },} |
| static struct ast_generator | spygen |
ChanSpy: Listen in on any channel.
Definition in file app_chanspy.c.
| #define AST_NAME_STRLEN 256 |
Definition at line 54 of file app_chanspy.c.
Referenced by common_exec().
| #define NUM_SPYGROUPS 128 |
Definition at line 55 of file app_chanspy.c.
Referenced by common_exec().
| anonymous enum |
Definition at line 296 of file app_chanspy.c.
00296 { 00297 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */ 00298 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */ 00299 OPTION_VOLUME = (1 << 2), /* Specify initial volume */ 00300 OPTION_GROUP = (1 << 3), /* Only look at channels in group */ 00301 OPTION_RECORD = (1 << 4), 00302 OPTION_WHISPER = (1 << 5), 00303 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */ 00304 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */ 00305 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */ 00306 OPTION_ENFORCED = (1 << 9), /* Enforced mode */ 00307 OPTION_NOTECH = (1 << 10), /* Skip technology name playback */ 00308 OPTION_BARGE = (1 << 11), /* Barge mode (whisper to both channels) */ 00309 OPTION_NAME = (1 << 12), /* Say the name of the person on whom we will spy */ 00310 OPTION_DTMF_SWITCH_MODES = (1 << 13), /*Allow numeric DTMF to switch between chanspy modes */ 00311 } chanspy_opt_flags;
| anonymous enum |
| OPT_ARG_VOLUME | |
| OPT_ARG_GROUP | |
| OPT_ARG_RECORD | |
| OPT_ARG_ENFORCED | |
| OPT_ARG_NAME | |
| OPT_ARG_ARRAY_SIZE |
Definition at line 313 of file app_chanspy.c.
00313 { 00314 OPT_ARG_VOLUME = 0, 00315 OPT_ARG_GROUP, 00316 OPT_ARG_RECORD, 00317 OPT_ARG_ENFORCED, 00318 OPT_ARG_NAME, 00319 OPT_ARG_ARRAY_SIZE, 00320 } chanspy_opt_args;
| static void __reg_module | ( | void | ) | [static] |
Definition at line 1244 of file app_chanspy.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 1244 of file app_chanspy.c.
| static void change_spy_mode | ( | const char | digit, | |
| struct ast_flags * | flags | |||
| ) | [static] |
Definition at line 431 of file app_chanspy.c.
References ast_clear_flag, ast_set_flag, OPTION_BARGE, and OPTION_WHISPER.
Referenced by channel_spy().
00432 { 00433 if (digit == '4') { 00434 ast_clear_flag(flags, OPTION_WHISPER); 00435 ast_clear_flag(flags, OPTION_BARGE); 00436 } else if (digit == '5') { 00437 ast_clear_flag(flags, OPTION_BARGE); 00438 ast_set_flag(flags, OPTION_WHISPER); 00439 } else if (digit == '6') { 00440 ast_clear_flag(flags, OPTION_WHISPER); 00441 ast_set_flag(flags, OPTION_BARGE); 00442 } 00443 }
| static int channel_spy | ( | struct ast_channel * | chan, | |
| struct chanspy_ds * | spyee_chanspy_ds, | |||
| int * | volfactor, | |||
| int | fd, | |||
| struct ast_flags * | flags, | |||
| char * | exitcontext | |||
| ) | [static] |
Definition at line 445 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_bridged_channel(), ast_channel_lock, ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_channel_trylock, ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_copy_flags, ast_deactivate_generator(), ast_debug, AST_FLAG_END_DTMF_ONLY, AST_FLAGS_ALL, AST_FRAME_DTMF, AST_FRAME_VOICE, ast_frfree, ast_goto_if_exists(), ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_read(), ast_set_flag, ast_strlen_zero(), ast_test_flag, ast_verb, ast_waitfor(), chanspy_translation_helper::bridge_whisper_audiohook, chanspy_ds::chan, change_spy_mode(), DEADLOCK_AVOIDANCE, EVENT_FLAG_CALL, f, chanspy_translation_helper::fd, ast_frame::frametype, chanspy_ds::lock, LOG_WARNING, manager_event, ast_channel::name, name, OPTION_BARGE, OPTION_DTMF_SWITCH_MODES, OPTION_EXIT, OPTION_PRIVATE, OPTION_WHISPER, ast_audiohook::options, pbx_builtin_setvar_helper(), ast_audiohook_options::read_volume, chanspy_translation_helper::spy_audiohook, start_spying(), ast_audiohook::status, ast_frame::subclass, chanspy_translation_helper::volfactor, chanspy_translation_helper::whisper_audiohook, and ast_audiohook_options::write_volume.
Referenced by common_exec().
00447 { 00448 struct chanspy_translation_helper csth; 00449 int running = 0, res, x = 0; 00450 char inp[24] = {0}; 00451 char *name; 00452 struct ast_frame *f; 00453 struct ast_silence_generator *silgen = NULL; 00454 struct ast_channel *spyee = NULL, *spyee_bridge = NULL; 00455 const char *spyer_name; 00456 00457 ast_channel_lock(chan); 00458 spyer_name = ast_strdupa(chan->name); 00459 ast_channel_unlock(chan); 00460 00461 ast_mutex_lock(&spyee_chanspy_ds->lock); 00462 while ((spyee = spyee_chanspy_ds->chan) && ast_channel_trylock(spyee)) { 00463 /* avoid a deadlock here, just in case spyee is masqueraded and 00464 * chanspy_ds_chan_fixup() is called with the channel locked */ 00465 DEADLOCK_AVOIDANCE(&spyee_chanspy_ds->lock); 00466 } 00467 ast_mutex_unlock(&spyee_chanspy_ds->lock); 00468 00469 if (!spyee) { 00470 return 0; 00471 } 00472 00473 /* We now hold the channel lock on spyee */ 00474 00475 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) { 00476 ast_channel_unlock(spyee); 00477 return 0; 00478 } 00479 00480 name = ast_strdupa(spyee->name); 00481 00482 ast_verb(2, "Spying on channel %s\n", name); 00483 manager_event(EVENT_FLAG_CALL, "ChanSpyStart", 00484 "SpyerChannel: %s\r\n" 00485 "SpyeeChannel: %s\r\n", 00486 spyer_name, name); 00487 00488 memset(&csth, 0, sizeof(csth)); 00489 ast_copy_flags(&csth.spy_audiohook, flags, AST_FLAGS_ALL); 00490 00491 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy"); 00492 00493 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) { 00494 ast_audiohook_destroy(&csth.spy_audiohook); 00495 ast_channel_unlock(spyee); 00496 return 0; 00497 } 00498 00499 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy"); 00500 ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy"); 00501 if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) { 00502 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name); 00503 } 00504 if ((spyee_bridge = ast_bridged_channel(spyee))) { 00505 ast_channel_lock(spyee_bridge); 00506 if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) { 00507 ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name); 00508 } 00509 ast_channel_unlock(spyee_bridge); 00510 } 00511 ast_channel_unlock(spyee); 00512 spyee = NULL; 00513 00514 ast_channel_lock(chan); 00515 ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY); 00516 ast_channel_unlock(chan); 00517 00518 csth.volfactor = *volfactor; 00519 00520 if (csth.volfactor) { 00521 csth.spy_audiohook.options.read_volume = csth.volfactor; 00522 csth.spy_audiohook.options.write_volume = csth.volfactor; 00523 } 00524 00525 csth.fd = fd; 00526 00527 if (ast_test_flag(flags, OPTION_PRIVATE)) 00528 silgen = ast_channel_start_silence_generator(chan); 00529 else 00530 ast_activate_generator(chan, &spygen, &csth); 00531 00532 /* We can no longer rely on 'spyee' being an actual channel; 00533 it can be hung up and freed out from under us. However, the 00534 channel destructor will put NULL into our csth.spy.chan 00535 field when that happens, so that is our signal that the spyee 00536 channel has gone away. 00537 */ 00538 00539 /* Note: it is very important that the ast_waitfor() be the first 00540 condition in this expression, so that if we wait for some period 00541 of time before receiving a frame from our spying channel, we check 00542 for hangup on the spied-on channel _after_ knowing that a frame 00543 has arrived, since the spied-on channel could have gone away while 00544 we were waiting 00545 */ 00546 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { 00547 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) { 00548 running = -1; 00549 break; 00550 } 00551 00552 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) { 00553 ast_audiohook_lock(&csth.whisper_audiohook); 00554 ast_audiohook_lock(&csth.bridge_whisper_audiohook); 00555 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); 00556 ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); 00557 ast_audiohook_unlock(&csth.whisper_audiohook); 00558 ast_audiohook_unlock(&csth.bridge_whisper_audiohook); 00559 ast_frfree(f); 00560 continue; 00561 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) { 00562 ast_audiohook_lock(&csth.whisper_audiohook); 00563 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); 00564 ast_audiohook_unlock(&csth.whisper_audiohook); 00565 ast_frfree(f); 00566 continue; 00567 } 00568 00569 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0; 00570 ast_frfree(f); 00571 if (!res) 00572 continue; 00573 00574 if (x == sizeof(inp)) 00575 x = 0; 00576 00577 if (res < 0) { 00578 running = -1; 00579 break; 00580 } 00581 00582 if (ast_test_flag(flags, OPTION_EXIT)) { 00583 char tmp[2]; 00584 tmp[0] = res; 00585 tmp[1] = '\0'; 00586 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) { 00587 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext); 00588 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name); 00589 running = -2; 00590 break; 00591 } else { 00592 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); 00593 } 00594 } else if (res >= '0' && res <= '9') { 00595 if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) { 00596 change_spy_mode(res, flags); 00597 } else { 00598 inp[x++] = res; 00599 } 00600 } 00601 00602 if (res == '*') { 00603 running = 0; 00604 break; 00605 } else if (res == '#') { 00606 if (!ast_strlen_zero(inp)) { 00607 running = atoi(inp); 00608 break; 00609 } 00610 00611 (*volfactor)++; 00612 if (*volfactor > 4) 00613 *volfactor = -4; 00614 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor); 00615 00616 csth.volfactor = *volfactor; 00617 csth.spy_audiohook.options.read_volume = csth.volfactor; 00618 csth.spy_audiohook.options.write_volume = csth.volfactor; 00619 } 00620 } 00621 00622 if (ast_test_flag(flags, OPTION_PRIVATE)) 00623 ast_channel_stop_silence_generator(chan, silgen); 00624 else 00625 ast_deactivate_generator(chan); 00626 00627 ast_channel_lock(chan); 00628 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY); 00629 ast_channel_unlock(chan); 00630 00631 ast_audiohook_lock(&csth.whisper_audiohook); 00632 ast_audiohook_detach(&csth.whisper_audiohook); 00633 ast_audiohook_unlock(&csth.whisper_audiohook); 00634 ast_audiohook_destroy(&csth.whisper_audiohook); 00635 00636 ast_audiohook_lock(&csth.bridge_whisper_audiohook); 00637 ast_audiohook_detach(&csth.bridge_whisper_audiohook); 00638 ast_audiohook_unlock(&csth.bridge_whisper_audiohook); 00639 ast_audiohook_destroy(&csth.bridge_whisper_audiohook); 00640 00641 ast_audiohook_lock(&csth.spy_audiohook); 00642 ast_audiohook_detach(&csth.spy_audiohook); 00643 ast_audiohook_unlock(&csth.spy_audiohook); 00644 ast_audiohook_destroy(&csth.spy_audiohook); 00645 00646 ast_verb(2, "Done Spying on channel %s\n", name); 00647 manager_event(EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name); 00648 00649 return running; 00650 }
| static void chanspy_ds_chan_fixup | ( | void * | data, | |
| struct ast_channel * | old_chan, | |||
| struct ast_channel * | new_chan | |||
| ) | [static] |
Definition at line 669 of file app_chanspy.c.
References ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, and chanspy_ds::lock.
00670 { 00671 struct chanspy_ds *chanspy_ds = data; 00672 00673 ast_mutex_lock(&chanspy_ds->lock); 00674 chanspy_ds->chan = new_chan; 00675 ast_mutex_unlock(&chanspy_ds->lock); 00676 }
| static void chanspy_ds_destroy | ( | void * | data | ) | [static] |
Definition at line 656 of file app_chanspy.c.
References ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, and chanspy_ds::lock.
Referenced by chanspy_ds_free().
00657 { 00658 struct chanspy_ds *chanspy_ds = data; 00659 00660 /* Setting chan to be NULL is an atomic operation, but we don't want this 00661 * value to change while this lock is held. The lock is held elsewhere 00662 * while it performs non-atomic operations with this channel pointer */ 00663 00664 ast_mutex_lock(&chanspy_ds->lock); 00665 chanspy_ds->chan = NULL; 00666 ast_mutex_unlock(&chanspy_ds->lock); 00667 }
| static struct chanspy_ds* chanspy_ds_free | ( | struct chanspy_ds * | chanspy_ds | ) | [static, read] |
Definition at line 684 of file app_chanspy.c.
References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_trylock, ast_channel_unlock, ast_datastore_free(), ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, chan, chanspy_ds_destroy(), ast_datastore::data, DEADLOCK_AVOIDANCE, chanspy_ds::lock, and chanspy_ds::unique_id.
Referenced by common_exec(), and setup_chanspy_ds().
00685 { 00686 struct ast_channel *chan; 00687 00688 if (!chanspy_ds) { 00689 return NULL; 00690 } 00691 00692 ast_mutex_lock(&chanspy_ds->lock); 00693 while ((chan = chanspy_ds->chan)) { 00694 struct ast_datastore *datastore; 00695 00696 if (ast_channel_trylock(chan)) { 00697 DEADLOCK_AVOIDANCE(&chanspy_ds->lock); 00698 continue; 00699 } 00700 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) { 00701 ast_channel_datastore_remove(chan, datastore); 00702 /* chanspy_ds->chan is NULL after this call */ 00703 chanspy_ds_destroy(datastore->data); 00704 datastore->data = NULL; 00705 ast_datastore_free(datastore); 00706 } 00707 ast_channel_unlock(chan); 00708 break; 00709 } 00710 ast_mutex_unlock(&chanspy_ds->lock); 00711 00712 return NULL; 00713 }
| static int chanspy_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 1035 of file app_chanspy.c.
References AST_APP_ARG, ast_app_parse_options(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_DECLARE_APP_ARGS, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_set_flag, ast_set_write_format(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_test_flag, common_exec(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, mailbox, OPT_ARG_ARRAY_SIZE, OPT_ARG_ENFORCED, OPT_ARG_GROUP, OPT_ARG_NAME, OPT_ARG_RECORD, OPT_ARG_VOLUME, OPTION_ENFORCED, OPTION_GROUP, OPTION_NAME, OPTION_PRIVATE, OPTION_RECORD, OPTION_VOLUME, OPTION_WHISPER, spy_opts, and ast_channel::writeformat.
Referenced by load_module().
01036 { 01037 char *myenforced = NULL; 01038 char *mygroup = NULL; 01039 char *recbase = NULL; 01040 int fd = 0; 01041 struct ast_flags flags; 01042 int oldwf = 0; 01043 int volfactor = 0; 01044 int res; 01045 char *mailbox = NULL; 01046 char *name_context = NULL; 01047 AST_DECLARE_APP_ARGS(args, 01048 AST_APP_ARG(spec); 01049 AST_APP_ARG(options); 01050 ); 01051 char *opts[OPT_ARG_ARRAY_SIZE]; 01052 01053 data = ast_strdupa(data); 01054 AST_STANDARD_APP_ARGS(args, data); 01055 01056 if (args.spec && !strcmp(args.spec, "all")) 01057 args.spec = NULL; 01058 01059 if (args.options) { 01060 ast_app_parse_options(spy_opts, &flags, opts, args.options); 01061 if (ast_test_flag(&flags, OPTION_GROUP)) 01062 mygroup = opts[OPT_ARG_GROUP]; 01063 01064 if (ast_test_flag(&flags, OPTION_RECORD) && 01065 !(recbase = opts[OPT_ARG_RECORD])) 01066 recbase = "chanspy"; 01067 01068 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { 01069 int vol; 01070 01071 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4)) 01072 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); 01073 else 01074 volfactor = vol; 01075 } 01076 01077 if (ast_test_flag(&flags, OPTION_PRIVATE)) 01078 ast_set_flag(&flags, OPTION_WHISPER); 01079 01080 if (ast_test_flag(&flags, OPTION_ENFORCED)) 01081 myenforced = opts[OPT_ARG_ENFORCED]; 01082 01083 if (ast_test_flag(&flags, OPTION_NAME)) { 01084 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) { 01085 char *delimiter; 01086 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) { 01087 mailbox = opts[OPT_ARG_NAME]; 01088 *delimiter++ = '\0'; 01089 name_context = delimiter; 01090 } else { 01091 mailbox = opts[OPT_ARG_NAME]; 01092 } 01093 } 01094 } 01095 01096 01097 } else 01098 ast_clear_flag(&flags, AST_FLAGS_ALL); 01099 01100 oldwf = chan->writeformat; 01101 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { 01102 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 01103 return -1; 01104 } 01105 01106 if (recbase) { 01107 char filename[PATH_MAX]; 01108 01109 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); 01110 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) { 01111 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); 01112 fd = 0; 01113 } 01114 } 01115 01116 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context); 01117 01118 if (fd) 01119 close(fd); 01120 01121 if (oldwf && ast_set_write_format(chan, oldwf) < 0) 01122 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 01123 01124 return res; 01125 }
| static int common_exec | ( | struct ast_channel * | chan, | |
| struct ast_flags * | flags, | |||
| int | volfactor, | |||
| const int | fd, | |||
| const char * | mygroup, | |||
| const char * | myenforced, | |||
| const char * | spec, | |||
| const char * | exten, | |||
| const char * | context, | |||
| const char * | mailbox, | |||
| const char * | name_context | |||
| ) | [static] |
Definition at line 767 of file app_chanspy.c.
References ast_channel::_state, ARRAY_LEN, ast_answer(), ast_app_sayname(), ast_app_separate_args, ast_atomic_fetchadd_int(), ast_bridged_channel(), ast_channel_lock, AST_CHANNEL_NAME, ast_channel_setoption(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_copy_string(), ast_debug, ast_fileexists(), AST_FLAG_SPYING, ast_get_channel_by_name_prefix_locked(), ast_goto_if_exists(), AST_MAX_CONTEXT, ast_mutex_destroy(), ast_mutex_init(), ast_mutex_lock(), ast_mutex_unlock(), AST_NAME_STRLEN, AST_OPTION_TXGAIN, ast_say_character_str(), ast_say_digits(), ast_set_flag, AST_STATE_UP, ast_streamfile(), ast_strlen_zero(), ast_test_flag, ast_waitfordigit(), ast_waitstream(), channel_spy(), chanspy_ds_free(), ast_channel::context, exitcontext, ext, ast_channel::language, chanspy_ds::lock, ast_channel::macrocontext, ast_channel::next, next_channel(), num, NUM_SPYGROUPS, OPTION_BRIDGED, OPTION_EXIT, OPTION_NAME, OPTION_NOTECH, OPTION_QUIET, pbx_builtin_getvar_helper(), s, S_OR, setup_chanspy_ds(), and chanspy_ds::unique_id.
Referenced by chanspy_exec(), and extenspy_exec().
00771 { 00772 char nameprefix[AST_NAME_STRLEN]; 00773 char peer_name[AST_NAME_STRLEN + 5]; 00774 char exitcontext[AST_MAX_CONTEXT] = ""; 00775 signed char zero_volume = 0; 00776 int waitms; 00777 int res; 00778 char *ptr; 00779 int num; 00780 int num_spyed_upon = 1; 00781 struct chanspy_ds chanspy_ds = { 0, }; 00782 00783 if (ast_test_flag(flags, OPTION_EXIT)) { 00784 const char *c; 00785 ast_channel_lock(chan); 00786 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) { 00787 ast_copy_string(exitcontext, c, sizeof(exitcontext)); 00788 } else if (!ast_strlen_zero(chan->macrocontext)) { 00789 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext)); 00790 } else { 00791 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext)); 00792 } 00793 ast_channel_unlock(chan); 00794 } 00795 00796 ast_mutex_init(&chanspy_ds.lock); 00797 00798 snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1)); 00799 00800 if (chan->_state != AST_STATE_UP) 00801 ast_answer(chan); 00802 00803 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ 00804 00805 waitms = 100; 00806 00807 for (;;) { 00808 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL; 00809 struct ast_channel *prev = NULL, *peer = NULL; 00810 00811 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { 00812 res = ast_streamfile(chan, "beep", chan->language); 00813 if (!res) 00814 res = ast_waitstream(chan, ""); 00815 else if (res < 0) { 00816 ast_clear_flag(chan, AST_FLAG_SPYING); 00817 break; 00818 } 00819 if (!ast_strlen_zero(exitcontext)) { 00820 char tmp[2]; 00821 tmp[0] = res; 00822 tmp[1] = '\0'; 00823 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) 00824 goto exit; 00825 else 00826 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); 00827 } 00828 } 00829 00830 res = ast_waitfordigit(chan, waitms); 00831 if (res < 0) { 00832 ast_clear_flag(chan, AST_FLAG_SPYING); 00833 break; 00834 } 00835 if (!ast_strlen_zero(exitcontext)) { 00836 char tmp[2]; 00837 tmp[0] = res; 00838 tmp[1] = '\0'; 00839 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) 00840 goto exit; 00841 else 00842 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); 00843 } 00844 00845 /* reset for the next loop around, unless overridden later */ 00846 waitms = 100; 00847 num_spyed_upon = 0; 00848 00849 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds); 00850 peer_chanspy_ds; 00851 chanspy_ds_free(peer_chanspy_ds), prev = peer, 00852 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 00853 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) { 00854 int igrp = !mygroup; 00855 int ienf = !myenforced; 00856 char *s; 00857 00858 peer = peer_chanspy_ds->chan; 00859 00860 ast_mutex_unlock(&peer_chanspy_ds->lock); 00861 00862 if (peer == prev) { 00863 ast_channel_unlock(peer); 00864 chanspy_ds_free(peer_chanspy_ds); 00865 break; 00866 } 00867 00868 if (ast_check_hangup(chan)) { 00869 ast_channel_unlock(peer); 00870 chanspy_ds_free(peer_chanspy_ds); 00871 break; 00872 } 00873 00874 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) { 00875 ast_channel_unlock(peer); 00876 continue; 00877 } 00878 00879 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) { 00880 ast_channel_unlock(peer); 00881 continue; 00882 } 00883 00884 if (mygroup) { 00885 int num_groups = 0; 00886 int num_mygroups = 0; 00887 char dup_group[512]; 00888 char dup_mygroup[512]; 00889 char *groups[NUM_SPYGROUPS]; 00890 char *mygroups[NUM_SPYGROUPS]; 00891 const char *group; 00892 int x; 00893 int y; 00894 ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup)); 00895 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups, 00896 ARRAY_LEN(mygroups)); 00897 00898 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) { 00899 ast_copy_string(dup_group, group, sizeof(dup_group)); 00900 num_groups = ast_app_separate_args(dup_group, ':', groups, 00901 ARRAY_LEN(groups)); 00902 } 00903 00904 for (y = 0; y < num_mygroups; y++) { 00905 for (x = 0; x < num_groups; x++) { 00906 if (!strcmp(mygroups[y], groups[x])) { 00907 igrp = 1; 00908 break; 00909 } 00910 } 00911 } 00912 } 00913 00914 if (!igrp) { 00915 ast_channel_unlock(peer); 00916 continue; 00917 } 00918 00919 if (myenforced) { 00920 char ext[AST_CHANNEL_NAME + 3]; 00921 char buffer[512]; 00922 char *end; 00923 00924 snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced); 00925 00926 ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1); 00927 if ((end = strchr(ext, '-'))) { 00928 *end++ = ':'; 00929 *end = '\0'; 00930 } 00931 00932 ext[0] = ':'; 00933 00934 if (strcasestr(buffer, ext)) { 00935 ienf = 1; 00936 } 00937 } 00938 00939 if (!ienf) { 00940 continue; 00941 } 00942 00943 strcpy(peer_name, "spy-"); 00944 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1); 00945 ptr = strchr(peer_name, '/'); 00946 *ptr++ = '\0'; 00947 ptr = strsep(&ptr, "-"); 00948 00949 for (s = peer_name; s < ptr; s++) 00950 *s = tolower(*s); 00951 /* We have to unlock the peer channel here to avoid a deadlock. 00952 * So, when we need to dereference it again, we have to lock the 00953 * datastore and get the pointer from there to see if the channel 00954 * is still valid. */ 00955 ast_channel_unlock(peer); 00956 00957 if (!ast_test_flag(flags, OPTION_QUIET)) { 00958 if (ast_test_flag(flags, OPTION_NAME)) { 00959 const char *local_context = S_OR(name_context, "default"); 00960 const char *local_mailbox = S_OR(mailbox, ptr); 00961 res = ast_app_sayname(chan, local_mailbox, local_context); 00962 } 00963 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) { 00964 if (!ast_test_flag(flags, OPTION_NOTECH)) { 00965 if (ast_fileexists(peer_name, NULL, NULL) != -1) { 00966 res = ast_streamfile(chan, peer_name, chan->language); 00967 if (!res) { 00968 res = ast_waitstream(chan, ""); 00969 } 00970 if (res) { 00971 chanspy_ds_free(peer_chanspy_ds); 00972 break; 00973 } 00974 } else { 00975 res = ast_say_character_str(chan, peer_name, "", chan->language); 00976 } 00977 } 00978 if ((num = atoi(ptr))) 00979 ast_say_digits(chan, atoi(ptr), "", chan->language); 00980 } 00981 } 00982 00983 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext); 00984 num_spyed_upon++; 00985 00986 if (res == -1) { 00987 chanspy_ds_free(peer_chanspy_ds); 00988 goto exit; 00989 } else if (res == -2) { 00990 res = 0; 00991 chanspy_ds_free(peer_chanspy_ds); 00992 goto exit; 00993 } else if (res > 1 && spec) { 00994 struct ast_channel *next; 00995 00996 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); 00997 00998 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) { 00999 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds); 01000 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds); 01001 } else { 01002 /* stay on this channel, if it is still valid */ 01003 01004 ast_mutex_lock(&peer_chanspy_ds->lock); 01005 if (peer_chanspy_ds->chan) { 01006 ast_channel_lock(peer_chanspy_ds->chan); 01007 next_chanspy_ds = peer_chanspy_ds; 01008 peer_chanspy_ds = NULL; 01009 } else { 01010 /* the channel is gone */ 01011 ast_mutex_unlock(&peer_chanspy_ds->lock); 01012 next_chanspy_ds = NULL; 01013 } 01014 } 01015 01016 peer = NULL; 01017 } 01018 } 01019 if (res == -1 || ast_check_hangup(chan)) 01020 break; 01021 } 01022 exit: 01023 01024 ast_clear_flag(chan, AST_FLAG_SPYING); 01025 01026 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); 01027 01028 ast_mutex_lock(&chanspy_ds.lock); 01029 ast_mutex_unlock(&chanspy_ds.lock); 01030 ast_mutex_destroy(&chanspy_ds.lock); 01031 01032 return res; 01033 }
| static int extenspy_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 1127 of file app_chanspy.c.
References AST_APP_ARG, ast_app_parse_options(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_DECLARE_APP_ARGS, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_set_flag, ast_set_write_format(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_test_flag, common_exec(), ast_channel::context, context, exten, LOG_ERROR, LOG_NOTICE, LOG_WARNING, mailbox, OPT_ARG_ARRAY_SIZE, OPT_ARG_GROUP, OPT_ARG_NAME, OPT_ARG_RECORD, OPT_ARG_VOLUME, OPTION_GROUP, OPTION_NAME, OPTION_PRIVATE, OPTION_RECORD, OPTION_VOLUME, OPTION_WHISPER, spy_opts, and ast_channel::writeformat.
Referenced by load_module().
01128 { 01129 char *ptr, *exten = NULL; 01130 char *mygroup = NULL; 01131 char *recbase = NULL; 01132 int fd = 0; 01133 struct ast_flags flags; 01134 int oldwf = 0; 01135 int volfactor = 0; 01136 int res; 01137 char *mailbox = NULL; 01138 char *name_context = NULL; 01139 AST_DECLARE_APP_ARGS(args, 01140 AST_APP_ARG(context); 01141 AST_APP_ARG(options); 01142 ); 01143 01144 data = ast_strdupa(data); 01145 01146 AST_STANDARD_APP_ARGS(args, data); 01147 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) { 01148 exten = args.context; 01149 *ptr++ = '\0'; 01150 args.context = ptr; 01151 } 01152 01153 if (ast_strlen_zero(args.context)) 01154 args.context = ast_strdupa(chan->context); 01155 01156 if (args.options) { 01157 char *opts[OPT_ARG_ARRAY_SIZE]; 01158 01159 ast_app_parse_options(spy_opts, &flags, opts, args.options); 01160 if (ast_test_flag(&flags, OPTION_GROUP)) 01161 mygroup = opts[OPT_ARG_GROUP]; 01162 01163 if (ast_test_flag(&flags, OPTION_RECORD) && 01164 !(recbase = opts[OPT_ARG_RECORD])) 01165 recbase = "chanspy"; 01166 01167 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { 01168 int vol; 01169 01170 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4)) 01171 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); 01172 else 01173 volfactor = vol; 01174 } 01175 01176 if (ast_test_flag(&flags, OPTION_PRIVATE)) 01177 ast_set_flag(&flags, OPTION_WHISPER); 01178 01179 01180 if (ast_test_flag(&flags, OPTION_NAME)) { 01181 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) { 01182 char *delimiter; 01183 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) { 01184 mailbox = opts[OPT_ARG_NAME]; 01185 *delimiter++ = '\0'; 01186 name_context = delimiter; 01187 } else { 01188 mailbox = opts[OPT_ARG_NAME]; 01189 } 01190 } 01191 } 01192 01193 } else 01194 ast_clear_flag(&flags, AST_FLAGS_ALL); 01195 01196 oldwf = chan->writeformat; 01197 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { 01198 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 01199 return -1; 01200 } 01201 01202 if (recbase) { 01203 char filename[PATH_MAX]; 01204 01205 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); 01206 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) { 01207 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); 01208 fd = 0; 01209 } 01210 } 01211 01212 01213 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context); 01214 01215 if (fd) 01216 close(fd); 01217 01218 if (oldwf && ast_set_write_format(chan, oldwf) < 0) 01219 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 01220 01221 return res; 01222 }
| static int load_module | ( | void | ) | [static] |
Definition at line 1234 of file app_chanspy.c.
References ast_register_application_xml, chanspy_exec(), and extenspy_exec().
01235 { 01236 int res = 0; 01237 01238 res |= ast_register_application_xml(app_chan, chanspy_exec); 01239 res |= ast_register_application_xml(app_ext, extenspy_exec); 01240 01241 return res; 01242 }
| 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, read] |
Definition at line 736 of file app_chanspy.c.
References ast_channel_unlock, ast_channel_walk_locked(), ast_strlen_zero(), ast_walk_channel_by_exten_locked(), ast_walk_channel_by_name_prefix_locked(), ast_channel::name, ast_channel::next, and setup_chanspy_ds().
Referenced by common_exec().
00739 { 00740 struct ast_channel *next; 00741 const size_t pseudo_len = strlen("DAHDI/pseudo"); 00742 00743 redo: 00744 if (!ast_strlen_zero(spec)) 00745 next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec)); 00746 else if (!ast_strlen_zero(exten)) 00747 next = ast_walk_channel_by_exten_locked(last, exten, context); 00748 else 00749 next = ast_channel_walk_locked(last); 00750 00751 if (!next) 00752 return NULL; 00753 00754 if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) { 00755 last = next; 00756 ast_channel_unlock(next); 00757 goto redo; 00758 } else if (next == chan) { 00759 last = next; 00760 ast_channel_unlock(next); 00761 goto redo; 00762 } 00763 00764 return setup_chanspy_ds(next, chanspy_ds); 00765 }
| static struct chanspy_ds* setup_chanspy_ds | ( | struct ast_channel * | chan, | |
| struct chanspy_ds * | chanspy_ds | |||
| ) | [static, read] |
Definition at line 716 of file app_chanspy.c.
References ast_channel_datastore_add(), ast_channel_unlock, ast_datastore_alloc, ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, chanspy_ds_free(), ast_datastore::data, chanspy_ds::lock, and chanspy_ds::unique_id.
Referenced by common_exec(), and next_channel().
00717 { 00718 struct ast_datastore *datastore = NULL; 00719 00720 ast_mutex_lock(&chanspy_ds->lock); 00721 00722 if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) { 00723 ast_mutex_unlock(&chanspy_ds->lock); 00724 chanspy_ds = chanspy_ds_free(chanspy_ds); 00725 ast_channel_unlock(chan); 00726 return NULL; 00727 } 00728 00729 chanspy_ds->chan = chan; 00730 datastore->data = chanspy_ds; 00731 ast_channel_datastore_add(chan, datastore); 00732 00733 return chanspy_ds; 00734 }
| static void* spy_alloc | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 350 of file app_chanspy.c.
00351 { 00352 /* just store the data pointer in the channel structure */ 00353 return data; 00354 }
| static int spy_generate | ( | struct ast_channel * | chan, | |
| void * | data, | |||
| int | len, | |||
| int | samples | |||
| ) | [static] |
Definition at line 361 of file app_chanspy.c.
References AST_AUDIOHOOK_DIRECTION_BOTH, AST_AUDIOHOOK_DIRECTION_READ, 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_test_flag, ast_write(), ast_frame::data, ast_frame::datalen, errno, f, chanspy_translation_helper::fd, ast_frame::frame_list, LOG_WARNING, OPTION_READONLY, ast_frame::ptr, chanspy_translation_helper::spy_audiohook, and ast_audiohook::status.
00362 { 00363 struct chanspy_translation_helper *csth = data; 00364 struct ast_frame *f, *cur; 00365 00366 ast_audiohook_lock(&csth->spy_audiohook); 00367 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) { 00368 /* Channel is already gone more than likely */ 00369 ast_audiohook_unlock(&csth->spy_audiohook); 00370 return -1; 00371 } 00372 00373 if (ast_test_flag(&csth->spy_audiohook, OPTION_READONLY)) { 00374 /* Option 'o' was set, so don't mix channel audio */ 00375 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, AST_FORMAT_SLINEAR); 00376 } else { 00377 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR); 00378 } 00379 00380 ast_audiohook_unlock(&csth->spy_audiohook); 00381 00382 if (!f) 00383 return 0; 00384 00385 for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) { 00386 if (ast_write(chan, cur)) { 00387 ast_frfree(f); 00388 return -1; 00389 } 00390 00391 if (csth->fd) { 00392 if (write(csth->fd, cur->data.ptr, cur->datalen) < 0) { 00393 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); 00394 } 00395 } 00396 } 00397 00398 ast_frfree(f); 00399 00400 return 0; 00401 }
| static void spy_release | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 356 of file app_chanspy.c.
| static int start_spying | ( | struct ast_channel * | chan, | |
| const char * | spychan_name, | |||
| struct ast_audiohook * | audiohook | |||
| ) | [static] |
Definition at line 409 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().
00410 { 00411 int res = 0; 00412 struct ast_channel *peer = NULL; 00413 00414 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name); 00415 00416 ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE); 00417 res = ast_audiohook_attach(chan, audiohook); 00418 00419 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 00420 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); 00421 } 00422 return res; 00423 }
| static int unload_module | ( | void | ) | [static] |
Definition at line 1224 of file app_chanspy.c.
References ast_unregister_application().
01225 { 01226 int res = 0; 01227 01228 res |= ast_unregister_application(app_chan); 01229 res |= ast_unregister_application(app_ext); 01230 01231 return res; 01232 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Listen to the audio of an active channel" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, } [static] |
Definition at line 1244 of file app_chanspy.c.
const char* app_chan = "ChanSpy" [static] |
Definition at line 292 of file app_chanspy.c.
const char* app_ext = "ExtenSpy" [static] |
Definition at line 294 of file app_chanspy.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 1244 of file app_chanspy.c.
struct ast_datastore_info chanspy_ds_info [static] |
{
.type = "chanspy",
.destroy = chanspy_ds_destroy,
.chan_fixup = chanspy_ds_chan_fixup,
}
Definition at line 678 of file app_chanspy.c.
| enum { ... } chanspy_opt_args |
| enum { ... } chanspy_opt_flags |
int next_unique_id_to_use = 0 [static] |
Definition at line 339 of file app_chanspy.c.
struct ast_app_option spy_opts[128] = { [ 'q' ] = { .flag = OPTION_QUIET }, [ 'b' ] = { .flag = OPTION_BRIDGED }, [ 'B' ] = { .flag = OPTION_BARGE }, [ 'w' ] = { .flag = OPTION_WHISPER }, [ 'W' ] = { .flag = OPTION_PRIVATE }, [ 'v' ] = { .flag = OPTION_VOLUME , .arg_index = OPT_ARG_VOLUME + 1 }, [ 'g' ] = { .flag = OPTION_GROUP , .arg_index = OPT_ARG_GROUP + 1 }, [ 'r' ] = { .flag = OPTION_RECORD , .arg_index = OPT_ARG_RECORD + 1 }, [ 'e' ] = { .flag = OPTION_ENFORCED , .arg_index = OPT_ARG_ENFORCED + 1 }, [ 'o' ] = { .flag = OPTION_READONLY }, [ 'X' ] = { .flag = OPTION_EXIT }, [ 's' ] = { .flag = OPTION_NOTECH }, [ 'n' ] = { .flag = OPTION_NAME , .arg_index = OPT_ARG_NAME + 1 }, [ 'd' ] = { .flag = OPTION_DTMF_SWITCH_MODES },} [static] |
Definition at line 337 of file app_chanspy.c.
Referenced by chanspy_exec(), and extenspy_exec().
struct ast_generator spygen [static] |
{
.alloc = spy_alloc,
.release = spy_release,
.generate = spy_generate,
}
Definition at line 403 of file app_chanspy.c.
1.6.2