Tue Nov 4 13:20:23 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
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 118 of file app_chanspy.c.

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

anonymous enum

Enumerator:
OPT_ARG_VOLUME 
OPT_ARG_GROUP 
OPT_ARG_RECORD 
OPT_ARG_ARRAY_SIZE 

Definition at line 128 of file app_chanspy.c.

00128      {
00129    OPT_ARG_VOLUME = 0,
00130    OPT_ARG_GROUP,
00131    OPT_ARG_RECORD,
00132    OPT_ARG_ARRAY_SIZE,
00133 } 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 224 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().

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

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

Definition at line 396 of file app_chanspy.c.

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

00397 {
00398    struct chanspy_ds *chanspy_ds = data;
00399    
00400    ast_mutex_lock(&chanspy_ds->lock);
00401    chanspy_ds->chan = new_chan;
00402    ast_mutex_unlock(&chanspy_ds->lock);
00403 }

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

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

Referenced by chanspy_ds_free().

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

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

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

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

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

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

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

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 488 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_strdupa, 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().

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

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

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

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

static int load_module ( void   )  [static]

Definition at line 850 of file app_chanspy.c.

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

00851 {
00852    int res = 0;
00853 
00854    res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
00855    res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
00856 
00857    return res;
00858 }

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

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

Referenced by common_exec().

00462 {
00463    struct ast_channel *this;
00464 
00465 redo:
00466    if (spec)
00467       this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
00468    else if (exten)
00469       this = ast_walk_channel_by_exten_locked(last, exten, context);
00470    else
00471       this = ast_channel_walk_locked(last);
00472 
00473    if (!this)
00474       return NULL;
00475 
00476    if (!strncmp(this->name, "Zap/pseudo", 10)) {
00477       ast_channel_unlock(this);
00478       goto redo;
00479    } else if (this == chan) {
00480       last = this;
00481       ast_channel_unlock(this);
00482       goto redo;
00483    }
00484 
00485    return setup_chanspy_ds(this, chanspy_ds);
00486 }

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

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

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

Definition at line 155 of file app_chanspy.c.

00156 {
00157    /* just store the data pointer in the channel structure */
00158    return data;
00159 }

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

Definition at line 166 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.

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

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

Definition at line 161 of file app_chanspy.c.

00162 {
00163    /* nothing to do */
00164 }

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

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

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

static int unload_module ( void   )  [static]

Definition at line 838 of file app_chanspy.c.

References ast_module_user_hangup_all, and ast_unregister_application().

00839 {
00840    int res = 0;
00841 
00842    res |= ast_unregister_application(app_chan);
00843    res |= ast_unregister_application(app_ext);
00844 
00845    ast_module_user_hangup_all();
00846 
00847    return res;
00848 }


Variable Documentation

const char* app_chan = "ChanSpy" [static]

Definition at line 59 of file app_chanspy.c.

const char* app_ext = "ExtenSpy" [static]

Definition at line 90 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 405 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 60 of file app_chanspy.c.

const char* desc_ext [static]

Definition at line 91 of file app_chanspy.c.

int next_unique_id_to_use = 0

Definition at line 145 of file app_chanspy.c.

struct ast_generator spygen [static]

Initial value:

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

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


Generated on Tue Nov 4 13:20:23 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7