Thu Jul 9 13:40:44 2009

Asterisk developer's documentation


app_chanspy.c File Reference

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

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)
}
enum  {
  OPT_ARG_VOLUME = 0, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_ENFORCED,
  OPT_ARG_ARRAY_SIZE
}

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int channel_spy (struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, int *volfactor, int fd, const struct ast_flags *flags, 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_dschanspy_ds_free (struct chanspy_ds *chanspy_ds)
static int chanspy_exec (struct ast_channel *chan, void *data)
static int common_exec (struct ast_channel *chan, const struct ast_flags *flags, int volfactor, const int fd, const char *mygroup, const char *myenforced, const char *spec, const char *exten, const char *context)
static int extenspy_exec (struct ast_channel *chan, void *data)
static int load_module (void)
static struct chanspy_dsnext_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_dssetup_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 = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, }
static const char * app_chan = "ChanSpy"
static const char * app_ext = "ExtenSpy"
static const struct ast_module_infoast_module_info = &__mod_info
static struct ast_datastore_info chanspy_ds_info
enum { ... }  chanspy_opt_args
enum { ... }  chanspy_opt_flags
static const char * desc_chan
static const char * desc_ext
static int next_unique_id_to_use = 0
static struct ast_app_option spy_opts [128] = { [ 'q' ] = { .flag = OPTION_QUIET }, [ 'b' ] = { .flag = OPTION_BRIDGED }, [ 'w' ] = { .flag = OPTION_WHISPER }, [ 'W' ] = { .flag = OPTION_PRIVATE }, [ 'v' ] = { .flag = OPTION_VOLUME , .arg_index = OPT_ARG_VOLUME + 1 }, [ 'g' ] = { .flag = OPTION_GROUP , .arg_index = OPT_ARG_GROUP + 1 }, [ 'r' ] = { .flag = OPTION_RECORD , .arg_index = OPT_ARG_RECORD + 1 }, [ 'e' ] = { .flag = OPTION_ENFORCED , .arg_index = OPT_ARG_ENFORCED + 1 }, [ 'o' ] = { .flag = OPTION_READONLY }, [ 'X' ] = { .flag = OPTION_EXIT },}
static struct ast_generator spygen
static const char * tdesc = "Listen to a channel, and optionally whisper into it"


Detailed Description

ChanSpy: Listen in on any channel.

Author:
Anthony Minessale II <anthmct@yahoo.com>

Joshua Colp <jcolp@digium.com>

Russell Bryant <russell@digium.com>

Definition in file app_chanspy.c.


Define Documentation

#define AST_NAME_STRLEN   256

Definition at line 53 of file app_chanspy.c.

Referenced by common_exec().


Enumeration Type Documentation

anonymous enum

Enumerator:
OPTION_QUIET 
OPTION_BRIDGED 
OPTION_VOLUME 
OPTION_GROUP 
OPTION_RECORD 
OPTION_WHISPER 
OPTION_PRIVATE 
OPTION_READONLY 
OPTION_EXIT 
OPTION_ENFORCED 

Definition at line 137 of file app_chanspy.c.

00137      {
00138    OPTION_QUIET     = (1 << 0),    /* Quiet, no announcement */
00139    OPTION_BRIDGED   = (1 << 1),    /* Only look at bridged calls */
00140    OPTION_VOLUME    = (1 << 2),    /* Specify initial volume */
00141    OPTION_GROUP     = (1 << 3),    /* Only look at channels in group */
00142    OPTION_RECORD    = (1 << 4),
00143    OPTION_WHISPER   = (1 << 5),
00144    OPTION_PRIVATE   = (1 << 6),    /* Private Whisper mode */
00145    OPTION_READONLY  = (1 << 7),    /* Don't mix the two channels */
00146    OPTION_EXIT      = (1 << 8),    /* Exit to a valid single digit extension */
00147    OPTION_ENFORCED  = (1 << 9),    /* Enforced mode */
00148 } chanspy_opt_flags;

anonymous enum

Enumerator:
OPT_ARG_VOLUME 
OPT_ARG_GROUP 
OPT_ARG_RECORD 
OPT_ARG_ENFORCED 
OPT_ARG_ARRAY_SIZE 

