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/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 = "a9c98e5d177805051735cb5b0b16b0a0" , .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 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 }, [ '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 |
| static const char * | tdesc = "Listen to a channel, and optionally whisper into it" |
ChanSpy: Listen in on any channel.
Definition in file app_chanspy.c.
| #define AST_NAME_STRLEN 256 |
Definition at line 53 of file app_chanspy.c.
Referenced by common_exec().
| #define NUM_SPYGROUPS 128 |
Definition at line 54 of file app_chanspy.c.
Referenced by common_exec().
| anonymous enum |
Definition at line 179 of file app_chanspy.c.
00179 { 00180 OPTION_QUIET = (1 << 0), /* Quiet, no announcement */ 00181 OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */ 00182 OPTION_VOLUME = (1 << 2), /* Specify initial volume */ 00183 OPTION_GROUP = (1 << 3), /* Only look at channels in group */ 00184 OPTION_RECORD = (1 << 4), 00185 OPTION_WHISPER = (1 << 5), 00186 OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */ 00187 OPTION_READONLY = (1 << 7), /* Don't mix the two channels */ 00188 OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */ 00189 OPTION_ENFORCED = (1 << 9), /* Enforced mode */ 00190 OPTION_NOTECH = (1 << 10), /* Skip technology name playback */ 00191 OPTION_BARGE = (1 << 11), /* Barge mode (whisper to both channels) */ 00192 OPTION_NAME = (1 << 12), /* Say the name of the person on whom we will spy */ 00193 OPTION_DTMF_SWITCH_MODES = (1 << 13), /*Allow numeric DTMF to switch between chanspy modes */ 00194 } 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 196 of file app_chanspy.c.
00196 { 00197 OPT_ARG_VOLUME = 0, 00198 OPT_ARG_GROUP, 00199 OPT_ARG_RECORD, 00200 OPT_ARG_ENFORCED, 00201 OPT_ARG_NAME, 00202 OPT_ARG_ARRAY_SIZE, 00203 } chanspy_opt_args;
| static void __reg_module | ( | void | ) | [static] |
Definition at line 1113 of file app_chanspy.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 1113 of file app_chanspy.c.
| static void change_spy_mode | ( | const char | digit, | |
| struct ast_flags * | flags | |||
| ) | [static] |
Definition at line 309 of file app_chanspy.c.
References ast_clear_flag, ast_set_flag, OPTION_BARGE, and OPTION_WHISPER.
Referenced by channel_spy().
00310 { 00311 if (digit == '4') { 00312 ast_clear_flag(flags, OPTION_WHISPER); 00313 ast_clear_flag(flags, OPTION_BARGE); 00314 } else if (digit == '5') { 00315 ast_clear_flag(flags, OPTION_BARGE); 00316 ast_set_flag(flags, OPTION_WHISPER); 00317 } else if (digit == '6') { 00318 ast_clear_flag(flags, OPTION_WHISPER); 00319 ast_set_flag(flags, OPTION_BARGE); 00320 } 00321 }
| 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 323 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_unlock, ast_check_hangup(), ast_clear_flag, ast_deactivate_generator(), ast_debug, AST_FLAG_END_DTMF_ONLY, 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_strdupa, ast_strlen_zero(), ast_test_flag, ast_verb, ast_waitfor(), chanspy_translation_helper::bridge_whisper_audiohook, chanspy_ds::chan, change_spy_mode(), f, chanspy_translation_helper::fd, ast_frame::frametype, chanspy_ds::lock, LOG_WARNING, 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().
00325 { 00326 struct chanspy_translation_helper csth; 00327 int running = 0, res, x = 0; 00328 char inp[24] = {0}; 00329 char *name; 00330 struct ast_frame *f; 00331 struct ast_silence_generator *silgen = NULL; 00332 struct ast_channel *spyee = NULL, *spyee_bridge = NULL; 00333 const char *spyer_name; 00334 00335 ast_channel_lock(chan); 00336 spyer_name = ast_strdupa(chan->name); 00337 ast_channel_unlock(chan); 00338 00339 ast_mutex_lock(&spyee_chanspy_ds->lock); 00340 if (spyee_chanspy_ds->chan) { 00341 spyee = spyee_chanspy_ds->chan; 00342 ast_channel_lock(spyee); 00343 } 00344 ast_mutex_unlock(&spyee_chanspy_ds->lock); 00345 00346 if (!spyee) 00347 return 0; 00348 00349 /* We now hold the channel lock on spyee */ 00350 00351 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) { 00352 ast_channel_unlock(spyee); 00353 return 0; 00354 } 00355 00356 name = ast_strdupa(spyee->name); 00357 ast_verb(2, "Spying on channel %s\n", name); 00358 00359 memset(&csth, 0, sizeof(csth)); 00360 00361 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy"); 00362 00363 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) { 00364 ast_audiohook_destroy(&csth.spy_audiohook); 00365 ast_channel_unlock(spyee); 00366 return 0; 00367 } 00368 00369 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy"); 00370 ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy"); 00371 if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) { 00372 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name); 00373 } 00374 if ((spyee_bridge = ast_bridged_channel(spyee))) { 00375 ast_channel_lock(spyee_bridge); 00376 if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) { 00377 ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name); 00378 } 00379 ast_channel_unlock(spyee_bridge); 00380 } 00381 ast_channel_unlock(spyee); 00382 spyee = NULL; 00383 00384 ast_channel_lock(chan); 00385 ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY); 00386 ast_channel_unlock(chan); 00387 00388 csth.volfactor = *volfactor; 00389 00390 if (csth.volfactor) { 00391 csth.spy_audiohook.options.read_volume = csth.volfactor; 00392 csth.spy_audiohook.options.write_volume = csth.volfactor; 00393 } 00394 00395 csth.fd = fd; 00396 00397 if (ast_test_flag(flags, OPTION_PRIVATE)) 00398 silgen = ast_channel_start_silence_generator(chan); 00399 else 00400 ast_activate_generator(chan, &spygen, &csth); 00401 00402 /* We can no longer rely on 'spyee' being an actual channel; 00403 it can be hung up and freed out from under us. However, the 00404 channel destructor will put NULL into our csth.spy.chan 00405 field when that happens, so that is our signal that the spyee 00406 channel has gone away. 00407 */ 00408 00409 /* Note: it is very important that the ast_waitfor() be the first 00410 condition in this expression, so that if we wait for some period 00411 of time before receiving a frame from our spying channel, we check 00412 for hangup on the spied-on channel _after_ knowing that a frame 00413 has arrived, since the spied-on channel could have gone away while 00414 we were waiting 00415 */ 00416 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { 00417 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) { 00418 running = -1; 00419 break; 00420 } 00421 00422 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) { 00423 ast_audiohook_lock(&csth.whisper_audiohook); 00424 ast_audiohook_lock(&csth.bridge_whisper_audiohook); 00425 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); 00426 ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); 00427 ast_audiohook_unlock(&csth.whisper_audiohook); 00428 ast_audiohook_unlock(&csth.bridge_whisper_audiohook); 00429 ast_frfree(f); 00430 continue; 00431 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) { 00432 ast_audiohook_lock(&csth.whisper_audiohook); 00433 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); 00434 ast_audiohook_unlock(&csth.whisper_audiohook); 00435 ast_frfree(f); 00436 continue; 00437 } 00438 00439 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0; 00440 ast_frfree(f); 00441 if (!res) 00442 continue; 00443 00444 if (x == sizeof(inp)) 00445 x = 0; 00446 00447 if (res < 0) { 00448 running = -1; 00449 break; 00450 } 00451 00452 if (ast_test_flag(flags, OPTION_EXIT)) { 00453 char tmp[2]; 00454 tmp[0] = res; 00455 tmp[1] = '\0'; 00456 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) { 00457 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext); 00458 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name); 00459 running = -2; 00460 break; 00461 } else { 00462 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); 00463 } 00464 } else if (res >= '0' && res <= '9') { 00465 if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) { 00466 change_spy_mode(res, flags); 00467 } else { 00468 inp[x++] = res; 00469 } 00470 } 00471 00472 if (res == '*') { 00473 running = 0; 00474 break; 00475 } else if (res == '#') { 00476 if (!ast_strlen_zero(inp)) { 00477 running = atoi(inp); 00478 break; 00479 } 00480 00481 (*volfactor)++; 00482 if (*volfactor > 4) 00483 *volfactor = -4; 00484 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor); 00485 00486 csth.volfactor = *volfactor; 00487 csth.spy_audiohook.options.read_volume = csth.volfactor; 00488 csth.spy_audiohook.options.write_volume = csth.volfactor; 00489 } 00490 } 00491 00492 if (ast_test_flag(flags, OPTION_PRIVATE)) 00493 ast_channel_stop_silence_generator(chan, silgen); 00494 else 00495 ast_deactivate_generator(chan); 00496 00497 ast_channel_lock(chan); 00498 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY); 00499 ast_channel_unlock(chan); 00500 00501 ast_audiohook_lock(&csth.whisper_audiohook); 00502 ast_audiohook_detach(&csth.whisper_audiohook); 00503 ast_audiohook_unlock(&csth.whisper_audiohook); 00504 ast_audiohook_destroy(&csth.whisper_audiohook); 00505 00506 ast_audiohook_lock(&csth.bridge_whisper_audiohook); 00507 ast_audiohook_detach(&csth.bridge_whisper_audiohook); 00508 ast_audiohook_unlock(&csth.bridge_whisper_audiohook); 00509 ast_audiohook_destroy(&csth.bridge_whisper_audiohook); 00510 00511 ast_audiohook_lock(&csth.spy_audiohook); 00512 ast_audiohook_detach(&csth.spy_audiohook); 00513 ast_audiohook_unlock(&csth.spy_audiohook); 00514 ast_audiohook_destroy(&csth.spy_audiohook); 00515 00516 ast_verb(2, "Done Spying on channel %s\n", name); 00517 00518 return running; 00519 }
| static void chanspy_ds_chan_fixup | ( | void * | data, | |
| struct ast_channel * | old_chan, | |||
| struct ast_channel * | new_chan | |||
| ) | [static] |
Definition at line 538 of file app_chanspy.c.
References ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, and chanspy_ds::lock.
00539 { 00540 struct chanspy_ds *chanspy_ds = data; 00541 00542 ast_mutex_lock(&chanspy_ds->lock); 00543 chanspy_ds->chan = new_chan; 00544 ast_mutex_unlock(&chanspy_ds->lock); 00545 }
| static void chanspy_ds_destroy | ( | void * | data | ) | [static] |
Definition at line 525 of file app_chanspy.c.
References ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, and chanspy_ds::lock.
Referenced by chanspy_ds_free().
00526 { 00527 struct chanspy_ds *chanspy_ds = data; 00528 00529 /* Setting chan to be NULL is an atomic operation, but we don't want this 00530 * value to change while this lock is held. The lock is held elsewhere 00531 * while it performs non-atomic operations with this channel pointer */ 00532 00533 ast_mutex_lock(&chanspy_ds->lock); 00534 chanspy_ds->chan = NULL; 00535 ast_mutex_unlock(&chanspy_ds->lock); 00536 }
| static struct chanspy_ds* chanspy_ds_free | ( | struct chanspy_ds * | chanspy_ds | ) | [static, read] |
Definition at line 553 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().
00554 { 00555 struct ast_channel *chan; 00556 00557 if (!chanspy_ds) { 00558 return NULL; 00559 } 00560 00561 ast_mutex_lock(&chanspy_ds->lock); 00562 while ((chan = chanspy_ds->chan)) { 00563 struct ast_datastore *datastore; 00564 00565 if (ast_channel_trylock(chan)) { 00566 DEADLOCK_AVOIDANCE(&chanspy_ds->lock); 00567 continue; 00568 } 00569 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) { 00570 ast_channel_datastore_remove(chan, datastore); 00571 /* chanspy_ds->chan is NULL after this call */ 00572 chanspy_ds_destroy(datastore->data); 00573 datastore->data = NULL; 00574 ast_datastore_free(datastore); 00575 } 00576 ast_channel_unlock(chan); 00577 break; 00578 } 00579 ast_mutex_unlock(&chanspy_ds->lock); 00580 00581 return NULL; 00582 }
| static int chanspy_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 904 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_FILE_MODE, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_set_flag, ast_set_write_format(), AST_STANDARD_APP_ARGS, ast_strdupa, 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().
00905 { 00906 char *myenforced = NULL; 00907 char *mygroup = NULL; 00908 char *recbase = NULL; 00909 int fd = 0; 00910 struct ast_flags flags; 00911 int oldwf = 0; 00912 int volfactor = 0; 00913 int res; 00914 char *mailbox = NULL; 00915 char *name_context = NULL; 00916 AST_DECLARE_APP_ARGS(args, 00917 AST_APP_ARG(spec); 00918 AST_APP_ARG(options); 00919 ); 00920 char *opts[OPT_ARG_ARRAY_SIZE]; 00921 00922 data = ast_strdupa(data); 00923 AST_STANDARD_APP_ARGS(args, data); 00924 00925 if (args.spec && !strcmp(args.spec, "all")) 00926 args.spec = NULL; 00927 00928 if (args.options) { 00929 ast_app_parse_options(spy_opts, &flags, opts, args.options); 00930 if (ast_test_flag(&flags, OPTION_GROUP)) 00931 mygroup = opts[OPT_ARG_GROUP]; 00932 00933 if (ast_test_flag(&flags, OPTION_RECORD) && 00934 !(recbase = opts[OPT_ARG_RECORD])) 00935 recbase = "chanspy"; 00936 00937 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { 00938 int vol; 00939 00940 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4)) 00941 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); 00942 else 00943 volfactor = vol; 00944 } 00945 00946 if (ast_test_flag(&flags, OPTION_PRIVATE)) 00947 ast_set_flag(&flags, OPTION_WHISPER); 00948 00949 if (ast_test_flag(&flags, OPTION_ENFORCED)) 00950 myenforced = opts[OPT_ARG_ENFORCED]; 00951 00952 if (ast_test_flag(&flags, OPTION_NAME)) { 00953 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) { 00954 char *delimiter; 00955 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) { 00956 mailbox = opts[OPT_ARG_NAME]; 00957 *delimiter++ = '\0'; 00958 name_context = delimiter; 00959 } else { 00960 mailbox = opts[OPT_ARG_NAME]; 00961 } 00962 } 00963 } 00964 00965 00966 } else 00967 ast_clear_flag(&flags, AST_FLAGS_ALL); 00968 00969 oldwf = chan->writeformat; 00970 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { 00971 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00972 return -1; 00973 } 00974 00975 if (recbase) { 00976 char filename[PATH_MAX]; 00977 00978 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); 00979 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) { 00980 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); 00981 fd = 0; 00982 } 00983 } 00984 00985 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context); 00986 00987 if (fd) 00988 close(fd); 00989 00990 if (oldwf && ast_set_write_format(chan, oldwf) < 0) 00991 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 00992 00993 return res; 00994 }
| 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 636 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, chanspy_ds::lock, ast_channel::macrocontext, 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(), strcasestr(), strsep(), and chanspy_ds::unique_id.
Referenced by chanspy_exec(), and extenspy_exec().
00640 { 00641 char nameprefix[AST_NAME_STRLEN]; 00642 char peer_name[AST_NAME_STRLEN + 5]; 00643 char exitcontext[AST_MAX_CONTEXT] = ""; 00644 signed char zero_volume = 0; 00645 int waitms; 00646 int res; 00647 char *ptr; 00648 int num; 00649 int num_spyed_upon = 1; 00650 struct chanspy_ds chanspy_ds = { 0, }; 00651 00652 if (ast_test_flag(flags, OPTION_EXIT)) { 00653 const char *c; 00654 ast_channel_lock(chan); 00655 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) { 00656 ast_copy_string(exitcontext, c, sizeof(exitcontext)); 00657 } else if (!ast_strlen_zero(chan->macrocontext)) { 00658 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext)); 00659 } else { 00660 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext)); 00661 } 00662 ast_channel_unlock(chan); 00663 } 00664 00665 ast_mutex_init(&chanspy_ds.lock); 00666 00667 snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1)); 00668 00669 if (chan->_state != AST_STATE_UP) 00670 ast_answer(chan); 00671 00672 ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ 00673 00674 waitms = 100; 00675 00676 for (;;) { 00677 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL; 00678 struct ast_channel *prev = NULL, *peer = NULL; 00679 00680 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { 00681 res = ast_streamfile(chan, "beep", chan->language); 00682 if (!res) 00683 res = ast_waitstream(chan, ""); 00684 else if (res < 0) { 00685 ast_clear_flag(chan, AST_FLAG_SPYING); 00686 break; 00687 } 00688 if (!ast_strlen_zero(exitcontext)) { 00689 char tmp[2]; 00690 tmp[0] = res; 00691 tmp[1] = '\0'; 00692 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) 00693 goto exit; 00694 else 00695 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); 00696 } 00697 } 00698 00699 res = ast_waitfordigit(chan, waitms); 00700 if (res < 0) { 00701 ast_clear_flag(chan, AST_FLAG_SPYING); 00702 break; 00703 } 00704 if (!ast_strlen_zero(exitcontext)) { 00705 char tmp[2]; 00706 tmp[0] = res; 00707 tmp[1] = '\0'; 00708 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) 00709 goto exit; 00710 else 00711 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); 00712 } 00713 00714 /* reset for the next loop around, unless overridden later */ 00715 waitms = 100; 00716 num_spyed_upon = 0; 00717 00718 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds); 00719 peer_chanspy_ds; 00720 chanspy_ds_free(peer_chanspy_ds), prev = peer, 00721 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 00722 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) { 00723 int igrp = !mygroup; 00724 int ienf = !myenforced; 00725 char *s; 00726 00727 peer = peer_chanspy_ds->chan; 00728 00729 ast_mutex_unlock(&peer_chanspy_ds->lock); 00730 00731 if (peer == prev) { 00732 ast_channel_unlock(peer); 00733 chanspy_ds_free(peer_chanspy_ds); 00734 break; 00735 } 00736 00737 if (ast_check_hangup(chan)) { 00738 ast_channel_unlock(peer); 00739 chanspy_ds_free(peer_chanspy_ds); 00740 break; 00741 } 00742 00743 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) { 00744 ast_channel_unlock(peer); 00745 continue; 00746 } 00747 00748 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) { 00749 ast_channel_unlock(peer); 00750 continue; 00751 } 00752 00753 if (mygroup) { 00754 int num_groups = 0; 00755 int num_mygroups = 0; 00756 char dup_group[512]; 00757 char dup_mygroup[512]; 00758 char *groups[NUM_SPYGROUPS]; 00759 char *mygroups[NUM_SPYGROUPS]; 00760 const char *group; 00761 int x; 00762 int y; 00763 ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup)); 00764 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups, 00765 ARRAY_LEN(mygroups)); 00766 00767 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) { 00768 ast_copy_string(dup_group, group, sizeof(dup_group)); 00769 num_groups = ast_app_separate_args(dup_group, ':', groups, 00770 ARRAY_LEN(groups)); 00771 } 00772 00773 for (y = 0; y < num_mygroups; y++) { 00774 for (x = 0; x < num_groups; x++) { 00775 if (!strcmp(mygroups[y], groups[x])) { 00776 igrp = 1; 00777 break; 00778 } 00779 } 00780 } 00781 } 00782 00783 if (!igrp) { 00784 ast_channel_unlock(peer); 00785 continue; 00786 } 00787 00788 if (myenforced) { 00789 char ext[AST_CHANNEL_NAME + 3]; 00790 char buffer[512]; 00791 char *end; 00792 00793 snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced); 00794 00795 ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1); 00796 if ((end = strchr(ext, '-'))) { 00797 *end++ = ':'; 00798 *end = '\0'; 00799 } 00800 00801 ext[0] = ':'; 00802 00803 if (strcasestr(buffer, ext)) { 00804 ienf = 1; 00805 } 00806 } 00807 00808 if (!ienf) { 00809 continue; 00810 } 00811 00812 strcpy(peer_name, "spy-"); 00813 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1); 00814 ptr = strchr(peer_name, '/'); 00815 *ptr++ = '\0'; 00816 ptr = strsep(&ptr, "-"); 00817 00818 for (s = peer_name; s < ptr; s++) 00819 *s = tolower(*s); 00820 /* We have to unlock the peer channel here to avoid a deadlock. 00821 * So, when we need to dereference it again, we have to lock the 00822 * datastore and get the pointer from there to see if the channel 00823 * is still valid. */ 00824 ast_channel_unlock(peer); 00825 00826 if (!ast_test_flag(flags, OPTION_QUIET)) { 00827 if (ast_test_flag(flags, OPTION_NAME)) { 00828 const char *local_context = S_OR(name_context, "default"); 00829 const char *local_mailbox = S_OR(mailbox, ptr); 00830 res = ast_app_sayname(chan, local_mailbox, local_context); 00831 } 00832 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) { 00833 if (!ast_test_flag(flags, OPTION_NOTECH)) { 00834 if (ast_fileexists(peer_name, NULL, NULL) != -1) { 00835 res = ast_streamfile(chan, peer_name, chan->language); 00836 if (!res) { 00837 res = ast_waitstream(chan, ""); 00838 } 00839 if (res) { 00840 chanspy_ds_free(peer_chanspy_ds); 00841 break; 00842 } 00843 } else { 00844 res = ast_say_character_str(chan, peer_name, "", chan->language); 00845 } 00846 } 00847 if ((num = atoi(ptr))) 00848 ast_say_digits(chan, atoi(ptr), "", chan->language); 00849 } 00850 } 00851 00852 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext); 00853 num_spyed_upon++; 00854 00855 if (res == -1) { 00856 chanspy_ds_free(peer_chanspy_ds); 00857 goto exit; 00858 } else if (res == -2) { 00859 res = 0; 00860 chanspy_ds_free(peer_chanspy_ds); 00861 goto exit; 00862 } else if (res > 1 && spec) { 00863 struct ast_channel *next; 00864 00865 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); 00866 00867 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) { 00868 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds); 00869 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds); 00870 } else { 00871 /* stay on this channel, if it is still valid */ 00872 00873 ast_mutex_lock(&peer_chanspy_ds->lock); 00874 if (peer_chanspy_ds->chan) { 00875 ast_channel_lock(peer_chanspy_ds->chan); 00876 next_chanspy_ds = peer_chanspy_ds; 00877 peer_chanspy_ds = NULL; 00878 } else { 00879 /* the channel is gone */ 00880 ast_mutex_unlock(&peer_chanspy_ds->lock); 00881 next_chanspy_ds = NULL; 00882 } 00883 } 00884 00885 peer = NULL; 00886 } 00887 } 00888 if (res == -1 || ast_check_hangup(chan)) 00889 break; 00890 } 00891 exit: 00892 00893 ast_clear_flag(chan, AST_FLAG_SPYING); 00894 00895 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); 00896 00897 ast_mutex_lock(&chanspy_ds.lock); 00898 ast_mutex_unlock(&chanspy_ds.lock); 00899 ast_mutex_destroy(&chanspy_ds.lock); 00900 00901 return res; 00902 }
| static int extenspy_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 996 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_FILE_MODE, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_set_flag, ast_set_write_format(), AST_STANDARD_APP_ARGS, ast_strdupa, 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().
00997 { 00998 char *ptr, *exten = NULL; 00999 char *mygroup = NULL; 01000 char *recbase = NULL; 01001 int fd = 0; 01002 struct ast_flags flags; 01003 int oldwf = 0; 01004 int volfactor = 0; 01005 int res; 01006 char *mailbox = NULL; 01007 char *name_context = NULL; 01008 AST_DECLARE_APP_ARGS(args, 01009 AST_APP_ARG(context); 01010 AST_APP_ARG(options); 01011 ); 01012 01013 data = ast_strdupa(data); 01014 01015 AST_STANDARD_APP_ARGS(args, data); 01016 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) { 01017 exten = args.context; 01018 *ptr++ = '\0'; 01019 args.context = ptr; 01020 } 01021 01022 if (ast_strlen_zero(args.context)) 01023 args.context = ast_strdupa(chan->context); 01024 01025 if (args.options) { 01026 char *opts[OPT_ARG_ARRAY_SIZE]; 01027 01028 ast_app_parse_options(spy_opts, &flags, opts, args.options); 01029 if (ast_test_flag(&flags, OPTION_GROUP)) 01030 mygroup = opts[OPT_ARG_GROUP]; 01031 01032 if (ast_test_flag(&flags, OPTION_RECORD) && 01033 !(recbase = opts[OPT_ARG_RECORD])) 01034 recbase = "chanspy"; 01035 01036 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { 01037 int vol; 01038 01039 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4)) 01040 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); 01041 else 01042 volfactor = vol; 01043 } 01044 01045 if (ast_test_flag(&flags, OPTION_PRIVATE)) 01046 ast_set_flag(&flags, OPTION_WHISPER); 01047 01048 01049 if (ast_test_flag(&flags, OPTION_NAME)) { 01050 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) { 01051 char *delimiter; 01052 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) { 01053 mailbox = opts[OPT_ARG_NAME]; 01054 *delimiter++ = '\0'; 01055 name_context = delimiter; 01056 } else { 01057 mailbox = opts[OPT_ARG_NAME]; 01058 } 01059 } 01060 } 01061 01062 } else 01063 ast_clear_flag(&flags, AST_FLAGS_ALL); 01064 01065 oldwf = chan->writeformat; 01066 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { 01067 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 01068 return -1; 01069 } 01070 01071 if (recbase) { 01072 char filename[PATH_MAX]; 01073 01074 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); 01075 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) { 01076 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); 01077 fd = 0; 01078 } 01079 } 01080 01081 01082 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context); 01083 01084 if (fd) 01085 close(fd); 01086 01087 if (oldwf && ast_set_write_format(chan, oldwf) < 0) 01088 ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); 01089 01090 return res; 01091 }
| static int load_module | ( | void | ) | [static] |
Definition at line 1103 of file app_chanspy.c.
References ast_register_application, chanspy_exec(), and extenspy_exec().
01104 { 01105 int res = 0; 01106 01107 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan); 01108 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext); 01109 01110 return res; 01111 }
| 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 605 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(), and setup_chanspy_ds().
Referenced by common_exec().
00608 { 00609 struct ast_channel *next; 00610 const size_t pseudo_len = strlen("DAHDI/pseudo"); 00611 00612 redo: 00613 if (!ast_strlen_zero(spec)) 00614 next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec)); 00615 else if (!ast_strlen_zero(exten)) 00616 next = ast_walk_channel_by_exten_locked(last, exten, context); 00617 else 00618 next = ast_channel_walk_locked(last); 00619 00620 if (!next) 00621 return NULL; 00622 00623 if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) { 00624 last = next; 00625 ast_channel_unlock(next); 00626 goto redo; 00627 } else if (next == chan) { 00628 last = next; 00629 ast_channel_unlock(next); 00630 goto redo; 00631 } 00632 00633 return setup_chanspy_ds(next, chanspy_ds); 00634 }
| static struct chanspy_ds* setup_chanspy_ds | ( | struct ast_channel * | chan, | |
| struct chanspy_ds * | chanspy_ds | |||
| ) | [static, read] |
Definition at line 585 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().
00586 { 00587 struct ast_datastore *datastore = NULL; 00588 00589 ast_mutex_lock(&chanspy_ds->lock); 00590 00591 if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) { 00592 ast_mutex_unlock(&chanspy_ds->lock); 00593 chanspy_ds = chanspy_ds_free(chanspy_ds); 00594 ast_channel_unlock(chan); 00595 return NULL; 00596 } 00597 00598 chanspy_ds->chan = chan; 00599 datastore->data = chanspy_ds; 00600 ast_channel_datastore_add(chan, datastore); 00601 00602 return chanspy_ds; 00603 }
| static void* spy_alloc | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 233 of file app_chanspy.c.
00234 { 00235 /* just store the data pointer in the channel structure */ 00236 return data; 00237 }
| static int spy_generate | ( | struct ast_channel * | chan, | |
| void * | data, | |||
| int | len, | |||
| int | samples | |||
| ) | [static] |
Definition at line 244 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, LOG_WARNING, ast_frame::ptr, chanspy_translation_helper::spy_audiohook, and ast_audiohook::status.
00245 { 00246 struct chanspy_translation_helper *csth = data; 00247 struct ast_frame *f, *cur; 00248 00249 ast_audiohook_lock(&csth->spy_audiohook); 00250 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) { 00251 /* Channel is already gone more than likely */ 00252 ast_audiohook_unlock(&csth->spy_audiohook); 00253 return -1; 00254 } 00255 00256 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR); 00257 00258 ast_audiohook_unlock(&csth->spy_audiohook); 00259 00260 if (!f) 00261 return 0; 00262 00263 for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) { 00264 if (ast_write(chan, cur)) { 00265 ast_frfree(f); 00266 return -1; 00267 } 00268 00269 if (csth->fd) { 00270 if (write(csth->fd, cur->data.ptr, cur->datalen) < 0) { 00271 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); 00272 } 00273 } 00274 } 00275 00276 ast_frfree(f); 00277 00278 return 0; 00279 }
| static void spy_release | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 239 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 287 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, and LOG_NOTICE.
Referenced by channel_spy().
00288 { 00289 int res = 0; 00290 struct ast_channel *peer = NULL; 00291 00292 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name); 00293 00294 ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE); 00295 res = ast_audiohook_attach(chan, audiohook); 00296 00297 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 00298 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); 00299 } 00300 return res; 00301 }
| static int unload_module | ( | void | ) | [static] |
Definition at line 1093 of file app_chanspy.c.
References ast_unregister_application().
01094 { 01095 int res = 0; 01096 01097 res |= ast_unregister_application(app_chan); 01098 res |= ast_unregister_application(app_ext); 01099 01100 return res; 01101 }
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 = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, } [static] |
Definition at line 1113 of file app_chanspy.c.
const char* app_chan = "ChanSpy" [static] |
Definition at line 57 of file app_chanspy.c.
const char* app_ext = "ExtenSpy" [static] |
Definition at line 122 of file app_chanspy.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 1113 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 547 of file app_chanspy.c.
| enum { ... } chanspy_opt_args |
| enum { ... } chanspy_opt_flags |
const char* desc_chan [static] |
Definition at line 58 of file app_chanspy.c.
const char* desc_ext [static] |
Definition at line 123 of file app_chanspy.c.
int next_unique_id_to_use = 0 [static] |
Definition at line 222 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 220 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 281 of file app_chanspy.c.
const char* tdesc = "Listen to a channel, and optionally whisper into it" [static] |
Definition at line 56 of file app_chanspy.c.
1.6.1