Thu Oct 11 06:47:30 2012

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/manager.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 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


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

Referenced by common_exec().

#define NUM_SPYGROUPS   128

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

00296      {
00297    OPTION_QUIET             = (1 << 0),    /* Quiet, no announcement */
00298    OPTION_BRIDGED           = (1 << 1),    /* Only look at bridged calls */
00299    OPTION_VOLUME            = (1 << 2),    /* Specify initial volume */
00300    OPTION_GROUP             = (1 << 3),    /* Only look at channels in group */
00301    OPTION_RECORD            = (1 << 4),
00302    OPTION_WHISPER           = (1 << 5),
00303    OPTION_PRIVATE           = (1 << 6),    /* Private Whisper mode */
00304    OPTION_READONLY          = (1 << 7),    /* Don't mix the two channels */
00305    OPTION_EXIT              = (1 << 8),    /* Exit to a valid single digit extension */
00306    OPTION_ENFORCED          = (1 << 9),    /* Enforced mode */
00307    OPTION_NOTECH            = (1 << 10),   /* Skip technology name playback */
00308    OPTION_BARGE             = (1 << 11),   /* Barge mode (whisper to both channels) */
00309    OPTION_NAME              = (1 << 12),   /* Say the name of the person on whom we will spy */
00310    OPTION_DTMF_SWITCH_MODES = (1 << 13),   /*Allow numeric DTMF to switch between chanspy modes */
00311 } 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 313 of file app_chanspy.c.

00313      {
00314    OPT_ARG_VOLUME = 0,
00315    OPT_ARG_GROUP,
00316    OPT_ARG_RECORD,
00317    OPT_ARG_ENFORCED,
00318    OPT_ARG_NAME,
00319    OPT_ARG_ARRAY_SIZE,
00320 } chanspy_opt_args;


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1246 of file app_chanspy.c.

static void __unreg_module ( void   )  [static]

Definition at line 1246 of file app_chanspy.c.

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

Definition at line 432 of file app_chanspy.c.

References ast_clear_flag, ast_set_flag, OPTION_BARGE, and OPTION_WHISPER.

Referenced by channel_spy().

00433 {
00434    if (digit == '4') {
00435       ast_clear_flag(flags, OPTION_WHISPER);
00436       ast_clear_flag(flags, OPTION_BARGE);
00437    } else if (digit == '5') {
00438       ast_clear_flag(flags, OPTION_BARGE);
00439       ast_set_flag(flags, OPTION_WHISPER);
00440    } else if (digit == '6') {
00441       ast_clear_flag(flags, OPTION_WHISPER);
00442       ast_set_flag(flags, OPTION_BARGE);
00443    }
00444 }

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 446 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_copy_flags, ast_deactivate_generator(), ast_debug, AST_FLAG_END_DTMF_ONLY, AST_FLAGS_ALL, 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, EVENT_FLAG_CALL, f, chanspy_translation_helper::fd, chanspy_translation_helper::flags, ast_frame::frametype, chanspy_ds::lock, LOG_WARNING, manager_event, 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().

