Mon Nov 24 15:33:55 2008

Asterisk developer's documentation


app_chanspy.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
00005  * Copyright (C) 2005 - 2008, Digium, Inc.
00006  *
00007  * A license has been granted to Digium (via disclaimer) for the use of
00008  * this code.
00009  *
00010  * See http://www.asterisk.org for more information about
00011  * the Asterisk project. Please do not directly contact
00012  * any of the maintainers of this project for assistance;
00013  * the project provides a web site, mailing lists and IRC
00014  * channels for your use.
00015  *
00016  * This program is free software, distributed under the terms of
00017  * the GNU General Public License Version 2. See the LICENSE file
00018  * at the top of the source tree.
00019  */
00020 
00021 /*! \file
00022  *
00023  * \brief ChanSpy: Listen in on any channel.
00024  *
00025  * \author Anthony Minessale II <anthmct@yahoo.com>
00026  * \author Joshua Colp <jcolp@digium.com>
00027  * \author Russell Bryant <russell@digium.com>
00028  *
00029  * \ingroup applications
00030  */
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 139213 $")
00035 
00036 #include <stdlib.h>
00037 #include <stdio.h>
00038 #include <string.h>
00039 #include <unistd.h>
00040 #include <ctype.h>
00041 
00042 #include "asterisk/file.h"
00043 #include "asterisk/logger.h"
00044 #include "asterisk/channel.h"
00045 #include "asterisk/audiohook.h"
00046 #include "asterisk/features.h"
00047 #include "asterisk/options.h"
00048 #include "asterisk/app.h"
00049 #include "asterisk/utils.h"
00050 #include "asterisk/say.h"
00051 #include "asterisk/pbx.h"
00052 #include "asterisk/translate.h"
00053 #include "asterisk/module.h"
00054 #include "asterisk/lock.h"
00055 
00056 #define AST_NAME_STRLEN 256
00057 
00058 /* "Zap/pseudo" is ten characters.
00059  * "DAHDI/pseudo" is twelve characters.
00060  */
00061 
00062 static const char *tdesc = "Listen to a channel, and optionally whisper into it";
00063 static const char *app_chan = "ChanSpy";
00064 static const char *desc_chan = 
00065 "  ChanSpy([chanprefix][|options]): This application is used to listen to the\n"
00066 "audio from an Asterisk channel. This includes the audio coming in and\n"
00067 "out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
00068 "only channels beginning with this string will be spied upon.\n"
00069 "  While spying, the following actions may be performed:\n"
00070 "    - Dialing # cycles the volume level.\n"
00071 "    - Dialing * will stop spying and look for another channel to spy on.\n"
00072 "    - Dialing a series of digits followed by # builds a channel name to append\n"
00073 "      to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
00074 "      the digits '1234#' while spying will begin spying on the channel\n"
00075 "      'Agent/1234'.\n"
00076 "  Options:\n"
00077 "    b             - Only spy on channels involved in a bridged call.\n"
00078 "    g(grp)        - Match only channels where their ${SPYGROUP} variable is set to\n"
00079 "                    contain 'grp' in an optional : delimited list.\n"
00080 "    q             - Don't play a beep when beginning to spy on a channel, or speak the\n"
00081 "                    selected channel name.\n"
00082 "    r[(basename)] - Record the session to the monitor spool directory. An\n"
00083 "                    optional base for the filename may be specified. The\n"
00084 "                    default is 'chanspy'.\n"
00085 "    v([value])    - Adjust the initial volume in the range from -4 to 4. A\n"
00086 "                    negative value refers to a quieter setting.\n"
00087 "    w             - Enable 'whisper' mode, so the spying channel can talk to\n"
00088 "                    the spied-on channel.\n"
00089 "    W             - Enable 'private whisper' mode, so the spying channel can\n"
00090 "                    talk to the spied-on channel but cannot listen to that\n"
00091 "                    channel.\n"
00092 ;
00093 
00094 static const char *app_ext = "ExtenSpy";
00095 static const char *desc_ext = 
00096 "  ExtenSpy(exten[@context][|options]): This application is used to listen to the\n"
00097 "audio from an Asterisk channel. This includes the audio coming in and\n"
00098 "out of the channel being spied on. Only channels created by outgoing calls for the\n"
00099 "specified extension will be selected for spying. If the optional context is not\n"
00100 "supplied, the current channel's context will be used.\n"
00101 "  While spying, the following actions may be performed:\n"
00102 "    - Dialing # cycles the volume level.\n"
00103 "    - Dialing * will stop spying and look for another channel to spy on.\n"
00104 "  Options:\n"
00105 "    b             - Only spy on channels involved in a bridged call.\n"
00106 "    g(grp)        - Match only channels where their ${SPYGROUP} variable is set to\n"
00107 "                    contain 'grp' in an optional : delimited list.\n"
00108 "    q             - Don't play a beep when beginning to spy on a channel, or speak the\n"
00109 "                    selected channel name.\n"
00110 "    r[(basename)] - Record the session to the monitor spool directory. An\n"
00111 "                    optional base for the filename may be specified. The\n"
00112 "                    default is 'chanspy'.\n"
00113 "    v([value])    - Adjust the initial volume in the range from -4 to 4. A\n"
00114 "                    negative value refers to a quieter setting.\n"
00115 "    w             - Enable 'whisper' mode, so the spying channel can talk to\n"
00116 "                    the spied-on channel.\n"
00117 "    W             - Enable 'private whisper' mode, so the spying channel can\n"
00118 "                    talk to the spied-on channel but cannot listen to that\n"
00119 "                    channel.\n"
00120 ;
00121 
00122 enum {
00123    OPTION_QUIET    = (1 << 0),   /* Quiet, no announcement */
00124    OPTION_BRIDGED   = (1 << 1),  /* Only look at bridged calls */
00125    OPTION_VOLUME    = (1 << 2),  /* Specify initial volume */
00126    OPTION_GROUP     = (1 << 3),  /* Only look at channels in group */
00127    OPTION_RECORD    = (1 << 4),
00128    OPTION_WHISPER  = (1 << 5),
00129    OPTION_PRIVATE   = (1 << 6),  /* Private Whisper mode */
00130 } chanspy_opt_flags;
00131 
00132 enum {
00133    OPT_ARG_VOLUME = 0,
00134    OPT_ARG_GROUP,
00135    OPT_ARG_RECORD,
00136    OPT_ARG_ARRAY_SIZE,
00137 } chanspy_opt_args;
00138 
00139 AST_APP_OPTIONS(spy_opts, {
00140    AST_APP_OPTION('q', OPTION_QUIET),
00141    AST_APP_OPTION('b', OPTION_BRIDGED),
00142    AST_APP_OPTION('w', OPTION_WHISPER),
00143    AST_APP_OPTION('W', OPTION_PRIVATE),
00144    AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
00145    AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
00146    AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
00147 });
00148 
00149 static int next_unique_id_to_use = 0;
00150 
00151 struct chanspy_translation_helper {
00152    /* spy data */
00153    struct ast_audiohook spy_audiohook;
00154    struct ast_audiohook whisper_audiohook;
00155    int fd;
00156    int volfactor;
00157 };
00158 
00159 static void *spy_alloc(struct ast_channel *chan, void *data)
00160 {
00161    /* just store the data pointer in the channel structure */
00162    return data;
00163 }
00164 
00165 static void spy_release(struct ast_channel *chan, void *data)
00166 {
00167    /* nothing to do */
00168 }
00169 
00170 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples) 
00171 {
00172    struct chanspy_translation_helper *csth = data;
00173    struct ast_frame *f;
00174 
00175    ast_audiohook_lock(&csth->spy_audiohook);
00176    if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00177       ast_audiohook_unlock(&csth->spy_audiohook);
00178       return -1;
00179    }
00180 
00181    f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
00182 
00183    ast_audiohook_unlock(&csth->spy_audiohook);
00184 
00185    if (!f)
00186       return 0;
00187       
00188    if (ast_write(chan, f)) {
00189       ast_frfree(f);
00190       return -1;
00191    }
00192 
00193    if (csth->fd)
00194       write(csth->fd, f->data, f->datalen);
00195 
00196    ast_frfree(f);
00197 
00198    return 0;
00199 }
00200 
00201 static struct ast_generator spygen = {
00202    .alloc = spy_alloc,
00203    .release = spy_release,
00204    .generate = spy_generate, 
00205 };
00206 
00207 static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook) 
00208 {
00209    int res;
00210    struct ast_channel *peer;
00211 
00212    ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
00213 
00214    res = ast_audiohook_attach(chan, audiohook);
00215 
00216    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 
00217       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
00218    }
00219    return res;
00220 }
00221 
00222 struct chanspy_ds {
00223    struct ast_channel *chan;
00224    char unique_id[20];
00225    ast_mutex_t lock;
00226 };
00227 
00228 static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, 
00229    int *volfactor, int fd, const struct ast_flags *flags) 
00230 {
00231    struct chanspy_translation_helper csth;
00232    int running = 0, res, x = 0;
00233    char inp[24] = {0};
00234    char *name;
00235    struct ast_frame *f;
00236    struct ast_silence_generator *silgen = NULL;
00237    struct ast_channel *spyee = NULL;
00238    const char *spyer_name;
00239 
00240    ast_channel_lock(chan);
00241    spyer_name = ast_strdupa(chan->name);
00242    ast_channel_unlock(chan);
00243 
00244    ast_mutex_lock(&spyee_chanspy_ds->lock);
00245    if (spyee_chanspy_ds->chan) {
00246       spyee = spyee_chanspy_ds->chan;
00247       ast_channel_lock(spyee);
00248    }
00249    ast_mutex_unlock(&spyee_chanspy_ds->lock);
00250 
00251    if (!spyee)
00252       return 0;
00253 
00254    /* We now hold the channel lock on spyee */
00255 
00256    if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
00257       ast_channel_unlock(spyee);
00258       return 0;
00259    }
00260 
00261    name = ast_strdupa(spyee->name);
00262    if (option_verbose >= 2)
00263       ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name);
00264 
00265    memset(&csth, 0, sizeof(csth));
00266    
00267    ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
00268 
00269    if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
00270       ast_audiohook_destroy(&csth.spy_audiohook);
00271       ast_channel_unlock(spyee);
00272       return 0;
00273    }
00274    
00275    if (ast_test_flag(flags, OPTION_WHISPER)) {
00276       ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
00277       start_spying(spyee, spyer_name, &csth.whisper_audiohook);
00278    }
00279 
00280    ast_channel_unlock(spyee);
00281    spyee = NULL;
00282 
00283    csth.volfactor = *volfactor;
00284    
00285    if (csth.volfactor) {
00286       csth.spy_audiohook.options.read_volume = csth.volfactor;
00287       csth.spy_audiohook.options.write_volume = csth.volfactor;
00288    }
00289    
00290    csth.fd = fd;
00291 
00292    if (ast_test_flag(flags, OPTION_PRIVATE))
00293       silgen = ast_channel_start_silence_generator(chan);
00294    else
00295       ast_activate_generator(chan, &spygen, &csth);
00296 
00297    /* We can no longer rely on 'spyee' being an actual channel;
00298       it can be hung up and freed out from under us. However, the
00299       channel destructor will put NULL into our csth.spy.chan
00300       field when that happens, so that is our signal that the spyee
00301       channel has gone away.
00302    */
00303 
00304    /* Note: it is very important that the ast_waitfor() be the first
00305       condition in this expression, so that if we wait for some period
00306       of time before receiving a frame from our spying channel, we check
00307       for hangup on the spied-on channel _after_ knowing that a frame
00308       has arrived, since the spied-on channel could have gone away while
00309       we were waiting
00310    */
00311    while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00312       if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
00313          running = -1;
00314          break;
00315       }
00316 
00317       if (ast_test_flag(flags, OPTION_WHISPER) && (f->frametype == AST_FRAME_VOICE)) {
00318          ast_audiohook_lock(&csth.whisper_audiohook);
00319          ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00320          ast_audiohook_unlock(&csth.whisper_audiohook);
00321          ast_frfree(f);
00322          continue;
00323       }
00324 
00325       res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
00326       ast_frfree(f);
00327       if (!res)
00328          continue;
00329 
00330       if (x == sizeof(inp))
00331          x = 0;
00332 
00333       if (res < 0) {
00334          running = -1;
00335          break;
00336       }
00337 
00338       if (res == '*') {
00339          running = 0;
00340          break;
00341       } else if (res == '#') {
00342          if (!ast_strlen_zero(inp)) {
00343             running = atoi(inp);
00344             break;
00345          }
00346 
00347          (*volfactor)++;
00348          if (*volfactor > 4)
00349             *volfactor = -4;
00350          if (option_verbose > 2)
00351             ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
00352          csth.volfactor = *volfactor;
00353          csth.spy_audiohook.options.read_volume = csth.volfactor;
00354          csth.spy_audiohook.options.write_volume = csth.volfactor;
00355       } else if (res >= '0' && res <= '9') {
00356          inp[x++] = res;
00357       }
00358    }
00359 
00360    if (ast_test_flag(flags, OPTION_PRIVATE))
00361       ast_channel_stop_silence_generator(chan, silgen);
00362    else
00363       ast_deactivate_generator(chan);
00364 
00365    if (ast_test_flag(flags, OPTION_WHISPER)) {
00366       ast_audiohook_lock(&csth.whisper_audiohook);
00367       ast_audiohook_detach(&csth.whisper_audiohook);
00368       ast_audiohook_unlock(&csth.whisper_audiohook);
00369       ast_audiohook_destroy(&csth.whisper_audiohook);
00370    }
00371    
00372    ast_audiohook_lock(&csth.spy_audiohook);
00373    ast_audiohook_detach(&csth.spy_audiohook);
00374    ast_audiohook_unlock(&csth.spy_audiohook);
00375    ast_audiohook_destroy(&csth.spy_audiohook);
00376    
00377    if (option_verbose >= 2)
00378       ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
00379    
00380    return running;
00381 }
00382 
00383 /*!
00384  * \note This relies on the embedded lock to be recursive, as it may be called
00385  * due to a call to chanspy_ds_free with the lock held there.
00386  */
00387 static void chanspy_ds_destroy(void *data)
00388 {
00389    struct chanspy_ds *chanspy_ds = data;
00390 
00391    /* Setting chan to be NULL is an atomic operation, but we don't want this
00392     * value to change while this lock is held.  The lock is held elsewhere
00393     * while it performs non-atomic operations with this channel pointer */
00394 
00395    ast_mutex_lock(&chanspy_ds->lock);
00396    chanspy_ds->chan = NULL;
00397    ast_mutex_unlock(&chanspy_ds->lock);
00398 }
00399 
00400 static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00401 {
00402    struct chanspy_ds *chanspy_ds = data;
00403    
00404    ast_mutex_lock(&chanspy_ds->lock);
00405    chanspy_ds->chan = new_chan;
00406    ast_mutex_unlock(&chanspy_ds->lock);
00407 }
00408 
00409 static const struct ast_datastore_info chanspy_ds_info = {
00410    .type = "chanspy",
00411    .destroy = chanspy_ds_destroy,
00412    .chan_fixup = chanspy_ds_chan_fixup,
00413 };
00414 
00415 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
00416 {
00417    if (!chanspy_ds)
00418       return NULL;
00419 
00420    ast_mutex_lock(&chanspy_ds->lock);
00421    if (chanspy_ds->chan) {
00422       struct ast_datastore *datastore;
00423       struct ast_channel *chan;
00424 
00425       chan = chanspy_ds->chan;
00426 
00427       ast_channel_lock(chan);
00428       if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
00429          ast_channel_datastore_remove(chan, datastore);
00430          /* chanspy_ds->chan is NULL after this call */
00431          chanspy_ds_destroy(datastore->data);
00432          datastore->data = NULL;
00433          ast_channel_datastore_free(datastore);
00434       }
00435       ast_channel_unlock(chan);
00436    }
00437    ast_mutex_unlock(&chanspy_ds->lock);
00438 
00439    return NULL;
00440 }
00441 
00442 /*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
00443 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
00444 {
00445    struct ast_datastore *datastore = NULL;
00446 
00447    ast_mutex_lock(&chanspy_ds->lock);
00448 
00449    if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
00450       ast_mutex_unlock(&chanspy_ds->lock);
00451       chanspy_ds = chanspy_ds_free(chanspy_ds);
00452       ast_channel_unlock(chan);
00453       return NULL;
00454    }
00455    
00456    chanspy_ds->chan = chan;
00457    datastore->data = chanspy_ds;
00458    ast_channel_datastore_add(chan, datastore);
00459 
00460    return chanspy_ds;
00461 }
00462 
00463 static struct chanspy_ds *next_channel(struct ast_channel *chan,
00464    const struct ast_channel *last, const char *spec,
00465    const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
00466 {
00467    struct ast_channel *this;
00468    char channel_name[AST_CHANNEL_NAME];
00469    static size_t PSEUDO_CHAN_LEN = 0;
00470 
00471    if (!PSEUDO_CHAN_LEN) {
00472       PSEUDO_CHAN_LEN = *dahdi_chan_name_len + strlen("/pseudo");
00473    }
00474 
00475 redo:
00476    if (spec)
00477       this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
00478    else if (exten)
00479       this = ast_walk_channel_by_exten_locked(last, exten, context);
00480    else
00481       this = ast_channel_walk_locked(last);
00482 
00483    if (!this)
00484       return NULL;
00485 
00486    snprintf(channel_name, AST_CHANNEL_NAME, "%s/pseudo", dahdi_chan_name);
00487    if (!strncmp(this->name, channel_name, PSEUDO_CHAN_LEN)) {
00488       last = this;
00489       ast_channel_unlock(this);
00490       goto redo;
00491    } else if (this == chan) {
00492       last = this;
00493       ast_channel_unlock(this);
00494       goto redo;
00495    }
00496 
00497    return setup_chanspy_ds(this, chanspy_ds);
00498 }
00499 
00500 static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
00501              int volfactor, const int fd, const char *mygroup, const char *spec,
00502              const char *exten, const char *context)
00503 {
00504    char nameprefix[AST_NAME_STRLEN];
00505    char peer_name[AST_NAME_STRLEN + 5];
00506    signed char zero_volume = 0;
00507    int waitms;
00508    int res;
00509    char *ptr;
00510    int num;
00511    int num_spyed_upon = 1;
00512    struct chanspy_ds chanspy_ds;
00513 
00514    ast_mutex_init(&chanspy_ds.lock);
00515 
00516    snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
00517 
00518    if (chan->_state != AST_STATE_UP)
00519       ast_answer(chan);
00520 
00521    ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
00522 
00523    waitms = 100;
00524 
00525    for (;;) {
00526       struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
00527       struct ast_channel *prev = NULL, *peer = NULL;
00528 
00529       if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
00530          res = ast_streamfile(chan, "beep", chan->language);
00531          if (!res)
00532             res = ast_waitstream(chan, "");
00533          else if (res < 0) {
00534             ast_clear_flag(chan, AST_FLAG_SPYING);
00535             break;
00536          }
00537       }
00538 
00539       res = ast_waitfordigit(chan, waitms);
00540       if (res < 0) {
00541          ast_clear_flag(chan, AST_FLAG_SPYING);
00542          break;
00543       }
00544             
00545       /* reset for the next loop around, unless overridden later */
00546       waitms = 100;
00547       num_spyed_upon = 0;
00548 
00549       for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
00550            peer_chanspy_ds;
00551           chanspy_ds_free(peer_chanspy_ds), prev = peer,
00552            peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 
00553             next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
00554          const char *group;
00555          int igrp = !mygroup;
00556          char *groups[25];
00557          int num_groups = 0;
00558          char dup_group[512];
00559          int x;
00560          char *s;
00561 
00562          peer = peer_chanspy_ds->chan;
00563 
00564          ast_mutex_unlock(&peer_chanspy_ds->lock);
00565 
00566          if (peer == prev) {
00567             ast_channel_unlock(peer);
00568             chanspy_ds_free(peer_chanspy_ds);
00569             break;
00570          }
00571 
00572          if (ast_check_hangup(chan)) {
00573             ast_channel_unlock(peer);
00574             chanspy_ds_free(peer_chanspy_ds);
00575             break;
00576          }
00577 
00578          if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
00579             ast_channel_unlock(peer);
00580             continue;
00581          }
00582 
00583          if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
00584             ast_channel_unlock(peer);
00585             continue;
00586          }
00587 
00588          if (mygroup) {
00589             if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
00590                ast_copy_string(dup_group, group, sizeof(dup_group));
00591                num_groups = ast_app_separate_args(dup_group, ':', groups,
00592                               sizeof(groups) / sizeof(groups[0]));
00593             }
00594             
00595             for (x = 0; x < num_groups; x++) {
00596                if (!strcmp(mygroup, groups[x])) {
00597                   igrp = 1;
00598                   break;
00599                }
00600             }
00601          }
00602          
00603          if (!igrp) {
00604             ast_channel_unlock(peer);
00605             continue;
00606          }
00607 
00608          strcpy(peer_name, "spy-");
00609          strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
00610          ptr = strchr(peer_name, '/');
00611          *ptr++ = '\0';
00612          
00613          for (s = peer_name; s < ptr; s++)
00614             *s = tolower(*s);
00615 
00616          /* We have to unlock the peer channel here to avoid a deadlock.
00617           * So, when we need to dereference it again, we have to lock the 
00618           * datastore and get the pointer from there to see if the channel 
00619           * is still valid. */
00620          ast_channel_unlock(peer);
00621 
00622          if (!ast_test_flag(flags, OPTION_QUIET)) {
00623             if (ast_fileexists(peer_name, NULL, NULL) != -1) {
00624                res = ast_streamfile(chan, peer_name, chan->language);
00625                if (!res)
00626                   res = ast_waitstream(chan, "");
00627                if (res) {
00628                   chanspy_ds_free(peer_chanspy_ds);
00629                   break;
00630                }
00631             } else
00632                res = ast_say_character_str(chan, peer_name, "", chan->language);
00633             if ((num = atoi(ptr))) 
00634                ast_say_digits(chan, atoi(ptr), "", chan->language);
00635          }
00636          
00637          res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags);
00638          num_spyed_upon++; 
00639 
00640          if (res == -1) {
00641             chanspy_ds_free(peer_chanspy_ds);
00642             break;
00643          } else if (res > 1 && spec) {
00644             struct ast_channel *next;
00645 
00646             snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
00647 
00648             if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
00649                peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
00650                next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
00651             } else {
00652                /* stay on this channel, if it is still valid */
00653 
00654                ast_mutex_lock(&peer_chanspy_ds->lock);
00655                if (peer_chanspy_ds->chan) {
00656                   ast_channel_lock(peer_chanspy_ds->chan);
00657                   next_chanspy_ds = peer_chanspy_ds;
00658                   peer_chanspy_ds = NULL;
00659                } else {
00660                   /* the channel is gone */
00661                   ast_mutex_unlock(&peer_chanspy_ds->lock);
00662                   next_chanspy_ds = NULL;
00663                }
00664             }
00665 
00666             peer = NULL;
00667          }
00668       }
00669       if (res == -1 || ast_check_hangup(chan))
00670          break;
00671    }
00672    
00673    ast_clear_flag(chan, AST_FLAG_SPYING);
00674 
00675    ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00676 
00677    ast_mutex_lock(&chanspy_ds.lock);
00678    ast_mutex_unlock(&chanspy_ds.lock);
00679    ast_mutex_destroy(&chanspy_ds.lock);
00680 
00681    return res;
00682 }
00683 
00684 static int chanspy_exec(struct ast_channel *chan, void *data)
00685 {
00686    struct ast_module_user *u;
00687    char *options = NULL;
00688    char *spec = NULL;
00689    char *argv[2];
00690    char *mygroup = NULL;
00691    char *recbase = NULL;
00692    int fd = 0;
00693    struct ast_flags flags;
00694    int oldwf = 0;
00695    int argc = 0;
00696    int volfactor = 0;
00697    int res;
00698 
00699    data = ast_strdupa(data);
00700 
00701    u = ast_module_user_add(chan);
00702 
00703    if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
00704       spec = argv[0];
00705       if (argc > 1)
00706          options = argv[1];
00707 
00708       if (ast_strlen_zero(spec) || !strcmp(spec, "all"))
00709          spec = NULL;
00710    }
00711 
00712    if (options) {
00713       char *opts[OPT_ARG_ARRAY_SIZE];
00714       
00715       ast_app_parse_options(spy_opts, &flags, opts, options);
00716       if (ast_test_flag(&flags, OPTION_GROUP))
00717          mygroup = opts[OPT_ARG_GROUP];
00718 
00719       if (ast_test_flag(&flags, OPTION_RECORD) &&
00720           !(recbase = opts[OPT_ARG_RECORD]))
00721          recbase = "chanspy";
00722 
00723       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00724          int vol;
00725 
00726          if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
00727             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00728          else
00729             volfactor = vol;
00730       }
00731 
00732       if (ast_test_flag(&flags, OPTION_PRIVATE))
00733          ast_set_flag(&flags, OPTION_WHISPER);
00734    } else
00735       ast_clear_flag(&flags, AST_FLAGS_ALL);
00736 
00737    oldwf = chan->writeformat;
00738    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00739       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00740       ast_module_user_remove(u);
00741       return -1;
00742    }
00743 
00744    if (recbase) {
00745       char filename[PATH_MAX];
00746 
00747       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
00748       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
00749          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
00750          fd = 0;
00751       }
00752    }
00753 
00754    res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL);
00755 
00756    if (fd)
00757       close(fd);
00758 
00759    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
00760       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00761 
00762    ast_module_user_remove(u);
00763 
00764    return res;
00765 }
00766 
00767 static int extenspy_exec(struct ast_channel *chan, void *data)
00768 {
00769    struct ast_module_user *u;
00770    char *options = NULL;
00771    char *exten = NULL;
00772    char *context = NULL;
00773    char *argv[2];
00774    char *mygroup = NULL;
00775    char *recbase = NULL;
00776    int fd = 0;
00777    struct ast_flags flags;
00778    int oldwf = 0;
00779    int argc = 0;
00780    int volfactor = 0;
00781    int res;
00782 
00783    data = ast_strdupa(data);
00784 
00785    u = ast_module_user_add(chan);
00786 
00787    if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
00788       context = argv[0];
00789       if (!ast_strlen_zero(argv[0]))
00790          exten = strsep(&context, "@");
00791       if (ast_strlen_zero(context))
00792          context = ast_strdupa(chan->context);
00793       if (argc > 1)
00794          options = argv[1];
00795    }
00796 
00797    if (options) {
00798       char *opts[OPT_ARG_ARRAY_SIZE];
00799       
00800       ast_app_parse_options(spy_opts, &flags, opts, options);
00801       if (ast_test_flag(&flags, OPTION_GROUP))
00802          mygroup = opts[OPT_ARG_GROUP];
00803 
00804       if (ast_test_flag(&flags, OPTION_RECORD) &&
00805           !(recbase = opts[OPT_ARG_RECORD]))
00806          recbase = "chanspy";
00807 
00808       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00809          int vol;
00810 
00811          if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
00812             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00813          else
00814             volfactor = vol;
00815       }
00816 
00817       if (ast_test_flag(&flags, OPTION_PRIVATE))
00818          ast_set_flag(&flags, OPTION_WHISPER);
00819    } else
00820       ast_clear_flag(&flags, AST_FLAGS_ALL);
00821 
00822    oldwf = chan->writeformat;
00823    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00824       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00825       ast_module_user_remove(u);
00826       return -1;
00827    }
00828 
00829    if (recbase) {
00830       char filename[PATH_MAX];
00831 
00832       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
00833       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
00834          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
00835          fd = 0;
00836       }
00837    }
00838 
00839    res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context);
00840 
00841    if (fd)
00842       close(fd);
00843 
00844    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
00845       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00846 
00847    ast_module_user_remove(u);
00848 
00849    return res;
00850 }
00851 
00852 static int unload_module(void)
00853 {
00854    int res = 0;
00855 
00856    res |= ast_unregister_application(app_chan);
00857    res |= ast_unregister_application(app_ext);
00858 
00859    ast_module_user_hangup_all();
00860 
00861    return res;
00862 }
00863 
00864 static int load_module(void)
00865 {
00866    int res = 0;
00867 
00868    res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
00869    res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
00870 
00871    return res;
00872 }
00873 
00874 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");

Generated on Mon Nov 24 15:33:55 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7