Definition at line 150 of file app_chanspy.c.

00150      {
00151    OPT_ARG_VOLUME = 0,
00152    OPT_ARG_GROUP,
00153    OPT_ARG_RECORD,
00154    OPT_ARG_ENFORCED,
00155    OPT_ARG_ARRAY_SIZE,
00156 } chanspy_opt_args;


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 954 of file app_chanspy.c.

static void __unreg_module ( void   )  [static]

Definition at line 954 of file app_chanspy.c.

static int channel_spy ( struct ast_channel chan,
struct chanspy_ds spyee_chanspy_ds,
int *  volfactor,
int  fd,
const struct ast_flags flags,
char *  exitcontext 
) [static]

Definition at line 257 of file app_chanspy.c.

References ast_activate_generator(), ast_audiohook_destroy(), ast_audiohook_detach(), AST_AUDIOHOOK_DIRECTION_WRITE, ast_audiohook_init(), ast_audiohook_lock, AST_AUDIOHOOK_STATUS_RUNNING, AST_AUDIOHOOK_TYPE_SPY, AST_AUDIOHOOK_TYPE_WHISPER, ast_audiohook_unlock, ast_audiohook_write_frame(), ast_channel_lock, ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_channel_unlock, ast_check_hangup(), ast_deactivate_generator(), ast_debug, AST_FRAME_DTMF, AST_FRAME_VOICE, ast_frfree, ast_goto_if_exists(), ast_mutex_lock(), ast_mutex_unlock(), ast_read(), ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_verb, ast_waitfor(), chanspy_ds::chan, chan, f, ast_channel::flags, chanspy_ds::lock, ast_channel::name, name, OPTION_EXIT, OPTION_PRIVATE, OPTION_WHISPER, pbx_builtin_setvar_helper(), spygen, and start_spying().

Referenced by common_exec().

