Wed Oct 28 11:51:15 2009

Asterisk developer's documentation


app_chanspy.c File Reference

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

#include "asterisk.h"
#include <ctype.h>
#include <errno.h>
#include "asterisk/paths.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/audiohook.h"
#include "asterisk/features.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 "asterisk/options.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
#define NUM_SPYGROUPS   128

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), OPTION_READONLY = (1 << 7),
  OPTION_EXIT = (1 << 8), OPTION_ENFORCED = (1 << 9), OPTION_NOTECH = (1 << 10), OPTION_BARGE = (1 << 11),
  OPTION_NAME = (1 << 12), OPTION_DTMF_SWITCH_MODES = (1 << 13)
}
enum  {
  OPT_ARG_VOLUME = 0, OPT_ARG_GROUP, OPT_ARG_RECORD, OPT_ARG_ENFORCED,
  OPT_ARG_NAME, OPT_ARG_ARRAY_SIZE
}

Functions

static void __reg_module (void)
static void __unreg_module (void)
static void change_spy_mode (const char digit, struct ast_flags *flags)
static int channel_spy (struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, int *volfactor, int fd, struct ast_flags *flags, char *exitcontext)
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, struct ast_flags *flags, int volfactor, const int fd, const char *mygroup, const char *myenforced, const char *spec, const char *exten, const char *context, const char *mailbox, const char *name_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 , .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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, }
static const char * app_chan = "ChanSpy"
static const char * app_ext = "ExtenSpy"
static 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 }, [ 'B' ] = { .flag = OPTION_BARGE }, [ '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 }, [ 'e' ] = { .flag = OPTION_ENFORCED , .arg_index = OPT_ARG_ENFORCED + 1 }, [ 'o' ] = { .flag = OPTION_READONLY }, [ 'X' ] = { .flag = OPTION_EXIT }, [ 's' ] = { .flag = OPTION_NOTECH }, [ 'n' ] = { .flag = OPTION_NAME , .arg_index = OPT_ARG_NAME + 1 }, [ 'd' ] = { .flag = OPTION_DTMF_SWITCH_MODES },}
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 53 of file app_chanspy.c.

Referenced by common_exec().

#define NUM_SPYGROUPS   128

Definition at line 54 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 
OPTION_READONLY 
OPTION_EXIT 
OPTION_ENFORCED 
OPTION_NOTECH 
OPTION_BARGE 
OPTION_NAME 
OPTION_DTMF_SWITCH_MODES 

Definition at line 179 of file app_chanspy.c.

00179      {
00180    OPTION_QUIET             = (1 << 0),    /* Quiet, no announcement */
00181    OPTION_BRIDGED           = (1 << 1),    /* Only look at bridged calls */
00182    OPTION_VOLUME            = (1 << 2),    /* Specify initial volume */
00183    OPTION_GROUP             = (1 << 3),    /* Only look at channels in group */
00184    OPTION_RECORD            = (1 << 4),
00185    OPTION_WHISPER           = (1 << 5),
00186    OPTION_PRIVATE           = (1 << 6),    /* Private Whisper mode */
00187    OPTION_READONLY          = (1 << 7),    /* Don't mix the two channels */
00188    OPTION_EXIT              = (1 << 8),    /* Exit to a valid single digit extension */
00189    OPTION_ENFORCED          = (1 << 9),    /* Enforced mode */
00190    OPTION_NOTECH            = (1 << 10),   /* Skip technology name playback */
00191    OPTION_BARGE             = (1 << 11),   /* Barge mode (whisper to both channels) */
00192    OPTION_NAME              = (1 << 12),   /* Say the name of the person on whom we will spy */
00193    OPTION_DTMF_SWITCH_MODES = (1 << 13),   /*Allow numeric DTMF to switch between chanspy modes */
00194 } chanspy_opt_flags;

anonymous enum

Enumerator:
OPT_ARG_VOLUME 
OPT_ARG_GROUP 
OPT_ARG_RECORD 
OPT_ARG_ENFORCED 
OPT_ARG_NAME 
OPT_ARG_ARRAY_SIZE 

Definition at line 196 of file app_chanspy.c.

00196      {
00197    OPT_ARG_VOLUME = 0,
00198    OPT_ARG_GROUP,
00199    OPT_ARG_RECORD,
00200    OPT_ARG_ENFORCED,
00201    OPT_ARG_NAME,
00202    OPT_ARG_ARRAY_SIZE,
00203 } chanspy_opt_args;


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1119 of file app_chanspy.c.

static void __unreg_module ( void   )  [static]

Definition at line 1119 of file app_chanspy.c.

static void change_spy_mode ( const char  digit,
struct ast_flags flags 
) [static]

Definition at line 314 of file app_chanspy.c.

References ast_clear_flag, ast_set_flag, OPTION_BARGE, and OPTION_WHISPER.

Referenced by channel_spy().

00315 {
00316    if (digit == '4') {
00317       ast_clear_flag(flags, OPTION_WHISPER);
00318       ast_clear_flag(flags, OPTION_BARGE);
00319    } else if (digit == '5') {
00320       ast_clear_flag(flags, OPTION_BARGE);
00321       ast_set_flag(flags, OPTION_WHISPER);
00322    } else if (digit == '6') {
00323       ast_clear_flag(flags, OPTION_WHISPER);
00324       ast_set_flag(flags, OPTION_BARGE);
00325    }
00326 }

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

Definition at line 328 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_bridged_channel(), ast_channel_lock, ast_channel_start_silence_generator(), ast_channel_stop_silence_generator(), ast_channel_trylock, ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_deactivate_generator(), ast_debug, AST_FLAG_END_DTMF_ONLY, AST_FRAME_DTMF, AST_FRAME_VOICE, ast_frfree, ast_goto_if_exists(), ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_read(), ast_set_flag, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_verb, ast_waitfor(), chanspy_translation_helper::bridge_whisper_audiohook, chanspy_ds::chan, change_spy_mode(), DEADLOCK_AVOIDANCE, f, chanspy_translation_helper::fd, ast_frame::frametype, chanspy_ds::lock, LOG_WARNING, ast_channel::name, name, OPTION_BARGE, OPTION_DTMF_SWITCH_MODES, OPTION_EXIT, OPTION_PRIVATE, OPTION_WHISPER, ast_audiohook::options, pbx_builtin_setvar_helper(), ast_audiohook_options::read_volume, chanspy_translation_helper::spy_audiohook, start_spying(), ast_audiohook::status, ast_frame::subclass, chanspy_translation_helper::volfactor, chanspy_translation_helper::whisper_audiohook, and ast_audiohook_options::write_volume.

Referenced by common_exec().

00330 {
00331    struct chanspy_translation_helper csth;
00332    int running = 0, res, x = 0;
00333    char inp[24] = {0};
00334    char *name;
00335    struct ast_frame *f;
00336    struct ast_silence_generator *silgen = NULL;
00337    struct ast_channel *spyee = NULL, *spyee_bridge = NULL;
00338    const char *spyer_name;
00339 
00340    ast_channel_lock(chan);
00341    spyer_name = ast_strdupa(chan->name);
00342    ast_channel_unlock(chan);
00343 
00344    ast_mutex_lock(&spyee_chanspy_ds->lock);
00345    while ((spyee = spyee_chanspy_ds->chan) && ast_channel_trylock(spyee)) {
00346       /* avoid a deadlock here, just in case spyee is masqueraded and
00347        * chanspy_ds_chan_fixup() is called with the channel locked */
00348       DEADLOCK_AVOIDANCE(&spyee_chanspy_ds->lock);
00349    }
00350    ast_mutex_unlock(&spyee_chanspy_ds->lock);
00351 
00352    if (!spyee)
00353       return 0;
00354 
00355    /* We now hold the channel lock on spyee */
00356 
00357    if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
00358       ast_channel_unlock(spyee);
00359       return 0;
00360    }
00361 
00362    name = ast_strdupa(spyee->name);
00363    ast_verb(2, "Spying on channel %s\n", name);
00364 
00365    memset(&csth, 0, sizeof(csth));
00366 
00367    ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
00368 
00369    if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
00370       ast_audiohook_destroy(&csth.spy_audiohook);
00371       ast_channel_unlock(spyee);
00372       return 0;
00373    }
00374 
00375    ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
00376    ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
00377    if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) {
00378       ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name);
00379    }
00380    if ((spyee_bridge = ast_bridged_channel(spyee))) {
00381       ast_channel_lock(spyee_bridge);
00382       if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) {
00383          ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name);
00384       }
00385       ast_channel_unlock(spyee_bridge);
00386    }
00387    ast_channel_unlock(spyee);
00388    spyee = NULL;
00389 
00390    ast_channel_lock(chan);
00391    ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
00392    ast_channel_unlock(chan);
00393 
00394    csth.volfactor = *volfactor;
00395 
00396    if (csth.volfactor) {
00397       csth.spy_audiohook.options.read_volume = csth.volfactor;
00398       csth.spy_audiohook.options.write_volume = csth.volfactor;
00399    }
00400 
00401    csth.fd = fd;
00402 
00403    if (ast_test_flag(flags, OPTION_PRIVATE))
00404       silgen = ast_channel_start_silence_generator(chan);
00405    else
00406       ast_activate_generator(chan, &spygen, &csth);
00407 
00408    /* We can no longer rely on 'spyee' being an actual channel;
00409       it can be hung up and freed out from under us. However, the
00410       channel destructor will put NULL into our csth.spy.chan
00411       field when that happens, so that is our signal that the spyee
00412       channel has gone away.
00413    */
00414 
00415    /* Note: it is very important that the ast_waitfor() be the first
00416       condition in this expression, so that if we wait for some period
00417       of time before receiving a frame from our spying channel, we check
00418       for hangup on the spied-on channel _after_ knowing that a frame
00419       has arrived, since the spied-on channel could have gone away while
00420       we were waiting
00421    */
00422    while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00423       if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
00424          running = -1;
00425          break;
00426       }
00427 
00428       if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
00429          ast_audiohook_lock(&csth.whisper_audiohook);
00430          ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00431          ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00432          ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00433          ast_audiohook_unlock(&csth.whisper_audiohook);
00434          ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00435          ast_frfree(f);
00436          continue;
00437       } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
00438          ast_audiohook_lock(&csth.whisper_audiohook);
00439          ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00440          ast_audiohook_unlock(&csth.whisper_audiohook);
00441          ast_frfree(f);
00442          continue;
00443       }
00444       
00445       res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
00446       ast_frfree(f);
00447       if (!res)
00448          continue;
00449 
00450       if (x == sizeof(inp))
00451          x = 0;
00452 
00453       if (res < 0) {
00454          running = -1;
00455          break;
00456       }
00457 
00458       if (ast_test_flag(flags, OPTION_EXIT)) {
00459          char tmp[2];
00460          tmp[0] = res;
00461          tmp[1] = '\0';
00462          if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
00463             ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
00464             pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
00465             running = -2;
00466             break;
00467          } else {
00468             ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00469          }
00470       } else if (res >= '0' && res <= '9') {
00471          if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
00472             change_spy_mode(res, flags);
00473          } else {
00474             inp[x++] = res;
00475          }
00476       }
00477 
00478       if (res == '*') {
00479          running = 0;
00480          break;
00481       } else if (res == '#') {
00482          if (!ast_strlen_zero(inp)) {
00483             running = atoi(inp);
00484             break;
00485          }
00486 
00487          (*volfactor)++;
00488          if (*volfactor > 4)
00489             *volfactor = -4;
00490          ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
00491 
00492          csth.volfactor = *volfactor;
00493          csth.spy_audiohook.options.read_volume = csth.volfactor;
00494          csth.spy_audiohook.options.write_volume = csth.volfactor;
00495       }
00496    }
00497 
00498    if (ast_test_flag(flags, OPTION_PRIVATE))
00499       ast_channel_stop_silence_generator(chan, silgen);
00500    else
00501       ast_deactivate_generator(chan);
00502 
00503    ast_channel_lock(chan);
00504    ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00505    ast_channel_unlock(chan);
00506 
00507    ast_audiohook_lock(&csth.whisper_audiohook);
00508    ast_audiohook_detach(&csth.whisper_audiohook);
00509    ast_audiohook_unlock(&csth.whisper_audiohook);
00510    ast_audiohook_destroy(&csth.whisper_audiohook);
00511    
00512    ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00513    ast_audiohook_detach(&csth.bridge_whisper_audiohook);
00514    ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00515    ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
00516 
00517    ast_audiohook_lock(&csth.spy_audiohook);
00518    ast_audiohook_detach(&csth.spy_audiohook);
00519    ast_audiohook_unlock(&csth.spy_audiohook);
00520    ast_audiohook_destroy(&csth.spy_audiohook);
00521    
00522    ast_verb(2, "Done Spying on channel %s\n", name);
00523 
00524    return running;
00525 }

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

