Wed Aug 20 06:36:02 2008

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: 75066 $")
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], "%d", &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 }

Generated on Wed Aug 20 06:36:02 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1