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

Generated on Fri May 26 01:45:26 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6