00448 {
00449    struct chanspy_translation_helper csth;
00450    int running = 0, res, x = 0;
00451    char inp[24] = {0};
00452    char *name;
00453    struct ast_frame *f;
00454    struct ast_silence_generator *silgen = NULL;
00455    struct ast_channel *spyee = NULL, *spyee_bridge = NULL;
00456    const char *spyer_name;
00457 
00458    ast_channel_lock(chan);
00459    spyer_name = ast_strdupa(chan->name);
00460    ast_channel_unlock(chan);
00461 
00462    ast_mutex_lock(&spyee_chanspy_ds->lock);
00463    while ((spyee = spyee_chanspy_ds->chan) && ast_channel_trylock(spyee)) {
00464       /* avoid a deadlock here, just in case spyee is masqueraded and
00465        * chanspy_ds_chan_fixup() is called with the channel locked */
00466       DEADLOCK_AVOIDANCE(&spyee_chanspy_ds->lock);
00467    }
00468    ast_mutex_unlock(&spyee_chanspy_ds->lock);
00469 
00470    if (!spyee) {
00471       return 0;
00472    }
00473 
00474    /* We now hold the channel lock on spyee */
00475 
00476    if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
00477       ast_channel_unlock(spyee);
00478       return 0;
00479    }
00480 
00481    name = ast_strdupa(spyee->name);
00482 
00483    ast_verb(2, "Spying on channel %s\n", name);
00484    manager_event(EVENT_FLAG_CALL, "ChanSpyStart",
00485          "SpyerChannel: %s\r\n"
00486          "SpyeeChannel: %s\r\n",
00487          spyer_name, name);
00488 
00489    memset(&csth, 0, sizeof(csth));
00490    ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL);
00491 
00492    ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
00493 
00494    if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
00495       ast_audiohook_destroy(&csth.spy_audiohook);
00496       ast_channel_unlock(spyee);
00497       return 0;
00498    }
00499 
00500    ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
00501    ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
00502    if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) {
00503       ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name);
00504    }
00505    if ((spyee_bridge = ast_bridged_channel(spyee))) {
00506       ast_channel_lock(spyee_bridge);
00507       if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) {
00508          ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name);
00509       }
00510       ast_channel_unlock(spyee_bridge);
00511    }
00512    ast_channel_unlock(spyee);
00513    spyee = NULL;
00514 
00515    ast_channel_lock(chan);
00516    ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
00517    ast_channel_unlock(chan);
00518 
00519    csth.volfactor = *volfactor;
00520 
00521    if (csth.volfactor) {
00522       csth.spy_audiohook.options.read_volume = csth.volfactor;
00523       csth.spy_audiohook.options.write_volume = csth.volfactor;
00524    }
00525 
00526    csth.fd = fd;
00527 
00528    if (ast_test_flag(flags, OPTION_PRIVATE))
00529       silgen = ast_channel_start_silence_generator(chan);
00530    else
00531       ast_activate_generator(chan, &spygen, &csth);
00532 
00533    /* We can no longer rely on 'spyee' being an actual channel;
00534       it can be hung up and freed out from under us. However, the
00535       channel destructor will put NULL into our csth.spy.chan
00536       field when that happens, so that is our signal that the spyee
00537       channel has gone away.
00538    */
00539 
00540    /* Note: it is very important that the ast_waitfor() be the first
00541       condition in this expression, so that if we wait for some period
00542       of time before receiving a frame from our spying channel, we check
00543       for hangup on the spied-on channel _after_ knowing that a frame
00544       has arrived, since the spied-on channel could have gone away while
00545       we were waiting
00546    */
00547    while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00548       if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
00549          running = -1;
00550          break;
00551       }
00552 
00553       if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
00554          ast_audiohook_lock(&csth.whisper_audiohook);
00555          ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00556          ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00557          ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00558          ast_audiohook_unlock(&csth.whisper_audiohook);
00559          ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00560          ast_frfree(f);
00561          continue;
00562       } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
00563          ast_audiohook_lock(&csth.whisper_audiohook);
00564          ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00565          ast_audiohook_unlock(&csth.whisper_audiohook);
00566          ast_frfree(f);
00567          continue;
00568       }
00569       
00570       res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
00571       ast_frfree(f);
00572       if (!res)
00573          continue;
00574 
00575       if (x == sizeof(inp))
00576          x = 0;
00577 
00578       if (res < 0) {
00579          running = -1;
00580          break;
00581       }
00582 
00583       if (ast_test_flag(flags, OPTION_EXIT)) {
00584          char tmp[2];
00585          tmp[0] = res;
00586          tmp[1] = '\0';
00587          if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
00588             ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
00589             pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
00590             running = -2;
00591             break;
00592          } else {
00593             ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00594          }
00595       } else if (res >= '0' && res <= '9') {
00596          if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
00597             change_spy_mode(res, flags);
00598          } else {
00599             inp[x++] = res;
00600          }
00601       }
00602 
00603       if (res == '*') {
00604          running = 0;
00605          break;
00606       } else if (res == '#') {
00607          if (!ast_strlen_zero(inp)) {
00608             running = atoi(inp);
00609             break;
00610          }
00611 
00612          (*volfactor)++;
00613          if (*volfactor > 4)
00614             *volfactor = -4;
00615          ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
00616 
00617          csth.volfactor = *volfactor;
00618          csth.spy_audiohook.options.read_volume = csth.volfactor;
00619          csth.spy_audiohook.options.write_volume = csth.volfactor;
00620       }
00621    }
00622 
00623    if (ast_test_flag(flags, OPTION_PRIVATE))
00624       ast_channel_stop_silence_generator(chan, silgen);
00625    else
00626       ast_deactivate_generator(chan);
00627 
00628    ast_channel_lock(chan);
00629    ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00630    ast_channel_unlock(chan);
00631 
00632    ast_audiohook_lock(&csth.whisper_audiohook);
00633    ast_audiohook_detach(&csth.whisper_audiohook);
00634    ast_audiohook_unlock(&csth.whisper_audiohook);
00635    ast_audiohook_destroy(&csth.whisper_audiohook);
00636    
00637    ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00638    ast_audiohook_detach(&csth.bridge_whisper_audiohook);
00639    ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00640    ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
00641 
00642    ast_audiohook_lock(&csth.spy_audiohook);
00643    ast_audiohook_detach(&csth.spy_audiohook);
00644    ast_audiohook_unlock(&csth.spy_audiohook);
00645    ast_audiohook_destroy(&csth.spy_audiohook);
00646    
00647    ast_verb(2, "Done Spying on channel %s\n", name);
00648    manager_event(EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name);
00649 
00650    return running;
00651 }

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

