Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:47 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  *
    00006  * A license has been granted to Digium (via disclaimer) for the use of
    00007  * this code.
    00008  *
    00009  * See http://www.asterisk.org for more information about
    00010  * the Asterisk project. Please do not directly contact
    00011  * any of the maintainers of this project for assistance;
    00012  * the project provides a web site, mailing lists and IRC
    00013  * channels for your use.
    00014  *
    00015  * This program is free software, distributed under the terms of
    00016  * the GNU General Public License Version 2. See the LICENSE file
    00017  * at the top of the source tree.
    00018  */
    00019
    00020 /*! \file
    00021  * \brief ChanSpy: Listen in on any channel.
    00022  *
    00023  * \ingroup applications
    00024  */
    00025
    00026 #include <stdlib.h>
    00027 #include <stdio.h>
    00028 #include <string.h>
    00029 #include <unistd.h>
    00030 #include <ctype.h>
    00031
    00032 #include "asterisk.h"
    00033
    00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211526 $")
    00035
    00036 #include "asterisk/file.h"
    00037 #include "asterisk/logger.h"
    00038 #include "asterisk/channel.h"
    00039 #include "asterisk/chanspy.h"
    00040 #include "asterisk/features.h"
    00041 #include "asterisk/options.h"
    00042 #include "asterisk/app.h"
    00043 #include "asterisk/utils.h"
    00044 #include "asterisk/say.h"
    00045 #include "asterisk/pbx.h"
    00046 #include "asterisk/translate.h"
    00047 #include "asterisk/module.h"
    00048 #include "asterisk/lock.h"
    00049
    00050 AST_MUTEX_DEFINE_STATIC(modlock);
    00051
    00052 #define AST_NAME_STRLEN 256
    00053 #define ALL_DONE(u, ret) LOCAL_USER_REMOVE(u); return ret;
    00054 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
    00055
    00056 static const char *synopsis = "Listen to the audio of an active channel";
    00057 static const char *app = "ChanSpy";
    00058 static const char *desc =
    00059 "  ChanSpy([chanprefix][|options]): This application is used to listen to the\n"
    00060 "audio from an active 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'.\n"
    00070 "  Options:\n"
    00071 "    b - Only spy on channels involved in a bridged call.\n"
    00072 "    g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
    00073 "             'grp'.\n"
    00074 "    q - Don't play a beep when beginning to spy on a channel.\n"
    00075 "    r[(basename)] - Record the session to the monitor spool directory. An\n"
    00076 "                    optional base for the filename may be specified. The\n"
    00077 "                    default is 'chanspy'.\n"
    00078 "    v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
    00079 "                 negative value refers to a quieter setting.\n"
    00080 ;
    00081
    00082 static const char *chanspy_spy_type = "ChanSpy";
    00083
    00084 enum {
    00085    OPTION_QUIET    = (1 << 0),   /* Quiet, no announcement */
    00086    OPTION_BRIDGED   = (1 << 1),  /* Only look at bridged calls */
    00087    OPTION_VOLUME    = (1 << 2),  /* Specify initial volume */
    00088    OPTION_GROUP     = (1 << 3),  /* Only look at channels in group */
    00089    OPTION_RECORD    = (1 << 4),  /* Record */
    00090 } chanspy_opt_flags;
    00091
    00092 enum {
    00093    OPT_ARG_VOLUME = 0,
    00094    OPT_ARG_GROUP,
    00095    OPT_ARG_RECORD,
    00096    OPT_ARG_ARRAY_SIZE,
    00097 } chanspy_opt_args;
    00098
    00099 AST_APP_OPTIONS(chanspy_opts, {
    00100    AST_APP_OPTION('q', OPTION_QUIET),
    00101    AST_APP_OPTION('b', OPTION_BRIDGED),
    00102    AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
    00103    AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
    00104    AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
    00105 });
    00106
    00107 STANDARD_LOCAL_USER;
    00108 LOCAL_USER_DECL;
    00109
    00110 struct chanspy_translation_helper {
    00111    /* spy data */
    00112    struct ast_channel_spy spy;
    00113    int fd;
    00114    int volfactor;
    00115 };
    00116
    00117 static struct ast_channel *local_channel_walk(struct ast_channel *chan)
    00118 {
    00119    struct ast_channel *ret;
    00120    ast_mutex_lock(&modlock);
    00121    if ((ret = ast_channel_walk_locked(chan))) {
    00122       ast_mutex_unlock(&ret->lock);
    00123    }
    00124    ast_mutex_unlock(&modlock);
    00125    return ret;
    00126 }
    00127
    00128 static struct ast_channel *local_get_channel_begin_name(char *name)
    00129 {
    00130    struct ast_channel *chan, *ret = NULL;
    00131    ast_mutex_lock(&modlock);
    00132    chan = local_channel_walk(NULL);
    00133    while (chan) {
    00134       if (!strncmp(chan->name, name, strlen(name)) && strncmp(chan->name, "Zap/pseudo", 10)) {
    00135          ret = chan;
    00136          break;
    00137       }
    00138       chan = local_channel_walk(chan);
    00139    }
    00140    ast_mutex_unlock(&modlock);
    00141
    00142    return ret;
    00143 }
    00144
    00145 static void *spy_alloc(struct ast_channel *chan, void *data)
    00146 {
    00147    /* just store the data pointer in the channel structure */
    00148    return data;
    00149 }
    00150
    00151 static void spy_release(struct ast_channel *chan, void *data)
    00152 {
    00153    /* nothing to do */
    00154 }
    00155
    00156 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
    00157 {
    00158    struct chanspy_translation_helper *csth = data;
    00159    struct ast_frame *f;
    00160
    00161    if (csth->spy.status != CHANSPY_RUNNING)
    00162       /* Channel is already gone more than likely */
    00163       return -1;
    00164
    00165    ast_mutex_lock(&csth->spy.lock);
    00166    f = ast_channel_spy_read_frame(&csth->spy, samples);
    00167    ast_mutex_unlock(&csth->spy.lock);
    00168
    00169    if (!f)
    00170       return 0;
    00171
    00172    if (ast_write(chan, f)) {
    00173       ast_frfree(f);
    00174       return -1;
    00175    }
    00176
    00177    if (csth->fd)
    00178       write(csth->fd, f->data, f->datalen);
    00179
    00180    ast_frfree(f);
    00181
    00182    return 0;
    00183 }
    00184
    00185
    00186 static struct ast_generator spygen = {
    00187    .alloc = spy_alloc,
    00188    .release = spy_release,
    00189    .generate = spy_generate,
    00190 };
    00191
    00192 static int start_spying(struct ast_channel *chan, struct ast_channel *spychan, struct ast_channel_spy *spy)
    00193 {
    00194    int res;
    00195    struct ast_channel *peer;
    00196
    00197    ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan->name, chan->name);
    00198
    00199    ast_mutex_lock(&chan->lock);
    00200    res = ast_channel_spy_add(chan, spy);
    00201    ast_mutex_unlock(&chan->lock);
    00202
    00203    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
    00204       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
    00205    }
    00206
    00207    return res;
    00208 }
    00209
    00210 /* Map 'volume' levels from -4 through +4 into
    00211    decibel (dB) settings for channel drivers
    00212 */
    00213 static signed char volfactor_map[] = {
    00214    -24,
    00215    -18,
    00216    -12,
    00217    -6,
    00218    0,
    00219    6,
    00220    12,
    00221    18,
    00222    24,
    00223 };
    00224
    00225 /* attempt to set the desired gain adjustment via the channel driver;
    00226    if successful, clear it out of the csth structure so the
    00227    generator will not attempt to do the adjustment itself
    00228 */
    00229 static void set_volume(struct ast_channel *chan, struct chanspy_translation_helper *csth)
    00230 {
    00231    signed char volume_adjust = volfactor_map[csth->volfactor + 4];
    00232
    00233    if (!ast_channel_setoption(chan, AST_OPTION_TXGAIN, &volume_adjust, sizeof(volume_adjust), 0))
    00234       csth->volfactor = 0;
    00235 }
    00236
    00237 static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd)
    00238 {
    00239    struct chanspy_translation_helper csth;
    00240    int running = 0, res = 0, x = 0;
    00241    char inp[24] = "", *name = NULL;
    00242    struct ast_frame *f = NULL;
    00243
    00244    if ((chan && ast_check_hangup(chan)) || (spyee && ast_check_hangup(spyee)))
    00245       return 0;
    00246
    00247    name = ast_strdupa(spyee->name);
    00248    if (option_verbose > 1)
    00249       ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name);
    00250
    00251    memset(&csth, 0, sizeof(csth));
    00252    ast_set_flag(&csth.spy, CHANSPY_FORMAT_AUDIO);
    00253    ast_set_flag(&csth.spy, CHANSPY_TRIGGER_NONE);
    00254    ast_set_flag(&csth.spy, CHANSPY_MIXAUDIO);
    00255    csth.spy.type = chanspy_spy_type;
    00256    csth.spy.status = CHANSPY_RUNNING;
    00257    csth.spy.read_queue.format = AST_FORMAT_SLINEAR;
    00258    csth.spy.write_queue.format = AST_FORMAT_SLINEAR;
    00259    ast_mutex_init(&csth.spy.lock);
    00260    csth.volfactor = *volfactor;
    00261    set_volume(chan, &csth);
    00262    if (csth.volfactor) {
    00263       ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
    00264       csth.spy.read_vol_adjustment = csth.volfactor;
    00265       ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
    00266       csth.spy.write_vol_adjustment = csth.volfactor;
    00267    }
    00268    csth.fd = fd;
    00269
    00270    if (start_spying(spyee, chan, &csth.spy)) {
    00271       ast_channel_spy_free(&csth.spy);
    00272       return 0;
    00273    }
    00274
    00275    ast_activate_generator(chan, &spygen, &csth);
    00276
    00277    while (csth.spy.status == CHANSPY_RUNNING &&
    00278           (res = ast_waitfor(chan, -1) > -1)) {
    00279
    00280       /* Read in frame from channel, break out if no frame */
    00281       if (!(f = ast_read(chan)))
    00282          break;
    00283
    00284       /* Now if this is DTMF then we have to handle it as such, otherwise just skip it */
    00285       res = 0;
    00286       if (f->frametype == AST_FRAME_DTMF)
    00287          res = f->subclass;
    00288       ast_frfree(f);
    00289       if (!res)
    00290          continue;
    00291
    00292       if (x == sizeof(inp))
    00293          x = 0;
    00294
    00295       if (res < 0) {
    00296          running = -1;
    00297          break;
    00298       }
    00299
    00300       /* Process DTMF digits */
    00301       if (res == '#') {
    00302          if (!ast_strlen_zero(inp)) {
    00303             running = x ? atoi(inp) : -1;
    00304             break;
    00305          } else {
    00306             (*volfactor)++;
    00307             if (*volfactor > 4)
    00308                *volfactor = -1;
    00309             if (option_verbose > 2)
    00310                ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
    00311             csth.volfactor = *volfactor;
    00312             set_volume(chan, &csth);
    00313             if (csth.volfactor) {
    00314                ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
    00315                csth.spy.read_vol_adjustment = csth.volfactor;
    00316                ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
    00317                csth.spy.write_vol_adjustment = csth.volfactor;
    00318             } else {
    00319                ast_clear_flag(&csth.spy, CHANSPY_READ_VOLADJUST);
    00320                ast_clear_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST);
    00321             }
    00322          }
    00323       } else if (res == '*') {
    00324          break;
    00325       } else if (res >= 48 && res <= 57) {
    00326          inp[x++] = res;
    00327       }
    00328    }
    00329
    00330    ast_deactivate_generator(chan);
    00331
    00332    csth.spy.status = CHANSPY_DONE;
    00333
    00334    ast_mutex_lock(&csth.spy.lock);
    00335    if (csth.spy.chan) {
    00336       ast_mutex_lock(&csth.spy.chan->lock);
    00337       ast_channel_spy_remove(csth.spy.chan, &csth.spy);
    00338       ast_mutex_unlock(&csth.spy.chan->lock);
    00339    }
    00340    ast_mutex_unlock(&csth.spy.lock);
    00341
    00342    if (option_verbose > 1)
    00343       ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
    00344
    00345    ast_channel_spy_free(&csth.spy);
    00346
    00347    return running;
    00348 }
    00349
    00350 static int chanspy_exec(struct ast_channel *chan, void *data)
    00351 {
    00352    struct localuser *u;
    00353    struct ast_channel *peer=NULL, *prev=NULL;
    00354    char name[AST_NAME_STRLEN],
    00355       peer_name[AST_NAME_STRLEN + 5],
    00356       *args,
    00357       *ptr = NULL,
    00358       *options = NULL,
    00359       *spec = NULL,
    00360       *argv[5],
    00361       *mygroup = NULL,
    00362       *recbase = NULL;
    00363    int res = -1,
    00364       volfactor = 0,
    00365       silent = 0,
    00366       argc = 0,
    00367       bronly = 0,
    00368       chosen = 0,
    00369       count=0,
    00370       waitms = 100,
    00371       num = 0,
    00372       oldrf = 0,
    00373       oldwf = 0,
    00374       fd = 0;
    00375    struct ast_flags flags;
    00376    signed char zero_volume = 0;
    00377
    00378    if (!(args = ast_strdupa((char *)data))) {
    00379       ast_log(LOG_ERROR, "Out of memory!\n");
    00380       return -1;
    00381    }
    00382
    00383    LOCAL_USER_ADD(u);
    00384
    00385    oldrf = chan->readformat;
    00386    oldwf = chan->writeformat;
    00387    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
    00388       ast_log(LOG_ERROR, "Could Not Set Read Format.\n");
    00389       LOCAL_USER_REMOVE(u);
    00390       return -1;
    00391    }
    00392
    00393    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
    00394       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
    00395       LOCAL_USER_REMOVE(u);
    00396       return -1;
    00397    }
    00398
    00399    ast_answer(chan);
    00400
    00401    ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
    00402
    00403    if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
    00404       spec = argv[0];
    00405       if ( argc > 1) {
    00406          options = argv[1];
    00407       }
    00408       if (ast_strlen_zero(spec) || !strcmp(spec, "all")) {
    00409          spec = NULL;
    00410       }
    00411    }
    00412
    00413    if (options) {
    00414       char *opts[OPT_ARG_ARRAY_SIZE];
    00415       ast_app_parse_options(chanspy_opts, &flags, opts, options);
    00416       if (ast_test_flag(&flags, OPTION_GROUP)) {
    00417          mygroup = opts[OPT_ARG_GROUP];
    00418       }
    00419       if (ast_test_flag(&flags, OPTION_RECORD)) {
    00420          if (!(recbase = opts[OPT_ARG_RECORD])) {
    00421             recbase = "chanspy";
    00422          }
    00423       }
    00424       silent = ast_test_flag(&flags, OPTION_QUIET);
    00425       bronly = ast_test_flag(&flags, OPTION_BRIDGED);
    00426       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
    00427          int vol;
    00428
    00429          if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
    00430             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
    00431          else
    00432             volfactor = vol;
    00433          }
    00434    }
    00435    else
    00436       ast_clear_flag(&flags, AST_FLAGS_ALL);
    00437
    00438    if (recbase) {
    00439       char filename[512];
    00440       snprintf(filename,sizeof(filename),"%s/%s.%d.raw",ast_config_AST_MONITOR_DIR, recbase, (int)time(NULL));
    00441       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
    00442          ast_log(LOG_WARNING, "Cannot open %s for recording\n", filename);
    00443          fd = 0;
    00444       }
    00445    }
    00446
    00447    for(;;) {
    00448       if (!silent) {
    00449          res = ast_streamfile(chan, "beep", chan->language);
    00450          if (!res)
    00451             res = ast_waitstream(chan, "");
    00452          if (res < 0) {
    00453             ast_clear_flag(chan, AST_FLAG_SPYING);
    00454             break;
    00455          }
    00456       }
    00457
    00458       count = 0;
    00459       res = ast_waitfordigit(chan, waitms);
    00460       if (res < 0) {
    00461          ast_clear_flag(chan, AST_FLAG_SPYING);
    00462          break;
    00463       }
    00464
    00465       peer = local_channel_walk(NULL);
    00466       prev=NULL;
    00467       while(peer) {
    00468          if (peer != chan) {
    00469             char *group = NULL;
    00470             int igrp = 1;
    00471
    00472             if (peer == prev && !chosen) {
    00473                break;
    00474             }
    00475             chosen = 0;
    00476             group = pbx_builtin_getvar_helper(peer, "SPYGROUP");
    00477             if (mygroup) {
    00478                if (!group || strcmp(mygroup, group)) {
    00479                   igrp = 0;
    00480                }
    00481             }
    00482
    00483             if (igrp && (!spec || ((strlen(spec) <= strlen(peer->name) &&
    00484                      !strncasecmp(peer->name, spec, strlen(spec)))))) {
    00485                if (peer && (!bronly || ast_bridged_channel(peer)) &&
    00486                    !ast_check_hangup(peer) && !ast_test_flag(peer, AST_FLAG_SPYING)) {
    00487                   int x = 0;
    00488                   strncpy(peer_name, "spy-", 5);
    00489                   strncpy(peer_name + strlen(peer_name), peer->name, AST_NAME_STRLEN);
    00490                   ptr = strchr(peer_name, '/');
    00491                   *ptr = '\0';
    00492                   ptr++;
    00493                   for (x = 0 ; x < strlen(peer_name) ; x++) {
    00494                      if (peer_name[x] == '/') {
    00495                         break;
    00496                      }
    00497                      peer_name[x] = tolower(peer_name[x]);
    00498                   }
    00499
    00500                   if (!silent) {
    00501                      if (ast_fileexists(peer_name, NULL, NULL) != -1) {
    00502                         res = ast_streamfile(chan, peer_name, chan->language);
    00503                         if (!res)
    00504                            res = ast_waitstream(chan, "");
    00505                         if (res)
    00506                            break;
    00507                      } else
    00508                         res = ast_say_character_str(chan, peer_name, "", chan->language);
    00509                      if ((num=atoi(ptr)))
    00510                         ast_say_digits(chan, atoi(ptr), "", chan->language);
    00511                   }
    00512                   count++;
    00513                   prev = peer;
    00514                   res = channel_spy(chan, peer, &volfactor, fd);
    00515                   if (res == -1) {
    00516                      break;
    00517                   } else if (res > 1 && spec) {
    00518                      snprintf(name, AST_NAME_STRLEN, "%s/%d", spec, res);
    00519                      if ((peer = local_get_channel_begin_name(name))) {
    00520                         chosen = 1;
    00521                      }
    00522                      continue;
    00523                   }
    00524                }
    00525             }
    00526          }
    00527          if ((peer = local_channel_walk(peer)) == NULL) {
    00528             break;
    00529          }
    00530       }
    00531       waitms = count ? 100 : 5000;
    00532    }
    00533
    00534
    00535    if (fd > 0) {
    00536       close(fd);
    00537    }
    00538
    00539    if (oldrf && ast_set_read_format(chan, oldrf) < 0) {
    00540       ast_log(LOG_ERROR, "Could Not Set Read Format.\n");
    00541    }
    00542
    00543    if (oldwf && ast_set_write_format(chan, oldwf) < 0) {
    00544       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
    00545    }
    00546
    00547    ast_clear_flag(chan, AST_FLAG_SPYING);
    00548
    00549    ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
    00550
    00551    ALL_DONE(u, res);
    00552 }
    00553
    00554 int unload_module(void)
    00555 {
    00556    int res;
    00557
    00558    res = ast_unregister_application(app);
    00559
    00560    STANDARD_HANGUP_LOCALUSERS;
    00561
    00562    return res;
    00563 }
    00564
    00565 int load_module(void)
    00566 {
    00567    return ast_register_application(app, chanspy_exec, synopsis, desc);
    00568 }
    00569
    00570 char *description(void)
    00571 {
    00572    return (char *) synopsis;
    00573 }
    00574
    00575 int usecount(void)
    00576 {
    00577    int res;
    00578    STANDARD_USECOUNT(res);
    00579    return res;
    00580 }
    00581
    00582 char *key()
    00583 {
    00584    return ASTERISK_GPL_KEY;
    00585 }