Thu Oct 8 21:57:31 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 "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 chanspychan_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, const char *uniqueid)
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, const char *uniqueid)
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_chan_uniqueid = "ChanSpyChan"
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 const char * desc_uniqueid
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 140 of file app_chanspy.c.

00140      {
00141    OPTION_QUIET    = (1 << 0),   /* Quiet, no announcement */
00142    OPTION_BRIDGED   = (1 << 1),  /* Only look at bridged calls */
00143    OPTION_VOLUME    = (1 << 2),  /* Specify initial volume */
00144    OPTION_GROUP     = (1 << 3),  /* Only look at channels in group */
00145    OPTION_RECORD    = (1 << 4),
00146    OPTION_WHISPER  = (1 << 5),
00147    OPTION_PRIVATE   = (1 << 6),  /* Private Whisper mode */
00148 } chanspy_opt_flags;

anonymous enum

Enumerator:
OPT_ARG_VOLUME 
OPT_ARG_GROUP 
OPT_ARG_RECORD 
OPT_ARG_ARRAY_SIZE 

Definition at line 150 of file app_chanspy.c.

00150      {
00151    OPT_ARG_VOLUME = 0,
00152    OPT_ARG_GROUP,
00153    OPT_ARG_RECORD,
00154    OPT_ARG_ARRAY_SIZE,
00155 } 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 246 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, chanspy_translation_helper::fd, ast_frame::frametype, chanspy_ds::lock, name, OPTION_PRIVATE, option_verbose, OPTION_WHISPER, ast_audiohook::options, ast_audiohook_options::read_volume, chanspy_translation_helper::spy_audiohook, start_spying(), ast_audiohook::status, ast_frame::subclass, VERBOSE_PREFIX_2, VERBOSE_PREFIX_3, chanspy_translation_helper::volfactor, chanspy_translation_helper::whisper_audiohook, and ast_audiohook_options::write_volume.

Referenced by common_exec().

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

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

Definition at line 418 of file app_chanspy.c.

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

00419 {
00420    struct chanspy_ds *chanspy_ds = data;
00421    
00422    ast_mutex_lock(&chanspy_ds->lock);
00423    chanspy_ds->chan = new_chan;
00424    ast_mutex_unlock(&chanspy_ds->lock);
00425 }

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

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

Referenced by chanspy_ds_free().

00406 {
00407    struct chanspy_ds *chanspy_ds = data;
00408 
00409    /* Setting chan to be NULL is an atomic operation, but we don't want this
00410     * value to change while this lock is held.  The lock is held elsewhere
00411     * while it performs non-atomic operations with this channel pointer */
00412 
00413    ast_mutex_lock(&chanspy_ds->lock);
00414    chanspy_ds->chan = NULL;
00415    ast_mutex_unlock(&chanspy_ds->lock);
00416 }

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

Definition at line 433 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(), ast_datastore::data, chanspy_ds::lock, and chanspy_ds::unique_id.

Referenced by common_exec(), and setup_chanspy_ds().

00434 {
00435    if (!chanspy_ds)
00436       return NULL;
00437 
00438    ast_mutex_lock(&chanspy_ds->lock);
00439    if (chanspy_ds->chan) {
00440       struct ast_datastore *datastore;
00441       struct ast_channel *chan;
00442 
00443       chan = chanspy_ds->chan;
00444 
00445       ast_channel_lock(chan);
00446       if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
00447          ast_channel_datastore_remove(chan, datastore);
00448          /* chanspy_ds->chan is NULL after this call */
00449          chanspy_ds_destroy(datastore->data);
00450          datastore->data = NULL;
00451          ast_channel_datastore_free(datastore);
00452       }
00453       ast_channel_unlock(chan);
00454    }
00455    ast_mutex_unlock(&chanspy_ds->lock);
00456 
00457    return NULL;
00458 }

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

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

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

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

Definition at line 862 of file app_chanspy.c.

References ast_app_parse_options(), ast_app_separate_args(), ast_config_AST_MONITOR_DIR, 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(), 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().

