Thu May 14 15:13:06 2009

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

static void __reg_module (void)
static void __unreg_module (void)
static int channel_spy (struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, int *volfactor, int fd, const struct ast_flags *flags)
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 struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "Listen to the audio of an active channel" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, }
static const char * app_chan = "ChanSpy"
static const char * app_ext = "ExtenSpy"
static const struct ast_module_infoast_module_info = &__mod_info
static struct ast_datastore_info chanspy_ds_info
enum { ... }  chanspy_opt_args
enum { ... }  chanspy_opt_flags
static const char * desc_chan
static const char * desc_ext
static int next_unique_id_to_use = 0
static struct ast_app_option spy_opts [128] = { [ 'q' ] = { .flag = OPTION_QUIET }, [ 'b' ] = { .flag = OPTION_BRIDGED }, [ 'w' ] = { .flag = OPTION_WHISPER }, [ 'W' ] = { .flag = OPTION_PRIVATE }, [ 'v' ] = { .flag = OPTION_VOLUME , .arg_index = OPT_ARG_VOLUME + 1 }, [ 'g' ] = { .flag = OPTION_GROUP , .arg_index = OPT_ARG_GROUP + 1 }, [ 'r' ] = { .flag = OPTION_RECORD , .arg_index = OPT_ARG_RECORD + 1 },}
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 57 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 123 of file app_chanspy.c.

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

anonymous enum

Enumerator:
OPT_ARG_VOLUME 
OPT_ARG_GROUP 
OPT_ARG_RECORD 
OPT_ARG_ARRAY_SIZE 

Definition at line 133 of file app_chanspy.c.

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


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 878 of file app_chanspy.c.

static void __unreg_module ( void   )  [static]

Definition at line 878 of file app_chanspy.c.

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

Definition at line 232 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, ast_channel::name, name, OPTION_PRIVATE, option_verbose, OPTION_WHISPER, spygen, start_spying(), VERBOSE_PREFIX_2, and VERBOSE_PREFIX_3.

Referenced by common_exec().

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

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

Definition at line 404 of file app_chanspy.c.

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

00405 {
00406    struct chanspy_ds *chanspy_ds = data;
00407    
00408    ast_mutex_lock(&chanspy_ds->lock);
00409    chanspy_ds->chan = new_chan;
00410    ast_mutex_unlock(&chanspy_ds->lock);
00411 }

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

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

Referenced by chanspy_ds_free().

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

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

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

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

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

Definition at line 688 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, spy_opts, and ast_channel::writeformat.

Referenced by load_module().

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

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

References ast_channel::_state, ast_answer(), ast_app_separate_args(), ast_atomic_fetchadd_int(), ast_bridged_channel(), ast_channel_lock, ast_channel_setoption(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_copy_string(), 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, ast_channel::next, 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().

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

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

Definition at line 771 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, spy_opts, and ast_channel::writeformat.

Referenced by load_module().

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

static int load_module ( void   )  [static]

Definition at line 868 of file app_chanspy.c.

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

00869 {
00870    int res = 0;
00871 
00872    res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
00873    res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
00874 
00875    return res;
00876 }

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

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

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

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

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

Definition at line 160 of file app_chanspy.c.

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

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

Definition at line 171 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_log(), ast_write(), errno, f, chanspy_translation_helper::fd, LOG_WARNING, chanspy_translation_helper::spy_audiohook, and ast_audiohook::status.

00172 {
00173    struct chanspy_translation_helper *csth = data;
00174    struct ast_frame *f;
00175 
00176    ast_audiohook_lock(&csth->spy_audiohook);
00177    if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00178       ast_audiohook_unlock(&csth->spy_audiohook);
00179       return -1;
00180    }
00181 
00182    f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
00183 
00184    ast_audiohook_unlock(&csth->spy_audiohook);
00185 
00186    if (!f)
00187       return 0;
00188       
00189    if (ast_write(chan, f)) {
00190       ast_frfree(f);
00191       return -1;
00192    }
00193 
00194    if (csth->fd) {
00195       if (write(csth->fd, f->data, f->datalen) < 0) {
00196          ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00197       }
00198    }
00199 
00200    ast_frfree(f);
00201 
00202    return 0;
00203 }

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

Definition at line 166 of file app_chanspy.c.

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

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

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

Referenced by channel_spy().

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

static int unload_module ( void   )  [static]

Definition at line 856 of file app_chanspy.c.

References ast_module_user_hangup_all, and ast_unregister_application().

00857 {
00858    int res = 0;
00859 
00860    res |= ast_unregister_application(app_chan);
00861    res |= ast_unregister_application(app_ext);
00862 
00863    ast_module_user_hangup_all();
00864 
00865    return res;
00866 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "Listen to the audio of an active channel" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, } [static]

Definition at line 878 of file app_chanspy.c.

const char* app_chan = "ChanSpy" [static]

Definition at line 64 of file app_chanspy.c.

const char* app_ext = "ExtenSpy" [static]

Definition at line 95 of file app_chanspy.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 878 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 413 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 65 of file app_chanspy.c.

const char* desc_ext [static]

Definition at line 96 of file app_chanspy.c.

int next_unique_id_to_use = 0 [static]

Definition at line 150 of file app_chanspy.c.

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

Definition at line 148 of file app_chanspy.c.

Referenced by chanspy_exec(), and extenspy_exec().

struct ast_generator spygen [static]

Initial value:

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

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


Generated on Thu May 14 15:13:06 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7