00259 {
00260    struct chanspy_translation_helper csth;
00261    int running = 0, res, x = 0;
00262    char inp[24] = {0};
00263    char *name;
00264    struct ast_frame *f;
00265    struct ast_silence_generator *silgen = NULL;
00266    struct ast_channel *spyee = NULL;
00267    const char *spyer_name;
00268 
00269    ast_channel_lock(chan);
00270    spyer_name = ast_strdupa(chan->name);
00271    ast_channel_unlock(chan);
00272 
00273    ast_mutex_lock(&spyee_chanspy_ds->lock);
00274    if (spyee_chanspy_ds->chan) {
00275       spyee = spyee_chanspy_ds->chan;
00276       ast_channel_lock(spyee);
00277    }
00278    ast_mutex_unlock(&spyee_chanspy_ds->lock);
00279 
00280    if (!spyee)
00281       return 0;
00282 
00283    /* We now hold the channel lock on spyee */
00284 
00285    if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
00286       ast_channel_unlock(spyee);
00287       return 0;
00288    }
00289 
00290    name = ast_strdupa(spyee->name);
00291    ast_verb(2, "Spying on channel %s\n", name);
00292 
00293    memset(&csth, 0, sizeof(csth));
00294 
00295    ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
00296 
00297    if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
00298       ast_audiohook_destroy(&csth.spy_audiohook);
00299       ast_channel_unlock(spyee);
00300       return 0;
00301    }
00302 
00303    if (ast_test_flag(flags, OPTION_WHISPER)) {
00304       ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
00305       start_spying(spyee, spyer_name, &csth.whisper_audiohook);
00306    }
00307 
00308    ast_channel_unlock(spyee);
00309    spyee = NULL;
00310 
00311    csth.volfactor = *volfactor;
00312 
00313    if (csth.volfactor) {
00314       csth.spy_audiohook.options.read_volume = csth.volfactor;
00315       csth.spy_audiohook.options.write_volume = csth.volfactor;
00316    }
00317 
00318    csth.fd = fd;
00319 
00320    if (ast_test_flag(flags, OPTION_PRIVATE))
00321       silgen = ast_channel_start_silence_generator(chan);
00322    else
00323       ast_activate_generator(chan, &spygen, &csth);
00324 
00325    /* We can no longer rely on 'spyee' being an actual channel;
00326       it can be hung up and freed out from under us. However, the
00327       channel destructor will put NULL into our csth.spy.chan
00328       field when that happens, so that is our signal that the spyee
00329       channel has gone away.
00330    */
00331 
00332    /* Note: it is very important that the ast_waitfor() be the first
00333       condition in this expression, so that if we wait for some period
00334       of time before receiving a frame from our spying channel, we check
00335       for hangup on the spied-on channel _after_ knowing that a frame
00336       has arrived, since the spied-on channel could have gone away while
00337       we were waiting
00338    */
00339    while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00340       if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
00341          running = -1;
00342          break;
00343       }
00344 
00345       if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
00346          ast_audiohook_lock(&csth.whisper_audiohook);
00347          ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00348          ast_audiohook_unlock(&csth.whisper_audiohook);
00349          ast_frfree(f);
00350          continue;
00351       }
00352       
00353       res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
00354       ast_frfree(f);
00355       if (!res)
00356          continue;
00357 
00358       if (x == sizeof(inp))
00359          x = 0;
00360 
00361       if (res < 0) {
00362          running = -1;
00363          break;
00364       }
00365 
00366       if (ast_test_flag(flags, OPTION_EXIT)) {
00367          char tmp[2];
00368          tmp[0] = res;
00369          tmp[1] = '\0';
00370          if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
00371             ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
00372             pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
00373             running = -2;
00374             break;
00375          } else {
00376             ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00377          }
00378       } else if (res >= '0' && res <= '9') {
00379          inp[x++] = res;
00380       }
00381 
00382       if (res == '*') {
00383          running = 0;
00384          break;
00385       } else if (res == '#') {
00386          if (!ast_strlen_zero(inp)) {
00387             running = atoi(inp);
00388             break;
00389          }
00390 
00391          (*volfactor)++;
00392          if (*volfactor > 4)
00393             *volfactor = -4;
00394          ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
00395 
00396          csth.volfactor = *volfactor;
00397          csth.spy_audiohook.options.read_volume = csth.volfactor;
00398          csth.spy_audiohook.options.write_volume = csth.volfactor;
00399       }
00400    }
00401 
00402    if (ast_test_flag(flags, OPTION_PRIVATE))
00403       ast_channel_stop_silence_generator(chan, silgen);
00404    else
00405       ast_deactivate_generator(chan);
00406 
00407    if (ast_test_flag(flags, OPTION_WHISPER)) {
00408       ast_audiohook_lock(&csth.whisper_audiohook);
00409       ast_audiohook_detach(&csth.whisper_audiohook);
00410       ast_audiohook_unlock(&csth.whisper_audiohook);
00411       ast_audiohook_destroy(&csth.whisper_audiohook);
00412    }
00413 
00414    ast_audiohook_lock(&csth.spy_audiohook);
00415    ast_audiohook_detach(&csth.spy_audiohook);
00416    ast_audiohook_unlock(&csth.spy_audiohook);
00417    ast_audiohook_destroy(&csth.spy_audiohook);
00418    
00419    ast_verb(2, "Done Spying on channel %s\n", name);
00420 
00421    return running;
00422 }

static void chanspy_ds_chan_fixup ( void *  data,
struct ast_channel old_chan,
struct ast_channel new_chan 
) [static]

Definition at line 441 of file app_chanspy.c.

References ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, and chanspy_ds::lock.

00442 {
00443    struct chanspy_ds *chanspy_ds = data;
00444    
00445    ast_mutex_lock(&chanspy_ds->lock);
00446    chanspy_ds->chan = new_chan;
00447    ast_mutex_unlock(&chanspy_ds->lock);
00448 }

static void chanspy_ds_destroy ( void *  data  )  [static]

Note:
This relies on the embedded lock to be recursive, as it may be called due to a call to chanspy_ds_free with the lock held there.

Definition at line 428 of file app_chanspy.c.

References ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, and chanspy_ds::lock.

Referenced by chanspy_ds_free().