Definition at line 544 of file app_chanspy.c.

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

00545 {
00546    struct chanspy_ds *chanspy_ds = data;
00547    
00548    ast_mutex_lock(&chanspy_ds->lock);
00549    chanspy_ds->chan = new_chan;
00550    ast_mutex_unlock(&chanspy_ds->lock);
00551 }

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

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

Referenced by chanspy_ds_free().

00532 {
00533    struct chanspy_ds *chanspy_ds = data;
00534 
00535    /* Setting chan to be NULL is an atomic operation, but we don't want this
00536     * value to change while this lock is held.  The lock is held elsewhere
00537     * while it performs non-atomic operations with this channel pointer */
00538 
00539    ast_mutex_lock(&chanspy_ds->lock);
00540    chanspy_ds->chan = NULL;
00541    ast_mutex_unlock(&chanspy_ds->lock);
00542 }

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

Definition at line 559 of file app_chanspy.c.

References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_trylock, ast_channel_unlock, ast_datastore_free(), ast_mutex_lock(), ast_mutex_unlock(), chanspy_ds::chan, chan, chanspy_ds_destroy(), ast_datastore::data, DEADLOCK_AVOIDANCE, chanspy_ds::lock, and chanspy_ds::unique_id.

Referenced by common_exec(), and setup_chanspy_ds().