Definition at line 670 of file app_chanspy.c.

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

00671 {
00672    struct chanspy_ds *chanspy_ds = data;
00673    
00674    ast_mutex_lock(&chanspy_ds->lock);
00675    chanspy_ds->chan = new_chan;
00676    ast_mutex_unlock(&chanspy_ds->lock);
00677 }

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

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

Referenced by chanspy_ds_free().

00658 {
00659    struct chanspy_ds *chanspy_ds = data;
00660 
00661    /* Setting chan to be NULL is an atomic operation, but we don't want this
00662     * value to change while this lock is held.  The lock is held elsewhere
00663     * while it performs non-atomic operations with this channel pointer */
00664 
00665    ast_mutex_lock(&chanspy_ds->lock);
00666    chanspy_ds->chan = NULL;
00667    ast_mutex_unlock(&chanspy_ds->lock);
00668 }

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

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

00686 {
00687    struct ast_channel *chan;
00688 
00689    if (!chanspy_ds) {
00690       return NULL;
00691    }
00692 
00693    ast_mutex_lock(&chanspy_ds->lock);
00694    while ((chan = chanspy_ds->chan)) {
00695       struct ast_datastore *datastore;
00696 
00697       if (ast_channel_trylock(chan)) {
00698          DEADLOCK_AVOIDANCE(&chanspy_ds->lock);
00699          continue;
00700       }
00701       if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
00702          ast_channel_datastore_remove(chan, datastore);
00703          /* chanspy_ds->chan is NULL after this call */
00704          chanspy_ds_destroy(datastore->data);
00705          datastore->data = NULL;
00706          ast_datastore_free(datastore);
00707       }
00708       ast_channel_unlock(chan);
00709       break;
00710    }
00711    ast_mutex_unlock(&chanspy_ds->lock);
00712 
00713    return NULL;
00714 }

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

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