00429 {
00430    struct chanspy_ds *chanspy_ds = data;
00431 
00432    /* Setting chan to be NULL is an atomic operation, but we don't want this
00433     * value to change while this lock is held.  The lock is held elsewhere
00434     * while it performs non-atomic operations with this channel pointer */
00435 
00436    ast_mutex_lock(&chanspy_ds->lock);
00437    chanspy_ds->chan = NULL;
00438    ast_mutex_unlock(&chanspy_ds->lock);
00439 }

static struct chanspy_ds* chanspy_ds_free ( struct chanspy_ds chanspy_ds  )  [static]

Definition at line 456 of file app_chanspy.c.

References ast_channel_datastore_find(), ast_channel_datastore_free(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_unlock, ast_mutex_lock(), ast_mutex_unlock(), 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().

00457 {
00458    if (!chanspy_ds)
00459       return NULL;
00460 
00461    ast_mutex_lock(&chanspy_ds->lock);
00462    if (chanspy_ds->chan) {
00463       struct ast_datastore *datastore;
00464       struct ast_channel *chan;
00465 
00466       chan = chanspy_ds->chan;
00467 
00468       ast_channel_lock(chan);
00469       if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
00470          ast_channel_datastore_remove(chan, datastore);
00471          /* chanspy_ds->chan is NULL after this call */
00472          chanspy_ds_destroy(datastore->data);
00473          datastore->data = NULL;
00474          ast_channel_datastore_free(datastore);
00475       }
00476       ast_channel_unlock(chan);
00477    }
00478    ast_mutex_unlock(&chanspy_ds->lock);
00479 
00480    return NULL;
00481 }

static int chanspy_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 778 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_test_flag, chan, common_exec(), ast_flags::flags, LOG_ERROR, LOG_NOTICE, LOG_WARNING, OPT_ARG_ARRAY_SIZE, OPT_ARG_ENFORCED, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_VOLUME, OPTION_ENFORCED, OPTION_GROUP, OPTION_PRIVATE, OPTION_RECORD, OPTION_VOLUME, OPTION_WHISPER, spy_opts, and ast_channel::writeformat.

Referenced by load_module().

00779 {
00780    char *myenforced = NULL;
00781    char *mygroup = NULL;
00782    char *recbase = NULL;
00783    int fd = 0;
00784    struct ast_flags flags;
00785    int oldwf = 0;
00786    int volfactor = 0;
00787    int res;
00788    AST_DECLARE_APP_ARGS(args,
00789       AST_APP_ARG(spec);
00790       AST_APP_ARG(options);
00791    );
00792    char *opts[OPT_ARG_ARRAY_SIZE];
00793 
00794    data = ast_strdupa(data);
00795    AST_STANDARD_APP_ARGS(args, data);
00796 
00797    if (args.spec && !strcmp(args.spec, "all"))
00798       args.spec = NULL;
00799 
00800    if (args.options) {
00801       ast_app_parse_options(spy_opts, &flags, opts, args.options);
00802       if (ast_test_flag(&flags, OPTION_GROUP))
00803          mygroup = opts[OPT_ARG_GROUP];
00804 
00805       if (ast_test_flag(&flags, OPTION_RECORD) &&
00806          !(recbase = opts[OPT_ARG_RECORD]))
00807          recbase = "chanspy";
00808 
00809       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00810          int vol;
00811 
00812          if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
00813             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00814          else
00815             volfactor = vol;
00816       }
00817 
00818       if (ast_test_flag(&flags, OPTION_PRIVATE))
00819          ast_set_flag(&flags, OPTION_WHISPER);
00820 
00821       if (ast_test_flag(&flags, OPTION_ENFORCED))
00822          myenforced = opts[OPT_ARG_ENFORCED];
00823 
00824    } else
00825       ast_clear_flag(&flags, AST_FLAGS_ALL);
00826 
00827    oldwf = chan->writeformat;
00828    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00829       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00830       return -1;
00831    }
00832 
00833    if (recbase) {
00834       char filename[PATH_MAX];
00835 
00836       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
00837       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
00838          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
00839          fd = 0;
00840       }
00841    }
00842 
00843    res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL);
00844 
00845    if (fd)
00846       close(fd);
00847 
00848    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
00849       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00850 
00851    return res;
00852 }