00560 {
00561    struct ast_channel *chan;
00562 
00563    if (!chanspy_ds) {
00564       return NULL;
00565    }
00566 
00567    ast_mutex_lock(&chanspy_ds->lock);
00568    while ((chan = chanspy_ds->chan)) {
00569       struct ast_datastore *datastore;
00570 
00571       if (ast_channel_trylock(chan)) {
00572          DEADLOCK_AVOIDANCE(&chanspy_ds->lock);
00573          continue;
00574       }
00575       if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
00576          ast_channel_datastore_remove(chan, datastore);
00577          /* chanspy_ds->chan is NULL after this call */
00578          chanspy_ds_destroy(datastore->data);
00579          datastore->data = NULL;
00580          ast_datastore_free(datastore);
00581       }
00582       ast_channel_unlock(chan);
00583       break;
00584    }
00585    ast_mutex_unlock(&chanspy_ds->lock);
00586 
00587    return NULL;
00588 }

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

Definition at line 910 of file app_chanspy.c.

References AST_APP_ARG, ast_app_parse_options(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_DECLARE_APP_ARGS, AST_FILE_MODE, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_set_flag, ast_set_write_format(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_test_flag, common_exec(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, mailbox, OPT_ARG_ARRAY_SIZE, OPT_ARG_ENFORCED, OPT_ARG_GROUP, OPT_ARG_NAME, OPT_ARG_RECORD, OPT_ARG_VOLUME, OPTION_ENFORCED, OPTION_GROUP, OPTION_NAME, OPTION_PRIVATE, OPTION_RECORD, OPTION_VOLUME, OPTION_WHISPER, spy_opts, and ast_channel::writeformat.

Referenced by load_module().

00911 {
00912    char *myenforced = NULL;
00913    char *mygroup = NULL;
00914    char *recbase = NULL;
00915    int fd = 0;
00916    struct ast_flags flags;
00917    int oldwf = 0;
00918    int volfactor = 0;
00919    int res;
00920    char *mailbox = NULL;
00921    char *name_context = NULL;
00922    AST_DECLARE_APP_ARGS(args,
00923       AST_APP_ARG(spec);
00924       AST_APP_ARG(options);
00925    );
00926    char *opts[OPT_ARG_ARRAY_SIZE];
00927 
00928    data = ast_strdupa(data);
00929    AST_STANDARD_APP_ARGS(args, data);
00930 
00931    if (args.spec && !strcmp(args.spec, "all"))
00932       args.spec = NULL;
00933 
00934    if (args.options) {
00935       ast_app_parse_options(spy_opts, &flags, opts, args.options);
00936       if (ast_test_flag(&flags, OPTION_GROUP))
00937          mygroup = opts[OPT_ARG_GROUP];
00938 
00939       if (ast_test_flag(&flags, OPTION_RECORD) &&
00940          !(recbase = opts[OPT_ARG_RECORD]))
00941          recbase = "chanspy";
00942 
00943       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00944          int vol;
00945 
00946          if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
00947             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00948          else
00949             volfactor = vol;
00950       }
00951 
00952       if (ast_test_flag(&flags, OPTION_PRIVATE))
00953          ast_set_flag(&flags, OPTION_WHISPER);
00954 
00955       if (ast_test_flag(&flags, OPTION_ENFORCED))
00956          myenforced = opts[OPT_ARG_ENFORCED];
00957       
00958       if (ast_test_flag(&flags, OPTION_NAME)) {
00959          if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
00960             char *delimiter;
00961             if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
00962                mailbox = opts[OPT_ARG_NAME];
00963                *delimiter++ = '\0';
00964                name_context = delimiter;
00965             } else {
00966                mailbox = opts[OPT_ARG_NAME];
00967             }
00968          }
00969       }
00970 
00971 
00972    } else
00973       ast_clear_flag(&flags, AST_FLAGS_ALL);
00974 
00975    oldwf = chan->writeformat;
00976    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00977       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00978       return -1;
00979    }
00980 
00981    if (recbase) {
00982       char filename[PATH_MAX];
00983 
00984       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
00985       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
00986          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
00987          fd = 0;
00988       }
00989    }
00990 
00991    res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
00992 
00993    if (fd)
00994       close(fd);
00995 
00996    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
00997       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00998 
00999    return res;
01000 }

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