01038 {
01039    char *myenforced = NULL;
01040    char *mygroup = NULL;
01041    char *recbase = NULL;
01042    int fd = 0;
01043    struct ast_flags flags;
01044    int oldwf = 0;
01045    int volfactor = 0;
01046    int res;
01047    char *mailbox = NULL;
01048    char *name_context = NULL;
01049    AST_DECLARE_APP_ARGS(args,
01050       AST_APP_ARG(spec);
01051       AST_APP_ARG(options);
01052    );
01053    char *opts[OPT_ARG_ARRAY_SIZE];
01054 
01055    data = ast_strdupa(data);
01056    AST_STANDARD_APP_ARGS(args, data);
01057 
01058    if (args.spec && !strcmp(args.spec, "all"))
01059       args.spec = NULL;
01060 
01061    if (args.options) {
01062       ast_app_parse_options(spy_opts, &flags, opts, args.options);
01063       if (ast_test_flag(&flags, OPTION_GROUP))
01064          mygroup = opts[OPT_ARG_GROUP];
01065 
01066       if (ast_test_flag(&flags, OPTION_RECORD) &&
01067          !(recbase = opts[OPT_ARG_RECORD]))
01068          recbase = "chanspy";
01069 
01070       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
01071          int vol;
01072 
01073          if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
01074             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
01075          else
01076             volfactor = vol;
01077       }
01078 
01079       if (ast_test_flag(&flags, OPTION_PRIVATE))
01080          ast_set_flag(&flags, OPTION_WHISPER);
01081 
01082       if (ast_test_flag(&flags, OPTION_ENFORCED))
01083          myenforced = opts[OPT_ARG_ENFORCED];
01084       
01085       if (ast_test_flag(&flags, OPTION_NAME)) {
01086          if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
01087             char *delimiter;
01088             if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
01089                mailbox = opts[OPT_ARG_NAME];
01090                *delimiter++ = '\0';
01091                name_context = delimiter;
01092             } else {
01093                mailbox = opts[OPT_ARG_NAME];
01094             }
01095          }
01096       }
01097 
01098 
01099    } else
01100       ast_clear_flag(&flags, AST_FLAGS_ALL);
01101 
01102    oldwf = chan->writeformat;
01103    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01104       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01105       return -1;
01106    }
01107 
01108    if (recbase) {
01109       char filename[PATH_MAX];
01110 
01111       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
01112       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
01113          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
01114          fd = 0;
01115       }
01116    }
01117 
01118    res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
01119 
01120    if (fd)
01121       close(fd);
01122 
01123    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
01124       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01125 
01126    return res;
01127 }

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