static int common_exec ( struct ast_channel chan,
const struct ast_flags flags,
int  volfactor,
const int  fd,
const char *  mygroup,
const char *  myenforced,
const char *  spec,
const char *  exten,
const char *  context 
) [static]

Definition at line 536 of file app_chanspy.c.

References ast_channel::_state, ARRAY_LEN, ast_answer(), 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, OPTION_BRIDGED, OPTION_EXIT, OPTION_QUIET, pbx_builtin_getvar_helper(), s, setup_chanspy_ds(), strcasestr(), and chanspy_ds::unique_id.

Referenced by chanspy_exec(), and extenspy_exec().

00539 {
00540    char nameprefix[AST_NAME_STRLEN];
00541    char peer_name[AST_NAME_STRLEN + 5];
00542    char exitcontext[AST_MAX_CONTEXT] = "";
00543    signed char zero_volume = 0;
00544    int waitms;
00545    int res;
00546    char *ptr;
00547    int num;
00548    int num_spyed_upon = 1;
00549    struct chanspy_ds chanspy_ds = { 0, };
00550 
00551    if (ast_test_flag(flags, OPTION_EXIT)) {
00552       const char *c;
00553       if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT")))
00554          ast_copy_string(exitcontext, c, sizeof(exitcontext));
00555       else if (!ast_strlen_zero(chan->macrocontext))
00556          ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
00557       else
00558          ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
00559    }
00560 
00561    ast_mutex_init(&chanspy_ds.lock);
00562 
00563    snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
00564 
00565    if (chan->_state != AST_STATE_UP)
00566       ast_answer(chan);
00567 
00568    ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
00569 
00570    waitms = 100;
00571 
00572    for (;;) {
00573       struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
00574       struct ast_channel *prev = NULL, *peer = NULL;
00575 
00576       if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
00577          res = ast_streamfile(chan, "beep", chan->language);
00578          if (!res)
00579             res = ast_waitstream(chan, "");
00580          else if (res < 0) {
00581             ast_clear_flag(chan, AST_FLAG_SPYING);
00582             break;
00583          }
00584          if (!ast_strlen_zero(exitcontext)) {
00585             char tmp[2];
00586             tmp[0] = res;
00587             tmp[1] = '\0';
00588             if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00589                goto exit;
00590             else
00591                ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00592          }
00593       }
00594 
00595       res = ast_waitfordigit(chan, waitms);
00596       if (res < 0) {
00597          ast_clear_flag(chan, AST_FLAG_SPYING);
00598          break;
00599       }
00600       if (!ast_strlen_zero(exitcontext)) {
00601          char tmp[2];
00602          tmp[0] = res;
00603          tmp[1] = '\0';
00604          if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00605             goto exit;
00606          else
00607             ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00608       }
00609 
00610       /* reset for the next loop around, unless overridden later */
00611       waitms = 100;
00612       num_spyed_upon = 0;
00613 
00614       for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
00615            peer_chanspy_ds;
00616           chanspy_ds_free(peer_chanspy_ds), prev = peer,
00617            peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 
00618             next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
00619          int igrp = !mygroup;
00620          int ienf = !myenforced;
00621          char *s;
00622 
00623          peer = peer_chanspy_ds->chan;
00624 
00625          ast_mutex_unlock(&peer_chanspy_ds->lock);
00626 
00627          if (peer == prev) {
00628             ast_channel_unlock(peer);
00629             chanspy_ds_free(peer_chanspy_ds);
00630             break;
00631          }
00632 
00633          if (ast_check_hangup(chan)) {
00634             ast_channel_unlock(peer);
00635             chanspy_ds_free(peer_chanspy_ds);
00636             break;
00637          }
00638 
00639          if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
00640             ast_channel_unlock(peer);
00641             continue;
00642          }
00643 
00644          if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
00645             ast_channel_unlock(peer);
00646             continue;
00647          }
00648 
00649          if (mygroup) {
00650             int num_groups = 0;
00651             char dup_group[512];
00652             char *groups[25];
00653             const char *group;
00654             int x;
00655             if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
00656                ast_copy_string(dup_group, group, sizeof(dup_group));
00657                num_groups = ast_app_separate_args(dup_group, ':', groups,
00658                   ARRAY_LEN(groups));
00659             }
00660 
00661             for (x = 0; x < num_groups; x++) {
00662                if (!strcmp(mygroup, groups[x])) {
00663                   igrp = 1;
00664                   break;
00665                }
00666             }
00667          }
00668 
00669          if (!igrp) {
00670             ast_channel_unlock(peer);
00671             continue;
00672          }
00673 
00674          if (myenforced) {
00675             char ext[AST_CHANNEL_NAME + 3];
00676             char buffer[512];
00677             char *end;
00678 
00679             snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
00680 
00681             ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1);
00682             if ((end = strchr(ext, '-'))) {
00683                *end++ = ':';
00684                *end = '\0';
00685             }
00686 
00687             ext[0] = ':';
00688 
00689             if (strcasestr(buffer, ext)) {
00690                ienf = 1;
00691             }
00692          }
00693 
00694          if (!ienf) {
00695             continue;
00696          }
00697 
00698          strcpy(peer_name, "spy-");
00699          strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
00700          ptr = strchr(peer_name, '/');
00701          *ptr++ = '\0';
00702 
00703          for (s = peer_name; s < ptr; s++)
00704             *s = tolower(*s);
00705          /* We have to unlock the peer channel here to avoid a deadlock.
00706           * So, when we need to dereference it again, we have to lock the 
00707           * datastore and get the pointer from there to see if the channel 
00708           * is still valid. */
00709          ast_channel_unlock(peer);
00710 
00711          if (!ast_test_flag(flags, OPTION_QUIET)) {
00712             if (ast_fileexists(peer_name, NULL, NULL) != -1) {
00713                res = ast_streamfile(chan, peer_name, chan->language);
00714                if (!res)
00715                   res = ast_waitstream(chan, "");
00716                if (res) {
00717                   chanspy_ds_free(peer_chanspy_ds);
00718                   break;
00719                }
00720             } else
00721                res = ast_say_character_str(chan, peer_name, "", chan->language);
00722             if ((num = atoi(ptr)))
00723                ast_say_digits(chan, atoi(ptr), "", chan->language);
00724          }
00725 
00726          res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
00727          num_spyed_upon++; 
00728 
00729          if (res == -1) {
00730             chanspy_ds_free(peer_chanspy_ds);
00731             goto exit;
00732          } else if (res == -2) {
00733             res = 0;
00734             chanspy_ds_free(peer_chanspy_ds);
00735             goto exit;
00736          } else if (res > 1 && spec) {
00737             struct ast_channel *next;
00738 
00739             snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
00740 
00741             if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
00742                peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
00743                next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
00744             } else {
00745                /* stay on this channel, if it is still valid */
00746 
00747                ast_mutex_lock(&peer_chanspy_ds->lock);
00748                if (peer_chanspy_ds->chan) {
00749                   ast_channel_lock(peer_chanspy_ds->chan);
00750                   next_chanspy_ds = peer_chanspy_ds;
00751                   peer_chanspy_ds = NULL;
00752                } else {
00753                   /* the channel is gone */
00754                   ast_mutex_unlock(&peer_chanspy_ds->lock);
00755                   next_chanspy_ds = NULL;
00756                }
00757             }
00758 
00759             peer = NULL;
00760          }
00761       }
00762       if (res == -1 || ast_check_hangup(chan))
00763          break;
00764    }
00765 exit:
00766 
00767    ast_clear_flag(chan, AST_FLAG_SPYING);
00768 
00769    ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00770 
00771    ast_mutex_lock(&chanspy_ds.lock);
00772    ast_mutex_unlock(&chanspy_ds.lock);
00773    ast_mutex_destroy(&chanspy_ds.lock);
00774 
00775    return res;
00776 }