Definition at line 642 of file app_chanspy.c.

References ast_channel::_state, ARRAY_LEN, ast_answer(), ast_app_sayname(), ast_app_separate_args, ast_atomic_fetchadd_int(), ast_bridged_channel(), ast_channel_lock, AST_CHANNEL_NAME, ast_channel_setoption(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_copy_string(), ast_debug, ast_fileexists(), AST_FLAG_SPYING, ast_get_channel_by_name_prefix_locked(), ast_goto_if_exists(), AST_MAX_CONTEXT, 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_strlen_zero(), ast_test_flag, ast_waitfordigit(), ast_waitstream(), channel_spy(), chanspy_ds_free(), ast_channel::context, exitcontext, ext, ast_channel::language, chanspy_ds::lock, ast_channel::macrocontext, ast_channel::next, next_channel(), num, NUM_SPYGROUPS, OPTION_BRIDGED, OPTION_EXIT, OPTION_NAME, OPTION_NOTECH, OPTION_QUIET, pbx_builtin_getvar_helper(), s, S_OR, setup_chanspy_ds(), strcasestr(), strsep(), and chanspy_ds::unique_id.

Referenced by chanspy_exec(), and extenspy_exec().

00646 {
00647    char nameprefix[AST_NAME_STRLEN];
00648    char peer_name[AST_NAME_STRLEN + 5];
00649    char exitcontext[AST_MAX_CONTEXT] = "";
00650    signed char zero_volume = 0;
00651    int waitms;
00652    int res;
00653    char *ptr;
00654    int num;
00655    int num_spyed_upon = 1;
00656    struct chanspy_ds chanspy_ds = { 0, };
00657 
00658    if (ast_test_flag(flags, OPTION_EXIT)) {
00659       const char *c;
00660       ast_channel_lock(chan);
00661       if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
00662          ast_copy_string(exitcontext, c, sizeof(exitcontext));
00663       } else if (!ast_strlen_zero(chan->macrocontext)) {
00664          ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
00665       } else {
00666          ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
00667       }
00668       ast_channel_unlock(chan);
00669    }
00670 
00671    ast_mutex_init(&chanspy_ds.lock);
00672 
00673    snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
00674 
00675    if (chan->_state != AST_STATE_UP)
00676       ast_answer(chan);
00677 
00678    ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
00679 
00680    waitms = 100;
00681 
00682    for (;;) {
00683       struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
00684       struct ast_channel *prev = NULL, *peer = NULL;
00685 
00686       if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
00687          res = ast_streamfile(chan, "beep", chan->language);
00688          if (!res)
00689             res = ast_waitstream(chan, "");
00690          else if (res < 0) {
00691             ast_clear_flag(chan, AST_FLAG_SPYING);
00692             break;
00693          }
00694          if (!ast_strlen_zero(exitcontext)) {
00695             char tmp[2];
00696             tmp[0] = res;
00697             tmp[1] = '\0';
00698             if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00699                goto exit;
00700             else
00701                ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00702          }
00703       }
00704 
00705       res = ast_waitfordigit(chan, waitms);
00706       if (res < 0) {
00707          ast_clear_flag(chan, AST_FLAG_SPYING);
00708          break;
00709       }
00710       if (!ast_strlen_zero(exitcontext)) {
00711          char tmp[2];
00712          tmp[0] = res;
00713          tmp[1] = '\0';
00714          if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00715             goto exit;
00716          else
00717             ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00718       }
00719 
00720       /* reset for the next loop around, unless overridden later */
00721       waitms = 100;
00722       num_spyed_upon = 0;
00723 
00724       for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
00725            peer_chanspy_ds;
00726           chanspy_ds_free(peer_chanspy_ds), prev = peer,
00727            peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 
00728             next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
00729          int igrp = !mygroup;
00730          int ienf = !myenforced;
00731          char *s;
00732 
00733          peer = peer_chanspy_ds->chan;
00734 
00735          ast_mutex_unlock(&peer_chanspy_ds->lock);
00736 
00737          if (peer == prev) {
00738             ast_channel_unlock(peer);
00739             chanspy_ds_free(peer_chanspy_ds);
00740             break;
00741          }
00742 
00743          if (ast_check_hangup(chan)) {
00744             ast_channel_unlock(peer);
00745             chanspy_ds_free(peer_chanspy_ds);
00746             break;
00747          }
00748 
00749          if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
00750             ast_channel_unlock(peer);
00751             continue;
00752          }
00753 
00754          if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
00755             ast_channel_unlock(peer);
00756             continue;
00757          }
00758 
00759          if (mygroup) {
00760             int num_groups = 0;
00761             int num_mygroups = 0;
00762             char dup_group[512];
00763             char dup_mygroup[512];
00764             char *groups[NUM_SPYGROUPS];
00765             char *mygroups[NUM_SPYGROUPS];
00766             const char *group;
00767             int x;
00768             int y;
00769             ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
00770             num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
00771                ARRAY_LEN(mygroups));
00772 
00773             if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
00774                ast_copy_string(dup_group, group, sizeof(dup_group));
00775                num_groups = ast_app_separate_args(dup_group, ':', groups,
00776                   ARRAY_LEN(groups));
00777             }
00778 
00779             for (y = 0; y < num_mygroups; y++) {
00780                for (x = 0; x < num_groups; x++) {
00781                   if (!strcmp(mygroups[y], groups[x])) {
00782                      igrp = 1;
00783                      break;
00784                   }
00785                }
00786             }
00787          }
00788 
00789          if (!igrp) {
00790             ast_channel_unlock(peer);
00791             continue;
00792          }
00793 
00794          if (myenforced) {
00795             char ext[AST_CHANNEL_NAME + 3];
00796             char buffer[512];
00797             char *end;
00798 
00799             snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
00800 
00801             ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1);
00802             if ((end = strchr(ext, '-'))) {
00803                *end++ = ':';
00804                *end = '\0';
00805             }
00806 
00807             ext[0] = ':';
00808 
00809             if (strcasestr(buffer, ext)) {
00810                ienf = 1;
00811             }
00812          }
00813 
00814          if (!ienf) {
00815             continue;
00816          }
00817 
00818          strcpy(peer_name, "spy-");
00819          strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
00820          ptr = strchr(peer_name, '/');
00821          *ptr++ = '\0';
00822          ptr = strsep(&ptr, "-");
00823 
00824          for (s = peer_name; s < ptr; s++)
00825             *s = tolower(*s);
00826          /* We have to unlock the peer channel here to avoid a deadlock.
00827           * So, when we need to dereference it again, we have to lock the 
00828           * datastore and get the pointer from there to see if the channel 
00829           * is still valid. */
00830          ast_channel_unlock(peer);
00831 
00832          if (!ast_test_flag(flags, OPTION_QUIET)) {
00833             if (ast_test_flag(flags, OPTION_NAME)) {
00834                const char *local_context = S_OR(name_context, "default");
00835                const char *local_mailbox = S_OR(mailbox, ptr);
00836                res = ast_app_sayname(chan, local_mailbox, local_context);
00837             }
00838             if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
00839                if (!ast_test_flag(flags, OPTION_NOTECH)) {
00840                   if (ast_fileexists(peer_name, NULL, NULL) != -1) {
00841                      res = ast_streamfile(chan, peer_name, chan->language);
00842                      if (!res) {
00843                         res = ast_waitstream(chan, "");
00844                      }
00845                      if (res) {
00846                         chanspy_ds_free(peer_chanspy_ds);
00847                         break;
00848                      }
00849                   } else {
00850                      res = ast_say_character_str(chan, peer_name, "", chan->language);
00851                   }
00852                }
00853                if ((num = atoi(ptr)))
00854                   ast_say_digits(chan, atoi(ptr), "", chan->language);
00855             }
00856          }
00857 
00858          res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
00859          num_spyed_upon++; 
00860 
00861          if (res == -1) {
00862             chanspy_ds_free(peer_chanspy_ds);
00863             goto exit;
00864          } else if (res == -2) {
00865             res = 0;
00866             chanspy_ds_free(peer_chanspy_ds);
00867             goto exit;
00868          } else if (res > 1 && spec) {
00869             struct ast_channel *next;
00870 
00871             snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
00872 
00873             if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
00874                peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
00875                next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
00876             } else {
00877                /* stay on this channel, if it is still valid */
00878 
00879                ast_mutex_lock(&peer_chanspy_ds->lock);
00880                if (peer_chanspy_ds->chan) {
00881                   ast_channel_lock(peer_chanspy_ds->chan);
00882                   next_chanspy_ds = peer_chanspy_ds;
00883                   peer_chanspy_ds = NULL;
00884                } else {
00885                   /* the channel is gone */
00886                   ast_mutex_unlock(&peer_chanspy_ds->lock);
00887                   next_chanspy_ds = NULL;
00888                }
00889             }
00890 
00891             peer = NULL;
00892          }
00893       }
00894       if (res == -1 || ast_check_hangup(chan))
00895          break;
00896    }
00897 exit:
00898 
00899    ast_clear_flag(chan, AST_FLAG_SPYING);
00900 
00901    ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00902 
00903    ast_mutex_lock(&chanspy_ds.lock);
00904    ast_mutex_unlock(&chanspy_ds.lock);
00905    ast_mutex_destroy(&chanspy_ds.lock);
00906 
00907    return res;
00908 }

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