00772 {
00773    char nameprefix[AST_NAME_STRLEN];
00774    char peer_name[AST_NAME_STRLEN + 5];
00775    char exitcontext[AST_MAX_CONTEXT] = "";
00776    signed char zero_volume = 0;
00777    int waitms;
00778    int res;
00779    char *ptr;
00780    int num;
00781    int num_spyed_upon = 1;
00782    struct chanspy_ds chanspy_ds = { 0, };
00783 
00784    if (ast_test_flag(flags, OPTION_EXIT)) {
00785       const char *c;
00786       ast_channel_lock(chan);
00787       if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
00788          ast_copy_string(exitcontext, c, sizeof(exitcontext));
00789       } else if (!ast_strlen_zero(chan->macrocontext)) {
00790          ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
00791       } else {
00792          ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
00793       }
00794       ast_channel_unlock(chan);
00795    }
00796 
00797    ast_mutex_init(&chanspy_ds.lock);
00798 
00799    snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
00800 
00801    if (chan->_state != AST_STATE_UP)
00802       ast_answer(chan);
00803 
00804    ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
00805 
00806    waitms = 100;
00807 
00808    for (;;) {
00809       struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
00810       struct ast_channel *prev = NULL, *peer = NULL;
00811 
00812       if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
00813          res = ast_streamfile(chan, "beep", chan->language);
00814          if (!res)
00815             res = ast_waitstream(chan, "");
00816          else if (res < 0) {
00817             ast_clear_flag(chan, AST_FLAG_SPYING);
00818             break;
00819          }
00820          if (!ast_strlen_zero(exitcontext)) {
00821             char tmp[2];
00822             tmp[0] = res;
00823             tmp[1] = '\0';
00824             if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00825                goto exit;
00826             else
00827                ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00828          }
00829       }
00830 
00831       res = ast_waitfordigit(chan, waitms);
00832       if (res < 0) {
00833          ast_clear_flag(chan, AST_FLAG_SPYING);
00834          break;
00835       }
00836       if (!ast_strlen_zero(exitcontext)) {
00837          char tmp[2];
00838          tmp[0] = res;
00839          tmp[1] = '\0';
00840          if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00841             goto exit;
00842          else
00843             ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00844       }
00845 
00846       /* reset for the next loop around, unless overridden later */
00847       waitms = 100;
00848       num_spyed_upon = 0;
00849 
00850       for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
00851            peer_chanspy_ds;
00852           chanspy_ds_free(peer_chanspy_ds), prev = peer,
00853            peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 
00854             next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
00855          int igrp = !mygroup;
00856          int ienf = !myenforced;
00857          char *s;
00858 
00859          peer = peer_chanspy_ds->chan;
00860 
00861          ast_mutex_unlock(&peer_chanspy_ds->lock);
00862 
00863          if (peer == prev) {
00864             ast_channel_unlock(peer);
00865             chanspy_ds_free(peer_chanspy_ds);
00866             break;
00867          }
00868 
00869          if (ast_check_hangup(chan)) {
00870             ast_channel_unlock(peer);
00871             chanspy_ds_free(peer_chanspy_ds);
00872             break;
00873          }
00874 
00875          if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
00876             ast_channel_unlock(peer);
00877             continue;
00878          }
00879 
00880          if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
00881             ast_channel_unlock(peer);
00882             continue;
00883          }
00884 
00885          if (mygroup) {
00886             int num_groups = 0;
00887             int num_mygroups = 0;
00888             char dup_group[512];
00889             char dup_mygroup[512];
00890             char *groups[NUM_SPYGROUPS];
00891             char *mygroups[NUM_SPYGROUPS];
00892             const char *group;
00893             int x;
00894             int y;
00895             ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
00896             num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
00897                ARRAY_LEN(mygroups));
00898 
00899             if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
00900                ast_copy_string(dup_group, group, sizeof(dup_group));
00901                num_groups = ast_app_separate_args(dup_group, ':', groups,
00902                   ARRAY_LEN(groups));
00903             }
00904 
00905             for (y = 0; y < num_mygroups; y++) {
00906                for (x = 0; x < num_groups; x++) {
00907                   if (!strcmp(mygroups[y], groups[x])) {
00908                      igrp = 1;
00909                      break;
00910                   }
00911                }
00912             }
00913          }
00914 
00915          if (!igrp) {
00916             ast_channel_unlock(peer);
00917             continue;
00918          }
00919 
00920          if (myenforced) {
00921             char ext[AST_CHANNEL_NAME + 3];
00922             char buffer[512];
00923             char *end;
00924 
00925             snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
00926 
00927             ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1);
00928             if ((end = strchr(ext, '-'))) {
00929                *end++ = ':';
00930                *end = '\0';
00931             }
00932 
00933             ext[0] = ':';
00934 
00935             if (strcasestr(buffer, ext)) {
00936                ienf = 1;
00937             }
00938          }
00939 
00940          if (!ienf) {
00941             ast_channel_unlock(peer);
00942             continue;
00943          }
00944 
00945          strcpy(peer_name, "spy-");
00946          strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
00947          ptr = strchr(peer_name, '/');
00948          *ptr++ = '\0';
00949          ptr = strsep(&ptr, "-");
00950 
00951          for (s = peer_name; s < ptr; s++)
00952             *s = tolower(*s);
00953          /* We have to unlock the peer channel here to avoid a deadlock.
00954           * So, when we need to dereference it again, we have to lock the 
00955           * datastore and get the pointer from there to see if the channel 
00956           * is still valid. */
00957          ast_channel_unlock(peer);
00958 
00959          if (!ast_test_flag(flags, OPTION_QUIET)) {
00960             if (ast_test_flag(flags, OPTION_NAME)) {
00961                const char *local_context = S_OR(name_context, "default");
00962                const char *local_mailbox = S_OR(mailbox, ptr);
00963                res = ast_app_sayname(chan, local_mailbox, local_context);
00964             }
00965             if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
00966                if (!ast_test_flag(flags, OPTION_NOTECH)) {
00967                   if (ast_fileexists(peer_name, NULL, NULL) > 0) {
00968                      res = ast_streamfile(chan, peer_name, chan->language);
00969                      if (!res) {
00970                         res = ast_waitstream(chan, "");
00971                      }
00972                      if (res) {
00973                         chanspy_ds_free(peer_chanspy_ds);
00974                         break;
00975                      }
00976                   } else {
00977                      res = ast_say_character_str(chan, peer_name, "", chan->language);
00978                   }
00979                }
00980                if ((num = atoi(ptr)))
00981                   ast_say_digits(chan, atoi(ptr), "", chan->language);
00982             }
00983          }
00984 
00985          res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
00986          num_spyed_upon++; 
00987 
00988          if (res == -1) {
00989             chanspy_ds_free(peer_chanspy_ds);
00990             goto exit;
00991          } else if (res == -2) {
00992             res = 0;
00993             chanspy_ds_free(peer_chanspy_ds);
00994             goto exit;
00995          } else if (res > 1 && spec) {
00996             struct ast_channel *next;
00997 
00998             snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
00999 
01000             if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
01001                peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
01002                next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
01003             } else {
01004                /* stay on this channel, if it is still valid */
01005 
01006                ast_mutex_lock(&peer_chanspy_ds->lock);
01007                if (peer_chanspy_ds->chan) {
01008                   ast_channel_lock(peer_chanspy_ds->chan);
01009                   next_chanspy_ds = peer_chanspy_ds;
01010                   peer_chanspy_ds = NULL;
01011                } else {
01012                   /* the channel is gone */
01013                   ast_mutex_unlock(&peer_chanspy_ds->lock);
01014                   next_chanspy_ds = NULL;
01015                }
01016             }
01017 
01018             peer = NULL;
01019          }
01020       }
01021       if (res == -1 || ast_check_hangup(chan))
01022          break;
01023    }
01024 exit:
01025 
01026    ast_clear_flag(chan, AST_FLAG_SPYING);
01027 
01028    ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
01029 
01030    ast_mutex_lock(&chanspy_ds.lock);
01031    ast_mutex_unlock(&chanspy_ds.lock);
01032    ast_mutex_destroy(&chanspy_ds.lock);
01033 
01034    return res;
01035 }

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

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