static int extenspy_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 854 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, OPT_ARG_ARRAY_SIZE, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_VOLUME, OPTION_GROUP, OPTION_PRIVATE, OPTION_RECORD, OPTION_VOLUME, OPTION_WHISPER, spy_opts, and ast_channel::writeformat.

Referenced by load_module().

00855 {
00856    char *ptr, *exten = NULL;
00857    char *mygroup = NULL;
00858    char *recbase = NULL;
00859    int fd = 0;
00860    struct ast_flags flags;
00861    int oldwf = 0;
00862    int volfactor = 0;
00863    int res;
00864    AST_DECLARE_APP_ARGS(args,
00865       AST_APP_ARG(context);
00866       AST_APP_ARG(options);
00867    );
00868 
00869    data = ast_strdupa(data);
00870 
00871    AST_STANDARD_APP_ARGS(args, data);
00872    if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
00873       exten = args.context;
00874       *ptr++ = '\0';
00875       args.context = ptr;
00876    }
00877 
00878    if (ast_strlen_zero(args.context))
00879       args.context = ast_strdupa(chan->context);
00880 
00881    if (args.options) {
00882       char *opts[OPT_ARG_ARRAY_SIZE];
00883 
00884       ast_app_parse_options(spy_opts, &flags, opts, args.options);
00885       if (ast_test_flag(&flags, OPTION_GROUP))
00886          mygroup = opts[OPT_ARG_GROUP];
00887 
00888       if (ast_test_flag(&flags, OPTION_RECORD) &&
00889          !(recbase = opts[OPT_ARG_RECORD]))
00890          recbase = "chanspy";
00891 
00892       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00893          int vol;
00894 
00895          if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
00896             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00897          else
00898             volfactor = vol;
00899       }
00900 
00901       if (ast_test_flag(&flags, OPTION_PRIVATE))
00902          ast_set_flag(&flags, OPTION_WHISPER);
00903    } else
00904       ast_clear_flag(&flags, AST_FLAGS_ALL);
00905 
00906    oldwf = chan->writeformat;
00907    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00908       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00909       return -1;
00910    }
00911 
00912    if (recbase) {
00913       char filename[PATH_MAX];
00914 
00915       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
00916       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
00917          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
00918          fd = 0;
00919       }
00920    }
00921 
00922 
00923    res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context);
00924 
00925    if (fd)
00926       close(fd);
00927 
00928    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
00929       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00930 
00931    return res;
00932 }