Definition at line 1002 of file app_chanspy.c.

References AST_APP_ARG, ast_app_parse_options(), ast_clear_flag, ast_config_AST_MONITOR_DIR, AST_DECLARE_APP_ARGS, AST_FILE_MODE, AST_FLAGS_ALL, AST_FORMAT_SLINEAR, ast_log(), ast_set_flag, ast_set_write_format(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_test_flag, common_exec(), ast_channel::context, context, exten, LOG_ERROR, LOG_NOTICE, LOG_WARNING, mailbox, OPT_ARG_ARRAY_SIZE, OPT_ARG_GROUP, OPT_ARG_NAME, OPT_ARG_RECORD, OPT_ARG_VOLUME, OPTION_GROUP, OPTION_NAME, OPTION_PRIVATE, OPTION_RECORD, OPTION_VOLUME, OPTION_WHISPER, spy_opts, and ast_channel::writeformat.

Referenced by load_module().

01003 {
01004    char *ptr, *exten = NULL;
01005    char *mygroup = NULL;
01006    char *recbase = NULL;
01007    int fd = 0;
01008    struct ast_flags flags;
01009    int oldwf = 0;
01010    int volfactor = 0;
01011    int res;
01012    char *mailbox = NULL;
01013    char *name_context = NULL;
01014    AST_DECLARE_APP_ARGS(args,
01015       AST_APP_ARG(context);
01016       AST_APP_ARG(options);
01017    );
01018 
01019    data = ast_strdupa(data);
01020 
01021    AST_STANDARD_APP_ARGS(args, data);
01022    if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
01023       exten = args.context;
01024       *ptr++ = '\0';
01025       args.context = ptr;
01026    }
01027 
01028    if (ast_strlen_zero(args.context))
01029       args.context = ast_strdupa(chan->context);
01030 
01031    if (args.options) {
01032       char *opts[OPT_ARG_ARRAY_SIZE];
01033 
01034       ast_app_parse_options(spy_opts, &flags, opts, args.options);
01035       if (ast_test_flag(&flags, OPTION_GROUP))
01036          mygroup = opts[OPT_ARG_GROUP];
01037 
01038       if (ast_test_flag(&flags, OPTION_RECORD) &&
01039          !(recbase = opts[OPT_ARG_RECORD]))
01040          recbase = "chanspy";
01041 
01042       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
01043          int vol;
01044 
01045          if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
01046             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
01047          else
01048             volfactor = vol;
01049       }
01050 
01051       if (ast_test_flag(&flags, OPTION_PRIVATE))
01052          ast_set_flag(&flags, OPTION_WHISPER);
01053 
01054       
01055       if (ast_test_flag(&flags, OPTION_NAME)) {
01056          if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
01057             char *delimiter;
01058             if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
01059                mailbox = opts[OPT_ARG_NAME];
01060                *delimiter++ = '\0';
01061                name_context = delimiter;
01062             } else {
01063                mailbox = opts[OPT_ARG_NAME];
01064             }
01065          }
01066       }
01067 
01068    } else
01069       ast_clear_flag(&flags, AST_FLAGS_ALL);
01070 
01071    oldwf = chan->writeformat;
01072    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01073       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01074       return -1;
01075    }
01076 
01077    if (recbase) {
01078       char filename[PATH_MAX];
01079 
01080       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
01081       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
01082          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
01083          fd = 0;
01084       }
01085    }
01086 
01087 
01088    res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
01089 
01090    if (fd)
01091       close(fd);
01092 
01093    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
01094       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01095 
01096    return res;
01097 }

