Sat Nov 25 00:45:28 2006

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: 45060 $")
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\n";
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    ast_mutex_lock(&csth.spy.lock);
00333    if (csth.spy.chan) {
00334       csth.spy.status = CHANSPY_DONE;
00335       ast_mutex_lock(&csth.spy.chan->lock);
00336       ast_channel_spy_remove(csth.spy.chan, &csth.spy);
00337       ast_mutex_unlock(&csth.spy.chan->lock);
00338    }
00339    ast_mutex_unlock(&csth.spy.lock);
00340 
00341    if (option_verbose > 1)
00342       ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
00343 
00344    ast_channel_spy_free(&csth.spy);
00345 
00346    return running;
00347 }
00348 
00349 static int chanspy_exec(struct ast_channel *chan, void *data)
00350 {
00351    struct localuser *u;
00352    struct ast_channel *peer=NULL, *prev=NULL;
00353    char name[AST_NAME_STRLEN],
00354       peer_name[AST_NAME_STRLEN + 5],
00355       *args,
00356       *ptr = NULL,
00357       *options = NULL,
00358       *spec = NULL,
00359       *argv[5],
00360       *mygroup = NULL,
00361       *recbase = NULL;
00362    int res = -1,
00363       volfactor = 0,
00364       silent = 0,
00365       argc = 0,
00366       bronly = 0,
00367       chosen = 0,
00368       count=0,
00369       waitms = 100,
00370       num = 0,
00371       oldrf = 0,
00372       oldwf = 0,
00373       fd = 0;
00374    struct ast_flags flags;
00375    signed char zero_volume = 0;
00376 
00377    if (!(args = ast_strdupa((char *)data))) {
00378       ast_log(LOG_ERROR, "Out of memory!\n");
00379       return -1;
00380    }
00381 
00382    LOCAL_USER_ADD(u);
00383 
00384    oldrf = chan->readformat;
00385    oldwf = chan->writeformat;
00386    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
00387       ast_log(LOG_ERROR, "Could Not Set Read Format.\n");
00388       LOCAL_USER_REMOVE(u);
00389       return -1;
00390    }
00391    
00392    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00393       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00394       LOCAL_USER_REMOVE(u);
00395       return -1;
00396    }
00397 
00398    ast_answer(chan);
00399 
00400    ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
00401 
00402    if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
00403       spec = argv[0];
00404       if ( argc > 1) {
00405          options = argv[1];
00406       }
00407       if (ast_strlen_zero(spec) || !strcmp(spec, "all")) {
00408          spec = NULL;
00409       }
00410    }
00411    
00412    if (options) {
00413       char *opts[OPT_ARG_ARRAY_SIZE];
00414       ast_app_parse_options(chanspy_opts, &flags, opts, options);
00415       if (ast_test_flag(&flags, OPTION_GROUP)) {
00416          mygroup = opts[OPT_ARG_GROUP];
00417       }
00418       if (ast_test_flag(&flags, OPTION_RECORD)) {
00419          if (!(recbase = opts[OPT_ARG_RECORD])) {
00420             recbase = "chanspy";
00421          }
00422       }
00423       silent = ast_test_flag(&flags, OPTION_QUIET);
00424       bronly = ast_test_flag(&flags, OPTION_BRIDGED);
00425       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00426          int vol;
00427 
00428          if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
00429             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00430          else
00431             volfactor = vol;
00432          }
00433    }
00434 
00435    if (recbase) {
00436       char filename[512];
00437       snprintf(filename,sizeof(filename),"%s/%s.%d.raw",ast_config_AST_MONITOR_DIR, recbase, (int)time(NULL));
00438       if ((fd = open(filename, O_CREAT | O_WRONLY, O_TRUNC, 0644)) <= 0) {
00439          ast_log(LOG_WARNING, "Cannot open %s for recording\n", filename);
00440          fd = 0;
00441       }
00442    }
00443 
00444    for(;;) {
00445       if (!silent) {
00446          res = ast_streamfile(chan, "beep", chan->language);
00447          if (!res)
00448             res = ast_waitstream(chan, "");
00449          if (res < 0) {
00450             ast_clear_flag(chan, AST_FLAG_SPYING);
00451             break;
00452          }
00453       }
00454 
00455       count = 0;
00456       res = ast_waitfordigit(chan, waitms);
00457       if (res < 0) {
00458          ast_clear_flag(chan, AST_FLAG_SPYING);
00459          break;
00460       }
00461             
00462       peer = local_channel_walk(NULL);
00463       prev=NULL;
00464       while(peer) {
00465          if (peer != chan) {
00466             char *group = NULL;
00467             int igrp = 1;
00468 
00469             if (peer == prev && !chosen) {
00470                break;
00471             }
00472             chosen = 0;
00473             group = pbx_builtin_getvar_helper(peer, "SPYGROUP");
00474             if (mygroup) {
00475                if (!group || strcmp(mygroup, group)) {
00476                   igrp = 0;
00477                }
00478             }
00479             
00480             if (igrp && (!spec || ((strlen(spec) <= strlen(peer->name) &&
00481                      !strncasecmp(peer->name, spec, strlen(spec)))))) {
00482                if (peer && (!bronly || ast_bridged_channel(peer)) &&
00483                    !ast_check_hangup(peer) && !ast_test_flag(peer, AST_FLAG_SPYING)) {
00484                   int x = 0;
00485                   strncpy(peer_name, "spy-", 5);
00486                   strncpy(peer_name + strlen(peer_name), peer->name, AST_NAME_STRLEN);
00487                   ptr = strchr(peer_name, '/');
00488                   *ptr = '\0';
00489                   ptr++;
00490                   for (x = 0 ; x < strlen(peer_name) ; x++) {
00491                      if (peer_name[x] == '/') {
00492                         break;
00493                      }
00494                      peer_name[x] = tolower(peer_name[x]);
00495                   }
00496 
00497                   if (!silent) {
00498                      if (ast_fileexists(peer_name, NULL, NULL) != -1) {
00499                         res = ast_streamfile(chan, peer_name, chan->language);
00500                         if (!res)
00501                            res = ast_waitstream(chan, "");
00502                         if (res)
00503                            break;
00504                      } else
00505                         res = ast_say_character_str(chan, peer_name, "", chan->language);
00506                      if ((num=atoi(ptr))) 
00507                         ast_say_digits(chan, atoi(ptr), "", chan->language);
00508                   }
00509                   count++;
00510                   prev = peer;
00511                   res = channel_spy(chan, peer, &volfactor, fd);
00512                   if (res == -1) {
00513                      break;
00514                   } else if (res > 1 && spec) {
00515                      snprintf(name, AST_NAME_STRLEN, "%s/%d", spec, res);
00516                      if ((peer = local_get_channel_begin_name(name))) {
00517                         chosen = 1;
00518                      }
00519                      continue;
00520                   }
00521                }
00522             }
00523          }
00524          if ((peer = local_channel_walk(peer)) == NULL) {
00525             break;
00526          }
00527       }
00528       waitms = count ? 100 : 5000;
00529    }
00530    
00531 
00532    if (fd > 0) {
00533       close(fd);
00534    }
00535 
00536    if (oldrf && ast_set_read_format(chan, oldrf) < 0) {
00537       ast_log(LOG_ERROR, "Could Not Set Read Format.\n");
00538    }
00539    
00540    if (oldwf && ast_set_write_format(chan, oldwf) < 0) {
00541       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00542    }
00543 
00544    ast_clear_flag(chan, AST_FLAG_SPYING);
00545 
00546    ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00547 
00548    ALL_DONE(u, res);
00549 }
00550 
00551 int unload_module(void)
00552 {
00553    int res;
00554 
00555    res = ast_unregister_application(app);
00556 
00557    STANDARD_HANGUP_LOCALUSERS;
00558 
00559    return res;
00560 }
00561 
00562 int load_module(void)
00563 {
00564    return ast_register_application(app, chanspy_exec, synopsis, desc);
00565 }
00566 
00567 char *description(void)
00568 {
00569    return (char *) synopsis;
00570 }
00571 
00572 int usecount(void)
00573 {
00574    int res;
00575    STANDARD_USECOUNT(res);
00576    return res;
00577 }
00578 
00579 char *key()
00580 {
00581    return ASTERISK_GPL_KEY;
00582 }

Generated on Sat Nov 25 00:45:28 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6