static int load_module ( void   )  [static]

Definition at line 944 of file app_chanspy.c.

References ast_register_application, chanspy_exec(), and extenspy_exec().

00945 {
00946    int res = 0;
00947 
00948    res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
00949    res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
00950 
00951    return res;
00952 }

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 504 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().

00507 {
00508    struct ast_channel *next;
00509    const size_t pseudo_len = strlen("DAHDI/pseudo");
00510 
00511 redo:
00512    if (!ast_strlen_zero(spec))
00513       next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
00514 
00515    else if (!ast_strlen_zero(exten))
00516       next = ast_walk_channel_by_exten_locked(last, exten, context);
00517    else
00518       next = ast_channel_walk_locked(last);
00519 
00520    if (!next)
00521       return NULL;
00522 
00523    if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
00524       last = next;
00525       ast_channel_unlock(next);
00526       goto redo;
00527    } else if (next == chan) {
00528       last = next;
00529       ast_channel_unlock(next);
00530       goto redo;
00531    }
00532 
00533    return setup_chanspy_ds(next, chanspy_ds);
00534 }

static struct chanspy_ds* setup_chanspy_ds ( struct ast_channel chan,
struct chanspy_ds chanspy_ds 
) [static]

Note:
Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked

Definition at line 484 of file app_chanspy.c.

References ast_channel_datastore_add(), ast_channel_datastore_alloc(), ast_channel_unlock, ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, 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().

00485 {
00486    struct ast_datastore *datastore = NULL;
00487 
00488    ast_mutex_lock(&chanspy_ds->lock);
00489 
00490    if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
00491       ast_mutex_unlock(&chanspy_ds->lock);
00492       chanspy_ds = chanspy_ds_free(chanspy_ds);
00493       ast_channel_unlock(chan);
00494       return NULL;
00495    }
00496    
00497    chanspy_ds->chan = chan;
00498    datastore->data = chanspy_ds;
00499    ast_channel_datastore_add(chan, datastore);
00500 
00501    return chanspy_ds;
00502 }

