Mon Nov 24 15:34:22 2008

Asterisk developer's documentation


app_chanspy.c File Reference

ChanSpy: Listen in on any channel. More...

#include "asterisk.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/audiohook.h"
#include "asterisk/features.h"
#include "asterisk/options.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"

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

Functions

 AST_APP_OPTIONS (spy_opts,{AST_APP_OPTION('q', OPTION_QUIET), AST_APP_OPTION('b', OPTION_BRIDGED), AST_APP_OPTION('w', OPTION_WHISPER), AST_APP_OPTION('W', OPTION_PRIVATE), AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME), AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP), AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),})
 AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY,"Listen to the audio of an active channel")
static int channel_spy (struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, int *volfactor, int fd, const struct ast_flags *flags)
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 *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 const char * app_chan = "ChanSpy"
static const char * app_ext = "ExtenSpy"
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_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 56 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 

Definition at line 122 of file app_chanspy.c.

00122      {
00123    OPTION_QUIET    = (1 << 0),   /* Quiet, no announcement */
00124    OPTION_BRIDGED   = (1 << 1),  /* Only look at bridged calls */
00125    OPTION_VOLUME    = (1 << 2),  /* Specify initial volume */
00126    OPTION_GROUP     = (1 << 3),  /* Only look at channels in group */
00127    OPTION_RECORD    = (1 << 4),
00128    OPTION_WHISPER  = (1 << 5),
00129    OPTION_PRIVATE   = (1 << 6),  /* Private Whisper mode */
00130 } chanspy_opt_flags;

anonymous enum

Enumerator:
OPT_ARG_VOLUME 
OPT_ARG_GROUP 
OPT_ARG_RECORD 
OPT_ARG_ARRAY_SIZE 

Definition at line 132 of file app_chanspy.c.

00132      {
00133    OPT_ARG_VOLUME = 0,
00134    OPT_ARG_GROUP,
00135    OPT_ARG_RECORD,
00136    OPT_ARG_ARRAY_SIZE,
00137 } chanspy_opt_args;


Function Documentation

AST_APP_OPTIONS ( spy_opts   ) 

AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY  ,
"Listen to the audio of an active channel"   
)

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

Definition at line 228 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_FRAME_DTMF, AST_FRAME_VOICE, ast_frfree, ast_mutex_lock(), ast_mutex_unlock(), ast_read(), ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_verbose(), ast_waitfor(), chanspy_ds::chan, f, ast_channel::flags, chanspy_ds::lock, name, OPTION_PRIVATE, option_verbose, OPTION_WHISPER, spygen, start_spying(), VERBOSE_PREFIX_2, and VERBOSE_PREFIX_3.

Referenced by common_exec().

