Sat Sep 16 07:28:05 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: 42054 $")
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, res = 0, x = 0;
00241    char inp[24];
00242    char *name=NULL;
00243    struct ast_frame *f;
00244 
00245    running = (chan && !ast_check_hangup(chan) && spyee && !ast_check_hangup(spyee));
00246 
00247    if (running) {
00248       memset(inp, 0, sizeof(inp));
00249       name = ast_strdupa(spyee->name);
00250       if (option_verbose >= 2)
00251          ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name);
00252 
00253       memset(&csth, 0, sizeof(csth));
00254       ast_set_flag(&csth.spy, CHANSPY_FORMAT_AUDIO);
00255       ast_set_flag(&csth.spy, CHANSPY_TRIGGER_NONE);
00256       ast_set_flag(&csth.spy, CHANSPY_MIXAUDIO);
00257       csth.spy.type = chanspy_spy_type;
00258       csth.spy.status = CHANSPY_RUNNING;
00259       csth.spy.read_queue.format = AST_FORMAT_SLINEAR;
00260       csth.spy.write_queue.format = AST_FORMAT_SLINEAR;
00261       ast_mutex_init(&csth.spy.lock);
00262       csth.volfactor = *volfactor;
00263       set_volume(chan, &csth);
00264       csth.spy.read_vol_adjustment = csth.volfactor;
00265       csth.spy.write_vol_adjustment = csth.volfactor;
00266       csth.fd = fd;
00267 
00268       if (start_spying(spyee, chan, &csth.spy))
00269          running = 0;
00270    }
00271 
00272    if (running) {
00273       running = 1;
00274       ast_activate_generator(chan, &spygen, &csth);
00275 
00276       while (csth.spy.status == CHANSPY_RUNNING &&
00277              chan && !ast_check_hangup(chan) &&
00278              spyee &&
00279              !ast_check_hangup(spyee) &&
00280              running == 1 &&
00281              (res = ast_waitfor(chan, -1) > -1)) {
00282          if ((f = ast_read(chan))) {
00283             res = 0;
00284             if (f->frametype == AST_FRAME_DTMF) {
00285                res = f->subclass;
00286             }
00287             ast_frfree(f);
00288             if (!res) {
00289                continue;
00290             }
00291          } else {
00292             break;
00293          }
00294          if (x == sizeof(inp)) {
00295             x = 0;
00296          }
00297          if (res < 0) {
00298             running = -1;
00299          }
00300          if (res == 0) {
00301             continue;
00302          } else if (res == '*') {
00303             running = 0; 
00304          } else if (res == '#') {
00305             if (!ast_strlen_zero(inp)) {
00306                running = x ? atoi(inp) : -1;
00307                break;
00308             } else {
00309                (*volfactor)++;
00310                if (*volfactor > 4) {
00311                   *volfactor = -4;
00312                }
00313                if (option_verbose > 2) {
00314                   ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
00315                }
00316                csth.volfactor = *volfactor;
00317                set_volume(chan, &csth);
00318                csth.spy.read_vol_adjustment = csth.volfactor;
00319                csth.spy.write_vol_adjustment = csth.volfactor;
00320             }
00321          } else if (res >= 48 && res <= 57) {
00322             inp[x++] = res;
00323          }
00324       }
00325       ast_deactivate_generator(chan);
00326 
00327       if (csth.spy.chan) {
00328          csth.spy.status = CHANSPY_DONE;
00329          ast_mutex_lock(&csth.spy.chan->lock);
00330          ast_channel_spy_remove(csth.spy.chan, &csth.spy);
00331          ast_mutex_unlock(&csth.spy.chan->lock);
00332       }
00333 
00334       if (option_verbose >= 2) {
00335          ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
00336       }
00337    } else {
00338       running = 0;
00339    }
00340 
00341    ast_channel_spy_free(&csth.spy);
00342 
00343    return running;
00344 }
00345 
00346 static int chanspy_exec(struct ast_channel *chan, void *data)
00347 {
00348    struct localuser *u;
00349    struct ast_channel *peer=NULL, *prev=NULL;
00350    char name[AST_NAME_STRLEN],
00351       peer_name[AST_NAME_STRLEN + 5],
00352       *args,
00353       *ptr = NULL,
00354       *options = NULL,
00355       *spec = NULL,
00356       *argv[5],
00357       *mygroup = NULL,
00358       *recbase = NULL;
00359    int res = -1,
00360       volfactor = 0,
00361       silent = 0,
00362       argc = 0,
00363       bronly = 0,
00364       chosen = 0,
00365       count=0,
00366       waitms = 100,
00367       num = 0,
00368       oldrf = 0,
00369       oldwf = 0,
00370       fd = 0;
00371    struct ast_flags flags;
00372    signed char zero_volume = 0;
00373 
00374    if (!(args = ast_strdupa((char *)data))) {
00375       ast_log(LOG_ERROR, "Out of memory!\n");
00376       return -1;
00377    }
00378 
00379    LOCAL_USER_ADD(u);
00380 
00381    oldrf = chan->readformat;
00382    oldwf = chan->writeformat;
00383    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
00384       ast_log(LOG_ERROR, "Could Not Set Read Format.\n");
00385       LOCAL_USER_REMOVE(u);
00386       return -1;
00387    }
00388    
00389    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00390       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00391       LOCAL_USER_REMOVE(u);
00392       return -1;
00393    }
00394 
00395    ast_answer(chan);
00396 
00397    ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
00398 
00399    if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
00400       spec = argv[0];
00401       if ( argc > 1) {
00402          options = argv[1];
00403       }
00404       if (ast_strlen_zero(spec) || !strcmp(spec, "all")) {
00405          spec = NULL;
00406       }
00407    }
00408    
00409    if (options) {
00410       char *opts[OPT_ARG_ARRAY_SIZE];
00411       ast_app_parse_options(chanspy_opts, &flags, opts, options);
00412       if (ast_test_flag(&flags, OPTION_GROUP)) {
00413          mygroup = opts[OPT_ARG_GROUP];
00414       }
00415       if (ast_test_flag(&flags, OPTION_RECORD)) {
00416          if (!(recbase = opts[OPT_ARG_RECORD])) {
00417             recbase = "chanspy";
00418          }
00419       }
00420       silent = ast_test_flag(&flags, OPTION_QUIET);
00421       bronly = ast_test_flag(&flags, OPTION_BRIDGED);
00422       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00423          int vol;
00424 
00425          if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
00426             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00427          else
00428             volfactor = vol;
00429          }
00430    }
00431 
00432    if (recbase) {
00433       char filename[512];
00434       snprintf(filename,sizeof(filename),"%s/%s.%d.raw",ast_config_AST_MONITOR_DIR, recbase, (int)time(NULL));
00435       if ((fd = open(filename, O_CREAT | O_WRONLY, O_TRUNC, 0644)) <= 0) {
00436          ast_log(LOG_WARNING, "Cannot open %s for recording\n", filename);
00437          fd = 0;
00438       }
00439    }
00440 
00441    for(;;) {
00442       if (!silent) {
00443          res = ast_streamfile(chan, "beep", chan->language);
00444          if (!res)
00445             res = ast_waitstream(chan, "");
00446          if (res < 0) {
00447             ast_clear_flag(chan, AST_FLAG_SPYING);
00448             break;
00449          }
00450       }
00451 
00452       count = 0;
00453       res = ast_waitfordigit(chan, waitms);
00454       if (res < 0) {
00455          ast_clear_flag(chan, AST_FLAG_SPYING);
00456          break;
00457       }
00458             
00459       peer = local_channel_walk(NULL);
00460       prev=NULL;
00461       while(peer) {
00462          if (peer != chan) {
00463             char *group = NULL;
00464             int igrp = 1;
00465 
00466             if (peer == prev && !chosen) {
00467                break;
00468             }
00469             chosen = 0;
00470             group = pbx_builtin_getvar_helper(peer, "SPYGROUP");
00471             if (mygroup) {
00472                if (!group || strcmp(mygroup, group)) {
00473                   igrp = 0;
00474                }
00475             }
00476             
00477             if (igrp && (!spec || ((strlen(spec) <= strlen(peer->name) &&
00478                      !strncasecmp(peer->name, spec, strlen(spec)))))) {
00479                if (peer && (!bronly || ast_bridged_channel(peer)) &&
00480                    !ast_check_hangup(peer) && !ast_test_flag(peer, AST_FLAG_SPYING)) {
00481                   int x = 0;
00482                   strncpy(peer_name, "spy-", 5);
00483                   strncpy(peer_name + strlen(peer_name), peer->name, AST_NAME_STRLEN);
00484                   ptr = strchr(peer_name, '/');
00485                   *ptr = '\0';
00486                   ptr++;
00487                   for (x = 0 ; x < strlen(peer_name) ; x++) {
00488                      if (peer_name[x] == '/') {
00489                         break;
00490                      }
00491                      peer_name[x] = tolower(peer_name[x]);
00492                   }
00493 
00494                   if (!silent) {
00495                      if (ast_fileexists(peer_name, NULL, NULL) != -1) {
00496                         res = ast_streamfile(chan, peer_name, chan->language);
00497                         if (!res)
00498                            res = ast_waitstream(chan, "");
00499                         if (res)
00500                            break;
00501                      } else
00502                         res = ast_say_character_str(chan, peer_name, "", chan->language);
00503                      if ((num=atoi(ptr))) 
00504                         ast_say_digits(chan, atoi(ptr), "", chan->language);
00505                   }
00506                   count++;
00507                   prev = peer;
00508                   res = channel_spy(chan, peer, &volfactor, fd);
00509                   if (res == -1) {
00510                      break;
00511                   } else if (res > 1 && spec) {
00512                      snprintf(name, AST_NAME_STRLEN, "%s/%d", spec, res);
00513                      if ((peer = local_get_channel_begin_name(name))) {
00514                         chosen = 1;
00515                      }
00516                      continue;
00517                   }
00518                }
00519             }
00520          }
00521          if ((peer = local_channel_walk(peer)) == NULL) {
00522             break;
00523          }
00524       }
00525       waitms = count ? 100 : 5000;
00526    }
00527    
00528 
00529    if (fd > 0) {
00530       close(fd);
00531    }
00532 
00533    if (oldrf && ast_set_read_format(chan, oldrf) < 0) {
00534       ast_log(LOG_ERROR, "Could Not Set Read Format.\n");
00535    }
00536    
00537    if (oldwf && ast_set_write_format(chan, oldwf) < 0) {
00538       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00539    }
00540 
00541    ast_clear_flag(chan, AST_FLAG_SPYING);
00542 
00543    ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00544 
00545    ALL_DONE(u, res);
00546 }
00547 
00548 int unload_module(void)
00549 {
00550    int res;
00551 
00552    res = ast_unregister_application(app);
00553 
00554    STANDARD_HANGUP_LOCALUSERS;
00555 
00556    return res;
00557 }
00558 
00559 int load_module(void)
00560 {
00561    return ast_register_application(app, chanspy_exec, synopsis, desc);
00562 }
00563 
00564 char *description(void)
00565 {
00566    return (char *) synopsis;
00567 }
00568 
00569 int usecount(void)
00570 {
00571    int res;
00572    STANDARD_USECOUNT(res);
00573    return res;
00574 }
00575 
00576 char *key()
00577 {
00578    return ASTERISK_GPL_KEY;
00579 }

Generated on Sat Sep 16 07:28:05 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7