Wed Mar 3 22:35:38 2010

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

Generated on 3 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1