00863 {
00864    struct ast_module_user *u;
00865    char *options = NULL;
00866    char *uniqueid = NULL;
00867    char *argv[2];
00868    char *mygroup = NULL;
00869    char *recbase = NULL;
00870    int fd = 0;
00871    struct ast_flags flags;
00872    int oldwf = 0;
00873    int argc = 0;
00874    int volfactor = 0;
00875    int res;
00876 
00877    data = ast_strdupa(data);
00878 
00879    u = ast_module_user_add(chan);
00880 
00881    if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
00882       uniqueid = argv[0];
00883       if (argc > 1)
00884          options = argv[1];
00885 
00886       if (ast_strlen_zero(uniqueid)) {
00887          ast_log(LOG_ERROR, "no uniqueid specified.\n");
00888          ast_module_user_remove(u);
00889          return -1;
00890       }
00891    }
00892 
00893    if (options) {
00894       char *opts[OPT_ARG_ARRAY_SIZE];
00895 
00896       ast_app_parse_options(spy_opts, &flags, opts, options);
00897       if (ast_test_flag(&flags, OPTION_GROUP))
00898          mygroup = opts[OPT_ARG_GROUP];
00899 
00900       if (ast_test_flag(&flags, OPTION_RECORD) &&
00901           !(recbase = opts[OPT_ARG_RECORD]))
00902          recbase = "chanspy";
00903 
00904       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00905          int vol;
00906 
00907          if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
00908             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00909          else
00910             volfactor = vol;
00911       }
00912 
00913       if (ast_test_flag(&flags, OPTION_PRIVATE))
00914          ast_set_flag(&flags, OPTION_WHISPER);
00915    }
00916 
00917    oldwf = chan->writeformat;
00918    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00919       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00920       ast_module_user_remove(u);
00921       return -1;
00922    }
00923 
00924    if (recbase) {
00925       char filename[512];
00926 
00927       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
00928       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
00929          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
00930          fd = 0;
00931       }
00932    }
00933 
00934    res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, NULL, uniqueid);
00935 
00936    if (fd)
00937       close(fd);
00938 
00939    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
00940       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00941 
00942    ast_module_user_remove(u);
00943 
00944    return res;
00945 }

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,
const char *  uniqueid 
) [static]

Definition at line 512 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(), channel_spy(), chanspy_ds_free(), 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(), chanspychan_exec(), and extenspy_exec().

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

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

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

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

static int load_module ( void   )  [static]

Definition at line 960 of file app_chanspy.c.

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

00961 {
00962    int res = 0;
00963 
00964    res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
00965    res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
00966    res |= ast_register_application(app_chan_uniqueid, chanspychan_exec, tdesc, desc_uniqueid);
00967 
00968    return res;
00969 }

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,
const char *  uniqueid 
) [static, read]

Definition at line 481 of file app_chanspy.c.

References ast_channel_unlock, ast_channel_walk_locked(), ast_get_channel_by_uniqueid_locked(), ast_walk_channel_by_exten_locked(), ast_walk_channel_by_name_prefix_locked(), name, and setup_chanspy_ds().

Referenced by common_exec().

00484 {
00485    struct ast_channel *this;
00486 
00487 redo:
00488    if (spec)
00489       this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
00490    else if (exten)
00491       this = ast_walk_channel_by_exten_locked(last, exten, context);
00492    else if (uniqueid)
00493       this = ast_get_channel_by_uniqueid_locked(uniqueid);
00494    else
00495       this = ast_channel_walk_locked(last);
00496 
00497    if (!this)
00498       return NULL;
00499 
00500    if (!strncmp(this->name, "Zap/pseudo", 10)) {
00501       ast_channel_unlock(this);
00502       goto redo;
00503    } else if (this == chan) {
00504       last = this;
00505       ast_channel_unlock(this);
00506       goto redo;
00507    }
00508 
00509    return setup_chanspy_ds(this, chanspy_ds);
00510 }