00230 {
00231    struct chanspy_translation_helper csth;
00232    int running = 0, res, x = 0;
00233    char inp[24] = {0};
00234    char *name;
00235    struct ast_frame *f;
00236    struct ast_silence_generator *silgen = NULL;
00237    struct ast_channel *spyee = NULL;
00238    const char *spyer_name;
00239 
00240    ast_channel_lock(chan);
00241    spyer_name = ast_strdupa(chan->name);
00242    ast_channel_unlock(chan);
00243 
00244    ast_mutex_lock(&spyee_chanspy_ds->lock);
00245    if (spyee_chanspy_ds->chan) {
00246       spyee = spyee_chanspy_ds->chan;
00247       ast_channel_lock(spyee);
00248    }
00249    ast_mutex_unlock(&spyee_chanspy_ds->lock);
00250 
00251    if (!spyee)
00252       return 0;
00253 
00254    /* We now hold the channel lock on spyee */
00255 
00256    if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
00257       ast_channel_unlock(spyee);
00258       return 0;
00259    }
00260 
00261    name = ast_strdupa(spyee->name);
00262    if (option_verbose >= 2)
00263       ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name);
00264 
00265    memset(&csth, 0, sizeof(csth));
00266    
00267    ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
00268 
00269    if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
00270       ast_audiohook_destroy(&csth.spy_audiohook);
00271       ast_channel_unlock(spyee);
00272       return 0;
00273    }
00274    
00275    if (ast_test_flag(flags, OPTION_WHISPER)) {
00276       ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
00277       start_spying(spyee, spyer_name, &csth.whisper_audiohook);
00278    }
00279 
00280    ast_channel_unlock(spyee);
00281    spyee = NULL;
00282 
00283    csth.volfactor = *volfactor;
00284    
00285    if (csth.volfactor) {
00286       csth.spy_audiohook.options.read_volume = csth.volfactor;
00287       csth.spy_audiohook.options.write_volume = csth.volfactor;
00288    }
00289    
00290    csth.fd = fd;
00291 
00292    if (ast_test_flag(flags, OPTION_PRIVATE))
00293       silgen = ast_channel_start_silence_generator(chan);
00294    else
00295       ast_activate_generator(chan, &spygen, &csth);
00296 
00297    /* We can no longer rely on 'spyee' being an actual channel;
00298       it can be hung up and freed out from under us. However, the
00299       channel destructor will put NULL into our csth.spy.chan
00300       field when that happens, so that is our signal that the spyee
00301       channel has gone away.
00302    */
00303 
00304    /* Note: it is very important that the ast_waitfor() be the first
00305       condition in this expression, so that if we wait for some period
00306       of time before receiving a frame from our spying channel, we check
00307       for hangup on the spied-on channel _after_ knowing that a frame
00308       has arrived, since the spied-on channel could have gone away while
00309       we were waiting
00310    */
00311    while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00312       if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
00313          running = -1;
00314          break;
00315       }
00316 
00317       if (ast_test_flag(flags, OPTION_WHISPER) && (f->frametype == AST_FRAME_VOICE)) {
00318          ast_audiohook_lock(&csth.whisper_audiohook);
00319          ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00320          ast_audiohook_unlock(&csth.whisper_audiohook);
00321          ast_frfree(f);
00322          continue;
00323       }
00324 
00325       res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
00326       ast_frfree(f);
00327       if (!res)
00328          continue;
00329 
00330       if (x == sizeof(inp))
00331          x = 0;
00332 
00333       if (res < 0) {
00334          running = -1;
00335          break;
00336       }
00337 
00338       if (res == '*') {
00339          running = 0;
00340          break;
00341       } else if (res == '#') {
00342          if (!ast_strlen_zero(inp)) {
00343             running = atoi(inp);
00344             break;
00345          }
00346 
00347          (*volfactor)++;
00348          if (*volfactor > 4)
00349             *volfactor = -4;
00350          if (option_verbose > 2)
00351             ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
00352          csth.volfactor = *volfactor;
00353          csth.spy_audiohook.options.read_volume = csth.volfactor;
00354          csth.spy_audiohook.options.write_volume = csth.volfactor;
00355       } else if (res >= '0' && res <= '9') {
00356          inp[x++] = res;
00357       }
00358    }
00359 
00360    if (ast_test_flag(flags, OPTION_PRIVATE))
00361       ast_channel_stop_silence_generator(chan, silgen);
00362    else
00363       ast_deactivate_generator(chan);
00364 
00365    if (ast_test_flag(flags, OPTION_WHISPER)) {
00366       ast_audiohook_lock(&csth.whisper_audiohook);
00367       ast_audiohook_detach(&csth.whisper_audiohook);
00368       ast_audiohook_unlock(&csth.whisper_audiohook);
00369       ast_audiohook_destroy(&csth.whisper_audiohook);
00370    }
00371    
00372    ast_audiohook_lock(&csth.spy_audiohook);
00373    ast_audiohook_detach(&csth.spy_audiohook);
00374    ast_audiohook_unlock(&csth.spy_audiohook);
00375    ast_audiohook_destroy(&csth.spy_audiohook);
00376    
00377    if (option_verbose >= 2)
00378       ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
00379    
00380    return running;
00381 }

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

Definition at line 400 of file app_chanspy.c.

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

