Wed Mar 3 22:38:36 2010

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"
Include dependency graph for app_chanspy.c:

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_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, 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_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 = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, }
static const char * app_chan = "ChanSpy"
static const char * app_ext = "ExtenSpy"
static 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 }, [ '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"

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

#define NUM_SPYGROUPS   128

Definition at line 54 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 
OPTION_NOTECH 
OPTION_BARGE 
OPTION_NAME 
OPTION_DTMF_SWITCH_MODES 

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
Enumerator:
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;


Function Documentation

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]
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 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]
Note:
Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked

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.

00240 {
00241    /* nothing to do */
00242 }

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 }


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 = "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.

Definition at line 1113 of file app_chanspy.c.

Initial value:
 {
   .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]
Initial value:
 {
   .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.


Generated on 3 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1