Sun Aug 6 15:02:23 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

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

Generated on Sun Aug 6 15:02:23 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.2