00401 {
00402    struct chanspy_ds *chanspy_ds = data;
00403    
00404    ast_mutex_lock(&chanspy_ds->lock);
00405    chanspy_ds->chan = new_chan;
00406    ast_mutex_unlock(&chanspy_ds->lock);
00407 }

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 387 of file app_chanspy.c.

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

Referenced by chanspy_ds_free().

00388 {
00389    struct chanspy_ds *chanspy_ds = data;
00390 
00391    /* Setting chan to be NULL is an atomic operation, but we don't want this
00392     * value to change while this lock is held.  The lock is held elsewhere
00393     * while it performs non-atomic operations with this channel pointer */
00394 
00395    ast_mutex_lock(&chanspy_ds->lock);
00396    chanspy_ds->chan = NULL;
00397    ast_mutex_unlock(&chanspy_ds->lock);
00398 }

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

Definition at line 415 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(), 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().

00416 {
00417    if (!chanspy_ds)
00418       return NULL;
00419 
00420    ast_mutex_lock(&chanspy_ds->lock);
00421    if (chanspy_ds->chan) {
00422       struct ast_datastore *datastore;
00423       struct ast_channel *chan;
00424 
00425       chan = chanspy_ds->chan;
00426 
00427       ast_channel_lock(chan);
00428       if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
00429          ast_channel_datastore_remove(chan, datastore);
00430          /* chanspy_ds->chan is NULL after this call */
00431          chanspy_ds_destroy(datastore->data);
00432          datastore->data = NULL;
00433          ast_channel_datastore_free(datastore);
00434       }
00435       ast_channel_unlock(chan);
00436    }
00437    ast_mutex_unlock(&chanspy_ds->lock);
00438 
00439    return NULL;
00440 }

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

Definition at line 684 of file app_chanspy.c.

References ast_app_parse_options(), ast_app_separate_args(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_module_user_add, ast_module_user_remove, ast_set_flag, ast_set_write_format(), ast_strdupa, ast_strlen_zero(), ast_test_flag, common_exec(), 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, and ast_channel::writeformat.

Referenced by load_module().

00685 {
00686    struct ast_module_user *u;
00687    char *options = NULL;
00688    char *spec = NULL;
00689    char *argv[2];
00690    char *mygroup = NULL;
00691    char *recbase = NULL;
00692    int fd = 0;
00693    struct ast_flags flags;
00694    int oldwf = 0;
00695    int argc = 0;
00696    int volfactor = 0;
00697    int res;
00698 
00699    data = ast_strdupa(data);
00700 
00701    u = ast_module_user_add(chan);
00702 
00703    if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
00704       spec = argv[0];
00705       if (argc > 1)
00706          options = argv[1];
00707 
00708       if (ast_strlen_zero(spec) || !strcmp(spec, "all"))
00709          spec = NULL;
00710    }
00711 
00712    if (options) {
00713       char *opts[OPT_ARG_ARRAY_SIZE];
00714       
00715       ast_app_parse_options(spy_opts, &flags, opts, options);
00716       if (ast_test_flag(&flags, OPTION_GROUP))
00717          mygroup = opts[OPT_ARG_GROUP];
00718 
00719       if (ast_test_flag(&flags, OPTION_RECORD) &&
00720           !(recbase = opts[OPT_ARG_RECORD]))
00721          recbase = "chanspy";
00722 
00723       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00724          int vol;
00725 
00726          if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
00727             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00728          else
00729             volfactor = vol;
00730       }
00731 
00732       if (ast_test_flag(&flags, OPTION_PRIVATE))
00733          ast_set_flag(&flags, OPTION_WHISPER);
00734    } else
00735       ast_clear_flag(&flags, AST_FLAGS_ALL);
00736 
00737    oldwf = chan->writeformat;
00738    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00739       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00740       ast_module_user_remove(u);
00741       return -1;
00742    }
00743 
00744    if (recbase) {
00745       char filename[PATH_MAX];
00746 
00747       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
00748       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
00749          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
00750          fd = 0;
00751       }
00752    }
00753 
00754    res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL);
00755 
00756    if (fd)
00757       close(fd);
00758 
00759    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
00760       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00761 
00762    ast_module_user_remove(u);
00763 
00764    return res;
00765 }

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