static struct chanspy_ds* setup_chanspy_ds ( struct ast_channel chan,
struct chanspy_ds chanspy_ds 
) [static, read]

Note:
Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked

Definition at line 461 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(), ast_datastore::data, chanspy_ds::lock, and chanspy_ds::unique_id.

Referenced by common_exec(), and next_channel().

00462 {
00463    struct ast_datastore *datastore = NULL;
00464 
00465    ast_mutex_lock(&chanspy_ds->lock);
00466 
00467    if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
00468       ast_mutex_unlock(&chanspy_ds->lock);
00469       chanspy_ds = chanspy_ds_free(chanspy_ds);
00470       ast_channel_unlock(chan);
00471       return NULL;
00472    }
00473    
00474    chanspy_ds->chan = chan;
00475    datastore->data = chanspy_ds;
00476    ast_channel_datastore_add(chan, datastore);
00477 
00478    return chanspy_ds;
00479 }

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

Definition at line 177 of file app_chanspy.c.

00178 {
00179    /* just store the data pointer in the channel structure */
00180    return data;
00181 }

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

Definition at line 188 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(), ast_frame::data, ast_frame::datalen, f, chanspy_translation_helper::fd, chanspy_translation_helper::spy_audiohook, and ast_audiohook::status.

00189 {
00190    struct chanspy_translation_helper *csth = data;
00191    struct ast_frame *f;
00192 
00193    ast_audiohook_lock(&csth->spy_audiohook);
00194    if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00195       ast_audiohook_unlock(&csth->spy_audiohook);
00196       return -1;
00197    }
00198 
00199    f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
00200 
00201    ast_audiohook_unlock(&csth->spy_audiohook);
00202 
00203    if (!f)
00204       return 0;
00205       
00206    if (ast_write(chan, f)) {
00207       ast_frfree(f);
00208       return -1;
00209    }
00210 
00211    if (csth->fd)
00212       write(csth->fd, f->data, f->datalen);
00213 
00214    ast_frfree(f);
00215 
00216    return 0;
00217 }

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

Definition at line 183 of file app_chanspy.c.

00184 {
00185    /* nothing to do */
00186 }

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

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

00226 {
00227    int res;
00228    struct ast_channel *peer;
00229 
00230    ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
00231 
00232    res = ast_audiohook_attach(chan, audiohook);
00233 
00234    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 
00235       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
00236    }
00237    return res;
00238 }

static int unload_module ( void   )  [static]

Definition at line 948 of file app_chanspy.c.

References ast_unregister_application().

00949 {
00950    int res = 0;
00951 
00952    res |= ast_unregister_application(app_chan);
00953    res |= ast_unregister_application(app_chan_uniqueid);
00954    res |= ast_unregister_application(app_ext);
00955 
00956 
00957    return res;
00958 }


Variable Documentation

const char* app_chan = "ChanSpy" [static]

Definition at line 59 of file app_chanspy.c.

const char* app_chan_uniqueid = "ChanSpyChan" [static]

Definition at line 60 of file app_chanspy.c.

const char* app_ext = "ExtenSpy" [static]

Definition at line 112 of file app_chanspy.c.

Initial value:

 {
   .type = "chanspy",
   .destroy = chanspy_ds_destroy,
   .chan_fixup = chanspy_ds_chan_fixup,
}

Definition at line 427 of file app_chanspy.c.

enum { ... } chanspy_opt_args

enum { ... } chanspy_opt_flags

const char* desc_chan [static]

Definition at line 61 of file app_chanspy.c.

const char* desc_ext [static]

Definition at line 113 of file app_chanspy.c.

const char* desc_uniqueid [static]

Definition at line 91 of file app_chanspy.c.

Definition at line 167 of file app_chanspy.c.

struct ast_generator spygen [static]

Initial value:

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

Definition at line 219 of file app_chanspy.c.

const char* tdesc = "Listen to a channel, and optionally whisper into it" [static]

Definition at line 58 of file app_chanspy.c.


Generated on Thu Oct 8 21:57:31 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.8