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