01130 {
01131    char *ptr, *exten = NULL;
01132    char *mygroup = NULL;
01133    char *recbase = NULL;
01134    int fd = 0;
01135    struct ast_flags flags;
01136    int oldwf = 0;
01137    int volfactor = 0;
01138    int res;
01139    char *mailbox = NULL;
01140    char *name_context = NULL;
01141    AST_DECLARE_APP_ARGS(args,
01142       AST_APP_ARG(context);
01143       AST_APP_ARG(options);
01144    );
01145 
01146    data = ast_strdupa(data);
01147 
01148    AST_STANDARD_APP_ARGS(args, data);
01149    if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
01150       exten = args.context;
01151       *ptr++ = '\0';
01152       args.context = ptr;
01153    }
01154 
01155    if (ast_strlen_zero(args.context))
01156       args.context = ast_strdupa(chan->context);
01157 
01158    if (args.options) {
01159       char *opts[OPT_ARG_ARRAY_SIZE];
01160 
01161       ast_app_parse_options(spy_opts, &flags, opts, args.options);
01162       if (ast_test_flag(&flags, OPTION_GROUP))
01163          mygroup = opts[OPT_ARG_GROUP];
01164 
01165       if (ast_test_flag(&flags, OPTION_RECORD) &&
01166          !(recbase = opts[OPT_ARG_RECORD]))
01167          recbase = "chanspy";
01168 
01169       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
01170          int vol;
01171 
01172          if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
01173             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
01174          else
01175             volfactor = vol;
01176       }
01177 
01178       if (ast_test_flag(&flags, OPTION_PRIVATE))
01179          ast_set_flag(&flags, OPTION_WHISPER);
01180 
01181       
01182       if (ast_test_flag(&flags, OPTION_NAME)) {
01183          if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
01184             char *delimiter;
01185             if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
01186                mailbox = opts[OPT_ARG_NAME];
01187                *delimiter++ = '\0';
01188                name_context = delimiter;
01189             } else {
01190                mailbox = opts[OPT_ARG_NAME];
01191             }
01192          }
01193       }
01194 
01195    } else
01196       ast_clear_flag(&flags, AST_FLAGS_ALL);
01197 
01198    oldwf = chan->writeformat;
01199    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01200       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01201       return -1;
01202    }
01203 
01204    if (recbase) {
01205       char filename[PATH_MAX];
01206 
01207       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
01208       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
01209          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
01210          fd = 0;
01211       }
01212    }
01213 
01214 
01215    res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
01216 
01217    if (fd)
01218       close(fd);
01219 
01220    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
01221       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01222 
01223    return res;
01224 }

static int load_module ( void   )  [static]

Definition at line 1236 of file app_chanspy.c.

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

01237 {
01238    int res = 0;
01239 
01240    res |= ast_register_application_xml(app_chan, chanspy_exec);
01241    res |= ast_register_application_xml(app_ext, extenspy_exec);
01242 
01243    return res;
01244 }

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

