Mon Mar 31 07:38:15 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"

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

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

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

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

Definition at line 395 of file app_chanspy.c.

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

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

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

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

Referenced by chanspy_ds_free().

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

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

Definition at line 410 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, and chanspy_ds::lock.

Referenced by common_exec(), and setup_chanspy_ds().

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

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[512];
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 487 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, and setup_chanspy_ds().

Referenced by chanspy_exec(), and extenspy_exec().

00490 {
00491    char nameprefix[AST_NAME_STRLEN];
00492    char peer_name[AST_NAME_STRLEN + 5];
00493    signed char zero_volume = 0;
00494    int waitms;
00495    int res;
00496    char *ptr;
00497    int num;
00498    int num_spyed_upon = 1;
00499    struct chanspy_ds chanspy_ds;
00500 
00501    ast_mutex_init(&chanspy_ds.lock);
00502 
00503    if (chan->_state != AST_STATE_UP)
00504       ast_answer(chan);
00505 
00506    ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
00507 
00508    waitms = 100;
00509 
00510    for (;;) {
00511       struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
00512       struct ast_channel *prev = NULL, *peer = NULL;
00513 
00514       if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
00515          res = ast_streamfile(chan, "beep", chan->language);
00516          if (!res)
00517             res = ast_waitstream(chan, "");
00518          else if (res < 0) {
00519             ast_clear_flag(chan, AST_FLAG_SPYING);
00520             break;
00521          }
00522       }
00523 
00524       res = ast_waitfordigit(chan, waitms);
00525       if (res < 0) {
00526          ast_clear_flag(chan, AST_FLAG_SPYING);
00527          break;
00528       }
00529             
00530       /* reset for the next loop around, unless overridden later */
00531       waitms = 100;
00532       num_spyed_upon = 0;
00533 
00534       for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
00535            peer_chanspy_ds;
00536           chanspy_ds_free(peer_chanspy_ds), prev = peer,
00537            peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 
00538             next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
00539          const char *group;
00540          int igrp = !mygroup;
00541          char *groups[25];
00542          int num_groups = 0;
00543          char *dup_group;
00544          int x;
00545          char *s;
00546          struct ast_channel *peer;
00547 
00548          peer = peer_chanspy_ds->chan;
00549 
00550          ast_mutex_unlock(&peer_chanspy_ds->lock);
00551 
00552          if (peer == prev) {
00553             ast_channel_unlock(peer);
00554             chanspy_ds_free(peer_chanspy_ds);
00555             break;
00556          }
00557 
00558          if (ast_check_hangup(chan)) {
00559             ast_channel_unlock(peer);
00560             chanspy_ds_free(peer_chanspy_ds);
00561             break;
00562          }
00563 
00564          if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
00565             ast_channel_unlock(peer);
00566             continue;
00567          }
00568 
00569          if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
00570             ast_channel_unlock(peer);
00571             continue;
00572          }
00573 
00574          if (mygroup) {
00575             if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
00576                dup_group = ast_strdupa(group);
00577                num_groups = ast_app_separate_args(dup_group, ':', groups,
00578                               sizeof(groups) / sizeof(groups[0]));
00579             }
00580             
00581             for (x = 0; x < num_groups; x++) {
00582                if (!strcmp(mygroup, groups[x])) {
00583                   igrp = 1;
00584                   break;
00585                }
00586             }
00587          }
00588          
00589          if (!igrp) {
00590             ast_channel_unlock(peer);
00591             continue;
00592          }
00593 
00594          strcpy(peer_name, "spy-");
00595          strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
00596          ptr = strchr(peer_name, '/');
00597          *ptr++ = '\0';
00598          
00599          for (s = peer_name; s < ptr; s++)
00600             *s = tolower(*s);
00601 
00602       
00603          /* We have to unlock the peer channel here to avoid a deadlock.
00604           * So, when we need it again, we have to lock the datastore and get
00605           * the pointer from there to see if the channel is still valid. */
00606          ast_channel_unlock(peer);
00607          peer = NULL;
00608 
00609          if (!ast_test_flag(flags, OPTION_QUIET)) {
00610             if (ast_fileexists(peer_name, NULL, NULL) != -1) {
00611                res = ast_streamfile(chan, peer_name, chan->language);
00612                if (!res)
00613                   res = ast_waitstream(chan, "");
00614                if (res) {
00615                   chanspy_ds_free(peer_chanspy_ds);
00616                   break;
00617                }
00618             } else
00619                res = ast_say_character_str(chan, peer_name, "", chan->language);
00620             if ((num = atoi(ptr))) 
00621                ast_say_digits(chan, atoi(ptr), "", chan->language);
00622          }
00623          
00624          waitms = 5000;
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[512];
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 458 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().

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

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 438 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, and chanspy_ds::lock.

Referenced by common_exec(), and next_channel().

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

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

Definition at line 154 of file app_chanspy.c.

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

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

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

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

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

Definition at line 160 of file app_chanspy.c.

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

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

Definition at line 202 of file app_chanspy.c.

References ast_audiohook_attach(), ast_bridged_channel(), ast_channel_unlock, AST_FLAG_NBRIDGE, ast_log(), ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, ast_test_flag, and LOG_NOTICE.

Referenced by channel_spy().

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

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 404 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.

struct ast_generator spygen [static]

Initial value:

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

Definition at line 196 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 Mon Mar 31 07:38:16 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1