static int load_module ( void   )  [static]

Definition at line 1109 of file app_chanspy.c.

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

01110 {
01111    int res = 0;
01112 
01113    res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
01114    res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
01115 
01116    return res;
01117 }

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, read]

Definition at line 611 of file app_chanspy.c.

References ast_channel_unlock, ast_channel_walk_locked(), ast_strlen_zero(), ast_walk_channel_by_exten_locked(), ast_walk_channel_by_name_prefix_locked(), ast_channel::name, ast_channel::next, and setup_chanspy_ds().

Referenced by common_exec().

00614 {
00615    struct ast_channel *next;
00616    const size_t pseudo_len = strlen("DAHDI/pseudo");
00617 
00618 redo:
00619    if (!ast_strlen_zero(spec))
00620       next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
00621    else if (!ast_strlen_zero(exten))
00622       next = ast_walk_channel_by_exten_locked(last, exten, context);
00623    else
00624       next = ast_channel_walk_locked(last);
00625 
00626    if (!next)
00627       return NULL;
00628 
00629    if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
00630       last = next;
00631       ast_channel_unlock(next);
00632       goto redo;
00633    } else if (next == chan) {
00634       last = next;
00635       ast_channel_unlock(next);
00636       goto redo;
00637    }
00638 
00639    return setup_chanspy_ds(next, chanspy_ds);
00640 }

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

