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

Generated on Fri Jun 19 12:09:24 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7