Definition at line 500 of file app_chanspy.c.

References ast_channel::_state, ast_answer(), ast_app_separate_args(), ast_bridged_channel(), ast_channel_lock, ast_channel_setoption(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_fileexists(), AST_FLAG_SPYING, ast_get_channel_by_name_prefix_locked(), 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_test_flag, ast_waitfordigit(), ast_waitstream(), chanspy_ds::chan, channel_spy(), chanspy_ds_free(), ast_channel::flags, group, chanspy_ds::lock, next_channel(), OPTION_BRIDGED, OPTION_QUIET, pbx_builtin_getvar_helper(), s, setup_chanspy_ds(), and chanspy_ds::unique_id.

Referenced by chanspy_exec(), and extenspy_exec().

00503 {
00504    char nameprefix[AST_NAME_STRLEN];
00505    char peer_name[AST_NAME_STRLEN + 5];
00506    signed char zero_volume = 0;
00507    int waitms;
00508    int res;
00509    char *ptr;
00510    int num;
00511    int num_spyed_upon = 1;
00512    struct chanspy_ds chanspy_ds;
00513 
00514    ast_mutex_init(&chanspy_ds.lock);
00515 
00516    snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
00517 
00518    if (chan->_state != AST_STATE_UP)
00519       ast_answer(chan);
00520 
00521    ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
00522 
00523    waitms = 100;
00524 
00525    for (;;) {
00526       struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
00527       struct ast_channel *prev = NULL, *peer = NULL;
00528 
00529       if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
00530          res = ast_streamfile(chan, "beep", chan->language);
00531          if (!res)
00532             res = ast_waitstream(chan, "");
00533          else if (res < 0) {
00534             ast_clear_flag(chan, AST_FLAG_SPYING);
00535             break;
00536          }
00537       }
00538 
00539       res = ast_waitfordigit(chan, waitms);
00540       if (res < 0) {
00541          ast_clear_flag(chan, AST_FLAG_SPYING);
00542          break;
00543       }
00544             
00545       /* reset for the next loop around, unless overridden later */
00546       waitms = 100;
00547       num_spyed_upon = 0;
00548 
00549       for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
00550            peer_chanspy_ds;
00551           chanspy_ds_free(peer_chanspy_ds), prev = peer,
00552            peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 
00553             next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
00554          const char *group;
00555          int igrp = !mygroup;
00556          char *groups[25];
00557          int num_groups = 0;
00558          char dup_group[512];
00559          int x;
00560          char *s;
00561 
00562          peer = peer_chanspy_ds->chan;
00563 
00564          ast_mutex_unlock(&peer_chanspy_ds->lock);
00565 
00566          if (peer == prev) {
00567             ast_channel_unlock(peer);
00568             chanspy_ds_free(peer_chanspy_ds);
00569             break;
00570          }
00571 
00572          if (ast_check_hangup(chan)) {
00573             ast_channel_unlock(peer);
00574             chanspy_ds_free(peer_chanspy_ds);
00575             break;
00576          }
00577 
00578          if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
00579             ast_channel_unlock(peer);
00580             continue;
00581          }
00582 
00583          if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
00584             ast_channel_unlock(peer);
00585             continue;
00586          }
00587 
00588          if (mygroup) {
00589             if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
00590                ast_copy_string(dup_group, group, sizeof(dup_group));
00591                num_groups = ast_app_separate_args(dup_group, ':', groups,
00592                               sizeof(groups) / sizeof(groups[0]));
00593             }
00594             
00595             for (x = 0; x < num_groups; x++) {
00596                if (!strcmp(mygroup, groups[x])) {
00597                   igrp = 1;
00598                   break;
00599                }
00600             }
00601          }
00602          
00603          if (!igrp) {
00604             ast_channel_unlock(peer);
00605             continue;
00606          }
00607 
00608          strcpy(peer_name, "spy-");
00609          strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
00610          ptr = strchr(peer_name, '/');
00611          *ptr++ = '\0';
00612          
00613          for (s = peer_name; s < ptr; s++)
00614             *s = tolower(*s);
00615 
00616          /* We have to unlock the peer channel here to avoid a deadlock.
00617           * So, when we need to dereference it again, we have to lock the 
00618           * datastore and get the pointer from there to see if the channel 
00619           * is still valid. */
00620          ast_channel_unlock(peer);
00621 
00622          if (!ast_test_flag(flags, OPTION_QUIET)) {
00623             if (ast_fileexists(peer_name, NULL, NULL) != -1) {
00624                res = ast_streamfile(chan, peer_name, chan->language);
00625                if (!res)
00626                   res = ast_waitstream(chan, "");
00627                if (res) {
00628                   chanspy_ds_free(peer_chanspy_ds);
00629                   break;
00630                }
00631             } else
00632                res = ast_say_character_str(chan, peer_name, "", chan->language);
00633             if ((num = atoi(ptr))) 
00634                ast_say_digits(chan, atoi(ptr), "", chan->language);
00635          }
00636          
00637          res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags);
00638          num_spyed_upon++; 
00639 
00640          if (res == -1) {
00641             chanspy_ds_free(peer_chanspy_ds);
00642             break;
00643          } else if (res > 1 && spec) {
00644             struct ast_channel *next;
00645 
00646             snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
00647 
00648             if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
00649                peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
00650                next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
00651             } else {
00652                /* stay on this channel, if it is still valid */
00653 
00654                ast_mutex_lock(&peer_chanspy_ds->lock);
00655                if (peer_chanspy_ds->chan) {
00656                   ast_channel_lock(peer_chanspy_ds->chan);
00657                   next_chanspy_ds = peer_chanspy_ds;
00658                   peer_chanspy_ds = NULL;
00659                } else {
00660                   /* the channel is gone */
00661                   ast_mutex_unlock(&peer_chanspy_ds->lock);
00662                   next_chanspy_ds = NULL;
00663                }
00664             }
00665 
00666             peer = NULL;
00667          }
00668       }
00669       if (res == -1 || ast_check_hangup(chan))
00670          break;
00671    }
00672    
00673    ast_clear_flag(chan, AST_FLAG_SPYING);
00674 
00675    ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00676 
00677    ast_mutex_lock(&chanspy_ds.lock);
00678    ast_mutex_unlock(&chanspy_ds.lock);
00679    ast_mutex_destroy(&chanspy_ds.lock);
00680 
00681    return res;
00682 }

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