References ast_channel_datastore_add(), ast_channel_unlock, ast_datastore_alloc, 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().

00592 {
00593    struct ast_datastore *datastore = NULL;
00594 
00595    ast_mutex_lock(&chanspy_ds->lock);
00596 
00597    if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
00598       ast_mutex_unlock(&chanspy_ds->lock);
00599       chanspy_ds = chanspy_ds_free(chanspy_ds);
00600       ast_channel_unlock(chan);
00601       return NULL;
00602    }
00603    
00604    chanspy_ds->chan = chan;
00605    datastore->data = chanspy_ds;
00606    ast_channel_datastore_add(chan, datastore);
00607 
00608    return chanspy_ds;
00609 }

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

Definition at line 233 of file app_chanspy.c.

00234 {
00235    /* just store the data pointer in the channel structure */
00236    return data;
00237 }

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

Definition at line 244 of file app_chanspy.c.

References AST_AUDIOHOOK_DIRECTION_BOTH, AST_AUDIOHOOK_DIRECTION_READ, ast_audiohook_lock, ast_audiohook_read_frame(), AST_AUDIOHOOK_STATUS_RUNNING, ast_audiohook_unlock, AST_FORMAT_SLINEAR, ast_frfree, AST_LIST_NEXT, ast_log(), ast_test_flag, ast_write(), ast_frame::data, ast_frame::datalen, errno, f, chanspy_translation_helper::fd, ast_frame::frame_list, LOG_WARNING, OPTION_READONLY, ast_frame::ptr, chanspy_translation_helper::spy_audiohook, and ast_audiohook::status.

