Thu Jul 9 13:40:17 2009

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

Generated on Thu Jul 9 13:40:17 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7