Definition at line 767 of file app_chanspy.c.

References ast_app_parse_options(), ast_app_separate_args(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_module_user_add, ast_module_user_remove, ast_set_flag, ast_set_write_format(), ast_strdupa, ast_strlen_zero(), ast_test_flag, 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, strsep(), and ast_channel::writeformat.

Referenced by load_module().

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

static int load_module ( void   )  [static]

Definition at line 864 of file app_chanspy.c.

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

00865 {
00866    int res = 0;
00867 
00868    res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
00869    res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
00870 
00871    return res;
00872 }

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 463 of file app_chanspy.c.

References AST_CHANNEL_NAME, ast_channel_unlock, ast_channel_walk_locked(), ast_walk_channel_by_exten_locked(), ast_walk_channel_by_name_prefix_locked(), dahdi_chan_name, dahdi_chan_name_len, last, and setup_chanspy_ds().

Referenced by common_exec().

00466 {
00467    struct ast_channel *this;
00468    char channel_name[AST_CHANNEL_NAME];
00469    static size_t PSEUDO_CHAN_LEN = 0;
00470 
00471    if (!PSEUDO_CHAN_LEN) {
00472       PSEUDO_CHAN_LEN = *dahdi_chan_name_len + strlen("/pseudo");
00473    }
00474 
00475 redo:
00476    if (spec)
00477       this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
00478    else if (exten)
00479       this = ast_walk_channel_by_exten_locked(last, exten, context);
00480    else
00481       this = ast_channel_walk_locked(last);
00482 
00483    if (!this)
00484       return NULL;
00485 
00486    snprintf(channel_name, AST_CHANNEL_NAME, "%s/pseudo", dahdi_chan_name);
00487    if (!strncmp(this->name, channel_name, PSEUDO_CHAN_LEN)) {
00488       last = this;
00489       ast_channel_unlock(this);
00490       goto redo;
00491    } else if (this == chan) {
00492       last = this;
00493       ast_channel_unlock(this);
00494       goto redo;
00495    }
00496 
00497    return setup_chanspy_ds(this, chanspy_ds);
00498 }

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 443 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, chanspy_ds_free(), chanspy_ds_info, ast_datastore::data, chanspy_ds::lock, and chanspy_ds::unique_id.

Referenced by common_exec(), and next_channel().

00444 {
00445    struct ast_datastore *datastore = NULL;
00446 
00447    ast_mutex_lock(&chanspy_ds->lock);
00448 
00449    if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
00450       ast_mutex_unlock(&chanspy_ds->lock);
00451       chanspy_ds = chanspy_ds_free(chanspy_ds);
00452       ast_channel_unlock(chan);
00453       return NULL;
00454    }
00455    
00456    chanspy_ds->chan = chan;
00457    datastore->data = chanspy_ds;
00458    ast_channel_datastore_add(chan, datastore);
00459 
00460    return chanspy_ds;
00461 }

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