00245 {
00246    struct chanspy_translation_helper *csth = data;
00247    struct ast_frame *f, *cur;
00248 
00249    ast_audiohook_lock(&csth->spy_audiohook);
00250    if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00251       /* Channel is already gone more than likely */
00252       ast_audiohook_unlock(&csth->spy_audiohook);
00253       return -1;
00254    }
00255 
00256    if (ast_test_flag(chan, OPTION_READONLY)) {
00257       /* Option 'o' was set, so don't mix channel audio */
00258       f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, AST_FORMAT_SLINEAR);
00259    } else {
00260       f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
00261    }
00262 
00263    ast_audiohook_unlock(&csth->spy_audiohook);
00264 
00265    if (!f)
00266       return 0;
00267 
00268    for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00269       if (ast_write(chan, cur)) {
00270          ast_frfree(f);
00271          return -1;
00272       }
00273 
00274       if (csth->fd) {
00275          if (write(csth->fd, cur->data.ptr, cur->datalen) < 0) {
00276             ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00277          }
00278       }
00279    }
00280 
00281    ast_frfree(f);
00282 
00283    return 0;
00284 }

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

Definition at line 239 of file app_chanspy.c.

00240 {
00241    /* nothing to do */
00242 }

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

Definition at line 292 of file app_chanspy.c.

References ast_audiohook_attach(), AST_AUDIOHOOK_SMALL_QUEUE, AST_AUDIOHOOK_TRIGGER_SYNC, ast_bridged_channel(), AST_FLAG_NBRIDGE, ast_log(), ast_set_flag, ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, ast_test_flag, LOG_NOTICE, and ast_channel::name.

Referenced by channel_spy().

00293 {
00294    int res = 0;
00295    struct ast_channel *peer = NULL;
00296 
00297    ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
00298 
00299    ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
00300    res = ast_audiohook_attach(chan, audiohook);
00301 
00302    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 
00303       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00304    }
00305    return res;
00306 }

static int unload_module ( void   )  [static]

Definition at line 1099 of file app_chanspy.c.

References ast_unregister_application().

01100 {
01101    int res = 0;
01102 
01103    res |= ast_unregister_application(app_chan);
01104    res |= ast_unregister_application(app_ext);
01105 
01106    return res;
01107 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } [static]

Definition at line 1119 of file app_chanspy.c.

const char* app_chan = "ChanSpy" [static]

Definition at line 57 of file app_chanspy.c.

const char* app_ext = "ExtenSpy" [static]

Definition at line 122 of file app_chanspy.c.

Definition at line 1119 of file app_chanspy.c.

Initial value:

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

Definition at line 553 of file app_chanspy.c.

enum { ... } chanspy_opt_args

enum { ... } chanspy_opt_flags

const char* desc_chan [static]

Definition at line 58 of file app_chanspy.c.

const char* desc_ext [static]

Definition at line 123 of file app_chanspy.c.

int next_unique_id_to_use = 0 [static]

Definition at line 222 of file app_chanspy.c.

struct ast_app_option spy_opts[128] = { [ 'q' ] = { .flag = OPTION_QUIET }, [ 'b' ] = { .flag = OPTION_BRIDGED }, [ 'B' ] = { .flag = OPTION_BARGE }, [ '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 }, [ 'e' ] = { .flag = OPTION_ENFORCED , .arg_index = OPT_ARG_ENFORCED + 1 }, [ 'o' ] = { .flag = OPTION_READONLY }, [ 'X' ] = { .flag = OPTION_EXIT }, [ 's' ] = { .flag = OPTION_NOTECH }, [ 'n' ] = { .flag = OPTION_NAME , .arg_index = OPT_ARG_NAME + 1 }, [ 'd' ] = { .flag = OPTION_DTMF_SWITCH_MODES },} [static]

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

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

Definition at line 56 of file app_chanspy.c.


Generated on Wed Oct 28 11:51:16 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6