static void* spy_alloc ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 181 of file app_chanspy.c.

00182 {
00183    /* just store the data pointer in the channel structure */
00184    return data;
00185 }

static int spy_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 192 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(), chan, ast_frame::data, ast_frame::datalen, errno, f, chanspy_translation_helper::fd, ast_frame::frame_list, LOG_WARNING, chanspy_translation_helper::spy_audiohook, and ast_audiohook::status.

00193 {
00194    struct chanspy_translation_helper *csth = data;
00195    struct ast_frame *f, *cur;
00196 
00197    ast_audiohook_lock(&csth->spy_audiohook);
00198    if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00199       /* Channel is already gone more than likely */
00200       ast_audiohook_unlock(&csth->spy_audiohook);
00201       return -1;
00202    }
00203 
00204    f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
00205 
00206    ast_audiohook_unlock(&csth->spy_audiohook);
00207 
00208    if (!f)
00209       return 0;
00210 
00211    for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00212       if (ast_write(chan, cur)) {
00213          ast_frfree(f);
00214          return -1;
00215       }
00216 
00217       if (csth->fd) {
00218          if (write(csth->fd, cur->data, cur->datalen) < 0) {
00219             ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00220          }
00221       }
00222    }
00223 
00224    ast_frfree(f);
00225 
00226    return 0;
00227 }

static void spy_release ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 187 of file app_chanspy.c.

00188 {
00189    /* nothing to do */
00190 }

static int start_spying ( struct ast_channel chan,
const char *  spychan_name,
struct ast_audiohook audiohook 
) [static]

Definition at line 235 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, chan, LOG_NOTICE, and ast_channel::name.

Referenced by channel_spy().

00236 {
00237    int res = 0;
00238    struct ast_channel *peer = NULL;
00239 
00240    ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
00241 
00242    ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
00243    res = ast_audiohook_attach(chan, audiohook);
00244 
00245    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 
00246       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00247    }
00248    return res;
00249 }

static int unload_module ( void   )  [static]

Definition at line 934 of file app_chanspy.c.

References ast_unregister_application().

00935 {
00936    int res = 0;
00937 
00938    res |= ast_unregister_application(app_chan);
00939    res |= ast_unregister_application(app_ext);
00940 
00941    return res;
00942 }


Variable Documentation

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 = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, } [static]

Definition at line 954 of file app_chanspy.c.

const char* app_chan = "ChanSpy" [static]

Definition at line 56 of file app_chanspy.c.

const char* app_ext = "ExtenSpy" [static]

Definition at line 100 of file app_chanspy.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 954 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 450 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 57 of file app_chanspy.c.

const char* desc_ext [static]

Definition at line 101 of file app_chanspy.c.

int next_unique_id_to_use = 0 [static]

Definition at line 171 of file app_chanspy.c.

struct ast_app_option spy_opts[128] = { [ 'q' ] = { .flag = OPTION_QUIET }, [ 'b' ] = { .flag = OPTION_BRIDGED }, [ 'w' ] = { .flag = OPTION_WHISPER }, [ 'W' ] = { .flag = OPTION_PRIVATE }, [ 'v' ] = { .flag = OPTION_VOLUME , .arg_index = OPT_ARG_VOLUME + 1 }, [ 'g' ] = { .flag = OPTION_GROUP , .arg_index = OPT_ARG_GROUP + 1 }, [ 'r' ] = { .flag = OPTION_RECORD , .arg_index = OPT_ARG_RECORD + 1 }, [ 'e' ] = { .flag = OPTION_ENFORCED , .arg_index = OPT_ARG_ENFORCED + 1 }, [ 'o' ] = { .flag = OPTION_READONLY }, [ 'X' ] = { .flag = OPTION_EXIT },} [static]

Definition at line 169 of file app_chanspy.c.

Referenced by chanspy_exec(), and extenspy_exec().

struct ast_generator spygen [static]

Initial value:

 {
   .alloc = spy_alloc,
   .release = spy_release,
   .generate = spy_generate,
}

Definition at line 229 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 55 of file app_chanspy.c.


Generated on Thu Jul 9 13:40:44 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7