Definition at line 159 of file app_chanspy.c.

00160 {
00161    /* just store the data pointer in the channel structure */
00162    return data;
00163 }

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

Definition at line 170 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_write(), f, chanspy_translation_helper::fd, chanspy_translation_helper::spy_audiohook, and ast_audiohook::status.

00171 {
00172    struct chanspy_translation_helper *csth = data;
00173    struct ast_frame *f;
00174 
00175    ast_audiohook_lock(&csth->spy_audiohook);
00176    if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00177       ast_audiohook_unlock(&csth->spy_audiohook);
00178       return -1;
00179    }
00180 
00181    f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
00182 
00183    ast_audiohook_unlock(&csth->spy_audiohook);
00184 
00185    if (!f)
00186       return 0;
00187       
00188    if (ast_write(chan, f)) {
00189       ast_frfree(f);
00190       return -1;
00191    }
00192 
00193    if (csth->fd)
00194       write(csth->fd, f->data, f->datalen);
00195 
00196    ast_frfree(f);
00197 
00198    return 0;
00199 }

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

Definition at line 165 of file app_chanspy.c.

00166 {
00167    /* nothing to do */
00168 }

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

Definition at line 207 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, and LOG_NOTICE.

Referenced by channel_spy().

00208 {
00209    int res;
00210    struct ast_channel *peer;
00211 
00212    ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
00213 
00214    res = ast_audiohook_attach(chan, audiohook);
00215 
00216    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 
00217       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
00218    }
00219    return res;
00220 }

static int unload_module ( void   )  [static]

Definition at line 852 of file app_chanspy.c.

References ast_module_user_hangup_all, and ast_unregister_application().

00853 {
00854    int res = 0;
00855 
00856    res |= ast_unregister_application(app_chan);
00857    res |= ast_unregister_application(app_ext);
00858 
00859    ast_module_user_hangup_all();
00860 
00861    return res;
00862 }


Variable Documentation

const char* app_chan = "ChanSpy" [static]

Definition at line 63 of file app_chanspy.c.

const char* app_ext = "ExtenSpy" [static]

Definition at line 94 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 409 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 64 of file app_chanspy.c.

const char* desc_ext [static]

Definition at line 95 of file app_chanspy.c.

int next_unique_id_to_use = 0 [static]

Definition at line 149 of file app_chanspy.c.

struct ast_generator spygen [static]

Initial value:

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

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


Generated on Mon Nov 24 15:34:22 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7