Wed Oct 28 11:45:25 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: 224179 $")
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    if (ast_test_flag(chan, OPTION_READONLY)) {
00205       /* Option 'o' was set, so don't mix channel audio */
00206       f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, AST_FORMAT_SLINEAR);
00207    } else {
00208       f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
00209    }
00210 
00211    ast_audiohook_unlock(&csth->spy_audiohook);
00212 
00213    if (!f)
00214       return 0;
00215 
00216    for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00217       if (ast_write(chan, cur)) {
00218          ast_frfree(f);
00219          return -1;
00220       }
00221 
00222       if (csth->fd) {
00223          if (write(csth->fd, cur->data, cur->datalen) < 0) {
00224             ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00225          }
00226       }
00227    }
00228 
00229    ast_frfree(f);
00230 
00231    return 0;
00232 }
00233 
00234 static struct ast_generator spygen = {
00235    .alloc = spy_alloc,
00236    .release = spy_release,
00237    .generate = spy_generate,
00238 };
00239 
00240 static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook) 
00241 {
00242    int res = 0;
00243    struct ast_channel *peer = NULL;
00244 
00245    ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
00246 
00247    ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
00248    res = ast_audiohook_attach(chan, audiohook);
00249 
00250    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 
00251       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00252    }
00253    return res;
00254 }
00255 
00256 struct chanspy_ds {
00257    struct ast_channel *chan;
00258    char unique_id[20];
00259    ast_mutex_t lock;
00260 };
00261 
00262 static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, 
00263    int *volfactor, int fd, const struct ast_flags *flags, char *exitcontext) 
00264 {
00265    struct chanspy_translation_helper csth;
00266    int running = 0, res, x = 0;
00267    char inp[24] = {0};
00268    char *name;
00269    struct ast_frame *f;
00270    struct ast_silence_generator *silgen = NULL;
00271    struct ast_channel *spyee = NULL;
00272    const char *spyer_name;
00273 
00274    ast_channel_lock(chan);
00275    spyer_name = ast_strdupa(chan->name);
00276    ast_channel_unlock(chan);
00277 
00278    ast_mutex_lock(&spyee_chanspy_ds->lock);
00279    while ((spyee = spyee_chanspy_ds->chan) && ast_channel_trylock(spyee)) {
00280       /* avoid a deadlock here, just in case spyee is masqueraded and
00281        * chanspy_ds_chan_fixup() is called with the channel locked */
00282       DEADLOCK_AVOIDANCE(&spyee_chanspy_ds->lock);
00283    }
00284    ast_mutex_unlock(&spyee_chanspy_ds->lock);
00285 
00286    if (!spyee)
00287       return 0;
00288 
00289    /* We now hold the channel lock on spyee */
00290 
00291    if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
00292       ast_channel_unlock(spyee);
00293       return 0;
00294    }
00295 
00296    name = ast_strdupa(spyee->name);
00297    ast_verb(2, "Spying on channel %s\n", name);
00298 
00299    memset(&csth, 0, sizeof(csth));
00300 
00301    ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
00302 
00303    if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
00304       ast_audiohook_destroy(&csth.spy_audiohook);
00305       ast_channel_unlock(spyee);
00306       return 0;
00307    }
00308 
00309    if (ast_test_flag(flags, OPTION_WHISPER)) {
00310       ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
00311       start_spying(spyee, spyer_name, &csth.whisper_audiohook);
00312    }
00313 
00314    ast_channel_unlock(spyee);
00315    spyee = NULL;
00316 
00317    csth.volfactor = *volfactor;
00318 
00319    if (csth.volfactor) {
00320       csth.spy_audiohook.options.read_volume = csth.volfactor;
00321       csth.spy_audiohook.options.write_volume = csth.volfactor;
00322    }
00323 
00324    csth.fd = fd;
00325 
00326    if (ast_test_flag(flags, OPTION_PRIVATE))
00327       silgen = ast_channel_start_silence_generator(chan);
00328    else
00329       ast_activate_generator(chan, &spygen, &csth);
00330 
00331    /* We can no longer rely on 'spyee' being an actual channel;
00332       it can be hung up and freed out from under us. However, the
00333       channel destructor will put NULL into our csth.spy.chan
00334       field when that happens, so that is our signal that the spyee
00335       channel has gone away.
00336    */
00337 
00338    /* Note: it is very important that the ast_waitfor() be the first
00339       condition in this expression, so that if we wait for some period
00340       of time before receiving a frame from our spying channel, we check
00341       for hangup on the spied-on channel _after_ knowing that a frame
00342       has arrived, since the spied-on channel could have gone away while
00343       we were waiting
00344    */
00345    while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00346       if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
00347          running = -1;
00348          break;
00349       }
00350 
00351       if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
00352          ast_audiohook_lock(&csth.whisper_audiohook);
00353          ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00354          ast_audiohook_unlock(&csth.whisper_audiohook);
00355          ast_frfree(f);
00356          continue;
00357       }
00358       
00359       res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
00360       ast_frfree(f);
00361       if (!res)
00362          continue;
00363 
00364       if (x == sizeof(inp))
00365          x = 0;
00366 
00367       if (res < 0) {
00368          running = -1;
00369          break;
00370       }
00371 
00372       if (ast_test_flag(flags, OPTION_EXIT)) {
00373          char tmp[2];
00374          tmp[0] = res;
00375          tmp[1] = '\0';
00376          if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
00377             ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
00378             pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
00379             running = -2;
00380             break;
00381          } else {
00382             ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00383          }
00384       } else if (res >= '0' && res <= '9') {
00385          inp[x++] = res;
00386       }
00387 
00388       if (res == '*') {
00389          running = 0;
00390          break;
00391       } else if (res == '#') {
00392          if (!ast_strlen_zero(inp)) {
00393             running = atoi(inp);
00394             break;
00395          }
00396 
00397          (*volfactor)++;
00398          if (*volfactor > 4)
00399             *volfactor = -4;
00400          ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
00401 
00402          csth.volfactor = *volfactor;
00403          csth.spy_audiohook.options.read_volume = csth.volfactor;
00404          csth.spy_audiohook.options.write_volume = csth.volfactor;
00405       }
00406    }
00407 
00408    if (ast_test_flag(flags, OPTION_PRIVATE))
00409       ast_channel_stop_silence_generator(chan, silgen);
00410    else
00411       ast_deactivate_generator(chan);
00412 
00413    if (ast_test_flag(flags, OPTION_WHISPER)) {
00414       ast_audiohook_lock(&csth.whisper_audiohook);
00415       ast_audiohook_detach(&csth.whisper_audiohook);
00416       ast_audiohook_unlock(&csth.whisper_audiohook);
00417       ast_audiohook_destroy(&csth.whisper_audiohook);
00418    }
00419 
00420    ast_audiohook_lock(&csth.spy_audiohook);
00421    ast_audiohook_detach(&csth.spy_audiohook);
00422    ast_audiohook_unlock(&csth.spy_audiohook);
00423    ast_audiohook_destroy(&csth.spy_audiohook);
00424    
00425    ast_verb(2, "Done Spying on channel %s\n", name);
00426 
00427    return running;
00428 }
00429 
00430 /*!
00431  * \note This relies on the embedded lock to be recursive, as it may be called
00432  * due to a call to chanspy_ds_free with the lock held there.
00433  */
00434 static void chanspy_ds_destroy(void *data)
00435 {
00436    struct chanspy_ds *chanspy_ds = data;
00437 
00438    /* Setting chan to be NULL is an atomic operation, but we don't want this
00439     * value to change while this lock is held.  The lock is held elsewhere
00440     * while it performs non-atomic operations with this channel pointer */
00441 
00442    ast_mutex_lock(&chanspy_ds->lock);
00443    chanspy_ds->chan = NULL;
00444    ast_mutex_unlock(&chanspy_ds->lock);
00445 }
00446 
00447 static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00448 {
00449    struct chanspy_ds *chanspy_ds = data;
00450    
00451    ast_mutex_lock(&chanspy_ds->lock);
00452    chanspy_ds->chan = new_chan;
00453    ast_mutex_unlock(&chanspy_ds->lock);
00454 }
00455 
00456 static const struct ast_datastore_info chanspy_ds_info = {
00457    .type = "chanspy",
00458    .destroy = chanspy_ds_destroy,
00459    .chan_fixup = chanspy_ds_chan_fixup,
00460 };
00461 
00462 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
00463 {
00464    struct ast_channel *chan;
00465 
00466    if (!chanspy_ds) {
00467       return NULL;
00468    }
00469 
00470    ast_mutex_lock(&chanspy_ds->lock);
00471    while ((chan = chanspy_ds->chan)) {
00472       struct ast_datastore *datastore;
00473 
00474       if (ast_channel_trylock(chan)) {
00475          DEADLOCK_AVOIDANCE(&chanspy_ds->lock);
00476          continue;
00477       }
00478       if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
00479          ast_channel_datastore_remove(chan, datastore);
00480          /* chanspy_ds->chan is NULL after this call */
00481          chanspy_ds_destroy(datastore->data);
00482          datastore->data = NULL;
00483          ast_channel_datastore_free(datastore);
00484       }
00485       ast_channel_unlock(chan);
00486       break;
00487    }
00488    ast_mutex_unlock(&chanspy_ds->lock);
00489 
00490    return NULL;
00491 }
00492 
00493 /*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
00494 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
00495 {
00496    struct ast_datastore *datastore = NULL;
00497 
00498    ast_mutex_lock(&chanspy_ds->lock);
00499 
00500    if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
00501       ast_mutex_unlock(&chanspy_ds->lock);
00502       chanspy_ds = chanspy_ds_free(chanspy_ds);
00503       ast_channel_unlock(chan);
00504       return NULL;
00505    }
00506    
00507    chanspy_ds->chan = chan;
00508    datastore->data = chanspy_ds;
00509    ast_channel_datastore_add(chan, datastore);
00510 
00511    return chanspy_ds;
00512 }
00513 
00514 static struct chanspy_ds *next_channel(struct ast_channel *chan,
00515    const struct ast_channel *last, const char *spec,
00516    const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
00517 {
00518    struct ast_channel *next;
00519    const size_t pseudo_len = strlen("DAHDI/pseudo");
00520 
00521 redo:
00522    if (!ast_strlen_zero(spec))
00523       next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
00524 
00525    else if (!ast_strlen_zero(exten))
00526       next = ast_walk_channel_by_exten_locked(last, exten, context);
00527    else
00528       next = ast_channel_walk_locked(last);
00529 
00530    if (!next)
00531       return NULL;
00532 
00533    if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
00534       last = next;
00535       ast_channel_unlock(next);
00536       goto redo;
00537    } else if (next == chan) {
00538       last = next;
00539       ast_channel_unlock(next);
00540       goto redo;
00541    }
00542 
00543    return setup_chanspy_ds(next, chanspy_ds);
00544 }
00545 
00546 static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
00547    int volfactor, const int fd, const char *mygroup, const char *myenforced,
00548    const char *spec, const char *exten, const char *context)
00549 {
00550    char nameprefix[AST_NAME_STRLEN];
00551    char peer_name[AST_NAME_STRLEN + 5];
00552    char exitcontext[AST_MAX_CONTEXT] = "";
00553    signed char zero_volume = 0;
00554    int waitms;
00555    int res;
00556    char *ptr;
00557    int num;
00558    int num_spyed_upon = 1;
00559    struct chanspy_ds chanspy_ds = { 0, };
00560 
00561    if (ast_test_flag(flags, OPTION_EXIT)) {
00562       const char *c;
00563       if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT")))
00564          ast_copy_string(exitcontext, c, sizeof(exitcontext));
00565       else if (!ast_strlen_zero(chan->macrocontext))
00566          ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
00567       else
00568          ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
00569    }
00570 
00571    ast_mutex_init(&chanspy_ds.lock);
00572 
00573    snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
00574 
00575    if (chan->_state != AST_STATE_UP)
00576       ast_answer(chan);
00577 
00578    ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
00579 
00580    waitms = 100;
00581 
00582    for (;;) {
00583       struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
00584       struct ast_channel *prev = NULL, *peer = NULL;
00585 
00586       if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
00587          res = ast_streamfile(chan, "beep", chan->language);
00588          if (!res)
00589             res = ast_waitstream(chan, "");
00590          else if (res < 0) {
00591             ast_clear_flag(chan, AST_FLAG_SPYING);
00592             break;
00593          }
00594          if (!ast_strlen_zero(exitcontext)) {
00595             char tmp[2];
00596             tmp[0] = res;
00597             tmp[1] = '\0';
00598             if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00599                goto exit;
00600             else
00601                ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00602          }
00603       }
00604 
00605       res = ast_waitfordigit(chan, waitms);
00606       if (res < 0) {
00607          ast_clear_flag(chan, AST_FLAG_SPYING);
00608          break;
00609       }
00610       if (!ast_strlen_zero(exitcontext)) {
00611          char tmp[2];
00612          tmp[0] = res;
00613          tmp[1] = '\0';
00614          if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00615             goto exit;
00616          else
00617             ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00618       }
00619 
00620       /* reset for the next loop around, unless overridden later */
00621       waitms = 100;
00622       num_spyed_upon = 0;
00623 
00624       for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
00625            peer_chanspy_ds;
00626           chanspy_ds_free(peer_chanspy_ds), prev = peer,
00627            peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 
00628             next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
00629          int igrp = !mygroup;
00630          int ienf = !myenforced;
00631          char *s;
00632 
00633          peer = peer_chanspy_ds->chan;
00634 
00635          ast_mutex_unlock(&peer_chanspy_ds->lock);
00636 
00637          if (peer == prev) {
00638             ast_channel_unlock(peer);
00639             chanspy_ds_free(peer_chanspy_ds);
00640             break;
00641          }
00642 
00643          if (ast_check_hangup(chan)) {
00644             ast_channel_unlock(peer);
00645             chanspy_ds_free(peer_chanspy_ds);
00646             break;
00647          }
00648 
00649          if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
00650             ast_channel_unlock(peer);
00651             continue;
00652          }
00653 
00654          if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
00655             ast_channel_unlock(peer);
00656             continue;
00657          }
00658 
00659          if (mygroup) {
00660             int num_groups = 0;
00661             char dup_group[512];
00662             char *groups[25];
00663             const char *group;
00664             int x;
00665             if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
00666                ast_copy_string(dup_group, group, sizeof(dup_group));
00667                num_groups = ast_app_separate_args(dup_group, ':', groups,
00668                   ARRAY_LEN(groups));
00669             }
00670 
00671             for (x = 0; x < num_groups; x++) {
00672                if (!strcmp(mygroup, groups[x])) {
00673                   igrp = 1;
00674                   break;
00675                }
00676             }
00677          }
00678 
00679          if (!igrp) {
00680             ast_channel_unlock(peer);
00681             continue;
00682          }
00683 
00684          if (myenforced) {
00685             char ext[AST_CHANNEL_NAME + 3];
00686             char buffer[512];
00687             char *end;
00688 
00689             snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
00690 
00691             ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1);
00692             if ((end = strchr(ext, '-'))) {
00693                *end++ = ':';
00694                *end = '\0';
00695             }
00696 
00697             ext[0] = ':';
00698 
00699             if (strcasestr(buffer, ext)) {
00700                ienf = 1;
00701             }
00702          }
00703 
00704          if (!ienf) {
00705             continue;
00706          }
00707 
00708          strcpy(peer_name, "spy-");
00709          strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
00710          ptr = strchr(peer_name, '/');
00711          *ptr++ = '\0';
00712 
00713          for (s = peer_name; s < ptr; s++)
00714             *s = tolower(*s);
00715          /* We have to unlock the peer channel here to avoid a deadlock.
00716           * So, when we need to dereference it again, we have to lock the 
00717           * datastore and get the pointer from there to see if the channel 
00718           * is still valid. */
00719          ast_channel_unlock(peer);
00720 
00721          if (!ast_test_flag(flags, OPTION_QUIET)) {
00722             if (ast_fileexists(peer_name, NULL, NULL) != -1) {
00723                res = ast_streamfile(chan, peer_name, chan->language);
00724                if (!res)
00725                   res = ast_waitstream(chan, "");
00726                if (res) {
00727                   chanspy_ds_free(peer_chanspy_ds);
00728                   break;
00729                }
00730             } else
00731                res = ast_say_character_str(chan, peer_name, "", chan->language);
00732             if ((num = atoi(ptr)))
00733                ast_say_digits(chan, atoi(ptr), "", chan->language);
00734          }
00735 
00736          res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
00737          num_spyed_upon++; 
00738 
00739          if (res == -1) {
00740             chanspy_ds_free(peer_chanspy_ds);
00741             goto exit;
00742          } else if (res == -2) {
00743             res = 0;
00744             chanspy_ds_free(peer_chanspy_ds);
00745             goto exit;
00746          } else if (res > 1 && spec) {
00747             struct ast_channel *next;
00748 
00749             snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
00750 
00751             if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
00752                peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
00753                next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
00754             } else {
00755                /* stay on this channel, if it is still valid */
00756 
00757                ast_mutex_lock(&peer_chanspy_ds->lock);
00758                if (peer_chanspy_ds->chan) {
00759                   ast_channel_lock(peer_chanspy_ds->chan);
00760                   next_chanspy_ds = peer_chanspy_ds;
00761                   peer_chanspy_ds = NULL;
00762                } else {
00763                   /* the channel is gone */
00764                   ast_mutex_unlock(&peer_chanspy_ds->lock);
00765                   next_chanspy_ds = NULL;
00766                }
00767             }
00768 
00769             peer = NULL;
00770          }
00771       }
00772       if (res == -1 || ast_check_hangup(chan))
00773          break;
00774    }
00775 exit:
00776 
00777    ast_clear_flag(chan, AST_FLAG_SPYING);
00778 
00779    ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00780 
00781    ast_mutex_lock(&chanspy_ds.lock);
00782    ast_mutex_unlock(&chanspy_ds.lock);
00783    ast_mutex_destroy(&chanspy_ds.lock);
00784 
00785    return res;
00786 }
00787 
00788 static int chanspy_exec(struct ast_channel *chan, void *data)
00789 {
00790    char *myenforced = NULL;
00791    char *mygroup = NULL;
00792    char *recbase = NULL;
00793    int fd = 0;
00794    struct ast_flags flags;
00795    int oldwf = 0;
00796    int volfactor = 0;
00797    int res;
00798    AST_DECLARE_APP_ARGS(args,
00799       AST_APP_ARG(spec);
00800       AST_APP_ARG(options);
00801    );
00802    char *opts[OPT_ARG_ARRAY_SIZE];
00803 
00804    data = ast_strdupa(data);
00805    AST_STANDARD_APP_ARGS(args, data);
00806 
00807    if (args.spec && !strcmp(args.spec, "all"))
00808       args.spec = NULL;
00809 
00810    if (args.options) {
00811       ast_app_parse_options(spy_opts, &flags, opts, args.options);
00812       if (ast_test_flag(&flags, OPTION_GROUP))
00813          mygroup = opts[OPT_ARG_GROUP];
00814 
00815       if (ast_test_flag(&flags, OPTION_RECORD) &&
00816          !(recbase = opts[OPT_ARG_RECORD]))
00817          recbase = "chanspy";
00818 
00819       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00820          int vol;
00821 
00822          if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
00823             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00824          else
00825             volfactor = vol;
00826       }
00827 
00828       if (ast_test_flag(&flags, OPTION_PRIVATE))
00829          ast_set_flag(&flags, OPTION_WHISPER);
00830 
00831       if (ast_test_flag(&flags, OPTION_ENFORCED))
00832          myenforced = opts[OPT_ARG_ENFORCED];
00833 
00834    } else
00835       ast_clear_flag(&flags, AST_FLAGS_ALL);
00836 
00837    oldwf = chan->writeformat;
00838    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00839       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00840       return -1;
00841    }
00842 
00843    if (recbase) {
00844       char filename[PATH_MAX];
00845 
00846       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
00847       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
00848          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
00849          fd = 0;
00850       }
00851    }
00852 
00853    res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL);
00854 
00855    if (fd)
00856       close(fd);
00857 
00858    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
00859       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00860 
00861    return res;
00862 }
00863 
00864 static int extenspy_exec(struct ast_channel *chan, void *data)
00865 {
00866    char *ptr, *exten = NULL;
00867    char *mygroup = NULL;
00868    char *recbase = NULL;
00869    int fd = 0;
00870    struct ast_flags flags;
00871    int oldwf = 0;
00872    int volfactor = 0;
00873    int res;
00874    AST_DECLARE_APP_ARGS(args,
00875       AST_APP_ARG(context);
00876       AST_APP_ARG(options);
00877    );
00878 
00879    data = ast_strdupa(data);
00880 
00881    AST_STANDARD_APP_ARGS(args, data);
00882    if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
00883       exten = args.context;
00884       *ptr++ = '\0';
00885       args.context = ptr;
00886    }
00887 
00888    if (ast_strlen_zero(args.context))
00889       args.context = ast_strdupa(chan->context);
00890 
00891    if (args.options) {
00892       char *opts[OPT_ARG_ARRAY_SIZE];
00893 
00894       ast_app_parse_options(spy_opts, &flags, opts, args.options);
00895       if (ast_test_flag(&flags, OPTION_GROUP))
00896          mygroup = opts[OPT_ARG_GROUP];
00897 
00898       if (ast_test_flag(&flags, OPTION_RECORD) &&
00899          !(recbase = opts[OPT_ARG_RECORD]))
00900          recbase = "chanspy";
00901 
00902       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00903          int vol;
00904 
00905          if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
00906             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00907          else
00908             volfactor = vol;
00909       }
00910 
00911       if (ast_test_flag(&flags, OPTION_PRIVATE))
00912          ast_set_flag(&flags, OPTION_WHISPER);
00913    } else
00914       ast_clear_flag(&flags, AST_FLAGS_ALL);
00915 
00916    oldwf = chan->writeformat;
00917    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00918       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00919       return -1;
00920    }
00921 
00922    if (recbase) {
00923       char filename[PATH_MAX];
00924 
00925       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
00926       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
00927          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
00928          fd = 0;
00929       }
00930    }
00931 
00932 
00933    res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context);
00934 
00935    if (fd)
00936       close(fd);
00937 
00938    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
00939       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00940 
00941    return res;
00942 }
00943 
00944 static int unload_module(void)
00945 {
00946    int res = 0;
00947 
00948    res |= ast_unregister_application(app_chan);
00949    res |= ast_unregister_application(app_ext);
00950 
00951    return res;
00952 }
00953 
00954 static int load_module(void)
00955 {
00956    int res = 0;
00957 
00958    res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
00959    res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
00960 
00961    return res;
00962 }
00963 
00964 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");

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