00740 {
00741    struct ast_channel *next;
00742    const size_t pseudo_len = strlen("DAHDI/pseudo");
00743 
00744 redo:
00745    if (!ast_strlen_zero(spec))
00746       next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
00747    else if (!ast_strlen_zero(exten))
00748       next = ast_walk_channel_by_exten_locked(last, exten, context);
00749    else
00750       next = ast_channel_walk_locked(last);
00751 
00752    if (!next)
00753       return NULL;
00754 
00755    if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
00756       last = next;
00757       ast_channel_unlock(next);
00758       goto redo;
00759    } else if (next == chan) {
00760       last = next;
00761       ast_channel_unlock(next);
00762       goto redo;
00763    }
00764 
00765    return setup_chanspy_ds(next, chanspy_ds);
00766 }

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

00718 {
00719    struct ast_datastore *datastore = NULL;
00720 
00721    ast_mutex_lock(&chanspy_ds->lock);
00722 
00723    if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
00724       ast_mutex_unlock(&chanspy_ds->lock);
00725       chanspy_ds = chanspy_ds_free(chanspy_ds);
00726       ast_channel_unlock(chan);
00727       return NULL;
00728    }
00729    
00730    chanspy_ds->chan = chan;
00731    datastore->data = chanspy_ds;
00732    ast_channel_datastore_add(chan, datastore);
00733 
00734    return chanspy_ds;
00735 }

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

Definition at line 351 of file app_chanspy.c.

00352 {
00353    /* just store the data pointer in the channel structure */
00354    return data;
00355 }

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

Definition at line 362 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, chanspy_translation_helper::flags, LOG_WARNING, OPTION_READONLY, ast_frame::ptr, chanspy_translation_helper::spy_audiohook, and ast_audiohook::status.

00363 {
00364    struct chanspy_translation_helper *csth = data;
00365    struct ast_frame *f, *cur;
00366 
00367    ast_audiohook_lock(&csth->spy_audiohook);
00368    if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00369       /* Channel is already gone more than likely */
00370       ast_audiohook_unlock(&csth->spy_audiohook);
00371       return -1;
00372    }
00373 
00374    if (ast_test_flag(&csth->flags, OPTION_READONLY)) {
00375       /* Option 'o' was set, so don't mix channel audio */
00376       f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, AST_FORMAT_SLINEAR);
00377    } else {
00378       f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
00379    }
00380 
00381    ast_audiohook_unlock(&csth->spy_audiohook);
00382 
00383    if (!f)
00384       return 0;
00385 
00386    for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00387       if (ast_write(chan, cur)) {
00388          ast_frfree(f);
00389          return -1;
00390       }
00391 
00392       if (csth->fd) {
00393          if (write(csth->fd, cur->data.ptr, cur->datalen) < 0) {
00394             ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00395          }
00396       }
00397    }
00398 
00399    ast_frfree(f);
00400 
00401    return 0;
00402 }

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

Definition at line 357 of file app_chanspy.c.

00358 {
00359    /* nothing to do */
00360 }

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

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

00411 {
00412    int res = 0;
00413    struct ast_channel *peer = NULL;
00414 
00415    ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
00416 
00417    ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
00418    res = ast_audiohook_attach(chan, audiohook);
00419 
00420    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 
00421       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00422    }
00423    return res;
00424 }

static int unload_module ( void   )  [static]

Definition at line 1226 of file app_chanspy.c.

References ast_unregister_application().

01227 {
01228    int res = 0;
01229 
01230    res |= ast_unregister_application(app_chan);
01231    res |= ast_unregister_application(app_ext);
01232 
01233    return res;
01234 }


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

const char* app_chan = "ChanSpy" [static]

Definition at line 292 of file app_chanspy.c.

const char* app_ext = "ExtenSpy" [static]

Definition at line 294 of file app_chanspy.c.

Definition at line 1246 of file app_chanspy.c.

Initial value:

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

Definition at line 679 of file app_chanspy.c.

enum { ... } chanspy_opt_args

enum { ... } chanspy_opt_flags

int next_unique_id_to_use = 0 [static]

Definition at line 339 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 337 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 404 of file app_chanspy.c.


Generated on Thu Oct 11 06:47:30 2012 for Asterisk - the Open Source PBX by  doxygen 1.5.6