Fri Jul 15 2011 11:56:56

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  * Copyright (C) 2005 - 2008, Digium, Inc.
00006  *
00007  * A license has been granted to Digium (via disclaimer) for the use of
00008  * this code.
00009  *
00010  * See http://www.asterisk.org for more information about
00011  * the Asterisk project. Please do not directly contact
00012  * any of the maintainers of this project for assistance;
00013  * the project provides a web site, mailing lists and IRC
00014  * channels for your use.
00015  *
00016  * This program is free software, distributed under the terms of
00017  * the GNU General Public License Version 2. See the LICENSE file
00018  * at the top of the source tree.
00019  */
00020 
00021 /*! \file
00022  *
00023  * \brief ChanSpy: Listen in on any channel.
00024  *
00025  * \author Anthony Minessale II <anthmct@yahoo.com>
00026  * \author Joshua Colp <jcolp@digium.com>
00027  * \author Russell Bryant <russell@digium.com>
00028  *
00029  * \ingroup applications
00030  */
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 320506 $")
00035 
00036 #include <ctype.h>
00037 #include <errno.h>
00038 
00039 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
00040 #include "asterisk/file.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/audiohook.h"
00043 #include "asterisk/features.h"
00044 #include "asterisk/app.h"
00045 #include "asterisk/utils.h"
00046 #include "asterisk/say.h"
00047 #include "asterisk/pbx.h"
00048 #include "asterisk/translate.h"
00049 #include "asterisk/manager.h"
00050 #include "asterisk/module.h"
00051 #include "asterisk/lock.h"
00052 #include "asterisk/options.h"
00053 
00054 #define AST_NAME_STRLEN 256
00055 #define NUM_SPYGROUPS 128
00056 
00057 /*** DOCUMENTATION
00058    <application name="ChanSpy" language="en_US">
00059       <synopsis>
00060          Listen to a channel, and optionally whisper into it.
00061       </synopsis>
00062       <syntax>
00063          <parameter name="chanprefix" />
00064          <parameter name="options">
00065             <optionlist>
00066                <option name="b">
00067                   <para>Only spy on channels involved in a bridged call.</para>
00068                </option>
00069                <option name="B">
00070                   <para>Instead of whispering on a single channel barge in on both
00071                   channels involved in the call.</para>
00072                </option>
00073                <option name="d">
00074                   <para>Override the typical numeric DTMF functionality and instead
00075                   use DTMF to switch between spy modes.</para>
00076                   <enumlist>
00077                      <enum name="4">
00078                         <para>spy mode</para>
00079                      </enum>
00080                      <enum name="5">
00081                         <para>whisper mode</para>
00082                      </enum>
00083                      <enum name="6">
00084                         <para>barge mode</para>
00085                      </enum>
00086                   </enumlist>
00087                </option>
00088                <option name="g">
00089                   <argument name="grp" required="true">
00090                      <para>Only spy on channels in which one or more of the groups
00091                      listed in <replaceable>grp</replaceable> matches one or more groups from the
00092                      <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
00093                   </argument>
00094                   <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain 
00095                   either a single group or a colon-delimited list of groups, such
00096                   as <literal>sales:support:accounting</literal>.</para></note>
00097                </option>
00098                <option name="n" argsep="@">
00099                   <para>Say the name of the person being spied on if that person has recorded
00100                   his/her name. If a context is specified, then that voicemail context will
00101                   be searched when retrieving the name, otherwise the <literal>default</literal> context
00102                   be used when searching for the name (i.e. if SIP/1000 is the channel being
00103                   spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
00104                   for the name).</para>
00105                   <argument name="mailbox" />
00106                   <argument name="context" />
00107                </option>
00108                <option name="q">
00109                   <para>Don't play a beep when beginning to spy on a channel, or speak the
00110                   selected channel name.</para>
00111                </option>
00112                <option name="r">
00113                   <para>Record the session to the monitor spool directory. An optional base for the filename 
00114                   may be specified. The default is <literal>chanspy</literal>.</para>
00115                   <argument name="basename" />
00116                </option>
00117                <option name="s">
00118                   <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
00119                   speaking the selected channel name.</para>
00120                </option>
00121                <option name="v">
00122                   <argument name="value" />
00123                   <para>Adjust the initial volume in the range from <literal>-4</literal> 
00124                   to <literal>4</literal>. A negative value refers to a quieter setting.</para>
00125                </option>
00126                <option name="w">
00127                   <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
00128                   the spied-on channel.</para>
00129                </option>
00130                <option name="W">
00131                   <para>Enable <literal>private whisper</literal> mode, so the spying channel can
00132                   talk to the spied-on channel but cannot listen to that channel.</para>
00133                </option>
00134                <option name="o">
00135                   <para>Only listen to audio coming from this channel.</para>
00136                </option>
00137                <option name="X">
00138                   <para>Allow the user to exit ChanSpy to a valid single digit
00139                   numeric extension in the current context or the context
00140                   specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
00141                   name of the last channel that was spied on will be stored
00142                   in the <variable>SPY_CHANNEL</variable> variable.</para>
00143                </option>
00144                <option name="e">
00145                   <argument name="ext" required="true" />
00146                   <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
00147                   only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited 
00148                   list.</para>
00149                </option>
00150             </optionlist>     
00151          </parameter>
00152       </syntax>
00153       <description>
00154          <para>This application is used to listen to the audio from an Asterisk channel. This includes the audio 
00155          coming in and out of the channel being spied on. If the <literal>chanprefix</literal> parameter is specified,
00156          only channels beginning with this string will be spied upon.</para>
00157          <para>While spying, the following actions may be performed:</para>
00158          <para> - Dialing <literal>#</literal> cycles the volume level.</para>
00159          <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
00160          <para> - Dialing a series of digits followed by <literal>#</literal> builds a channel name to append
00161          to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing the digits '1234#' 
00162          while spying will begin spying on the channel 'Agent/1234'. Note that this feature will be overridden if the 'd' option
00163          is used</para>
00164          <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
00165          single digit extension exists in the correct context ChanSpy will exit to it.
00166          This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
00167       </description>
00168       <see-also>
00169          <ref type="application">ExtenSpy</ref>
00170       </see-also>
00171    </application>
00172    <application name="ExtenSpy" language="en_US">
00173       <synopsis>
00174          Listen to a channel, and optionally whisper into it.
00175       </synopsis>
00176       <syntax>
00177          <parameter name="exten" required="true" argsep="@">
00178             <argument name="exten" required="true">
00179                <para>Specify extension.</para>
00180             </argument>
00181             <argument name="context">
00182                <para>Optionally specify a context, defaults to <literal>default</literal>.</para>
00183             </argument>
00184          </parameter>
00185          <parameter name="options">
00186             <optionlist>
00187                <option name="b">
00188                   <para>Only spy on channels involved in a bridged call.</para>
00189                </option>
00190                <option name="B">
00191                   <para>Instead of whispering on a single channel barge in on both
00192                   channels involved in the call.</para>
00193                </option>
00194                <option name="d">
00195                   <para>Override the typical numeric DTMF functionality and instead
00196                   use DTMF to switch between spy modes.</para>
00197                   <enumlist>
00198                      <enum name="4">
00199                         <para>spy mode</para>
00200                      </enum>
00201                      <enum name="5">
00202                         <para>whisper mode</para>
00203                      </enum>
00204                      <enum name="6">
00205                         <para>barge mode</para>
00206                      </enum>
00207                   </enumlist>
00208                </option>
00209                <option name="g">
00210                   <argument name="grp" required="true">
00211                      <para>Only spy on channels in which one or more of the groups
00212                      listed in <replaceable>grp</replaceable> matches one or more groups from the
00213                      <variable>SPYGROUP</variable> variable set on the channel to be spied upon.</para>
00214                   </argument>
00215                   <note><para>both <replaceable>grp</replaceable> and <variable>SPYGROUP</variable> can contain 
00216                   either a single group or a colon-delimited list of groups, such
00217                   as <literal>sales:support:accounting</literal>.</para></note>
00218                </option>
00219                <option name="n" argsep="@">
00220                   <para>Say the name of the person being spied on if that person has recorded
00221                   his/her name. If a context is specified, then that voicemail context will
00222                   be searched when retrieving the name, otherwise the <literal>default</literal> context
00223                   be used when searching for the name (i.e. if SIP/1000 is the channel being
00224                   spied on and no mailbox is specified, then <literal>1000</literal> will be used when searching
00225                   for the name).</para>
00226                   <argument name="mailbox" />
00227                   <argument name="context" />
00228                </option>
00229                <option name="q">
00230                   <para>Don't play a beep when beginning to spy on a channel, or speak the
00231                   selected channel name.</para>
00232                </option>
00233                <option name="r">
00234                   <para>Record the session to the monitor spool directory. An optional base for the filename 
00235                   may be specified. The default is <literal>chanspy</literal>.</para>
00236                   <argument name="basename" />
00237                </option>
00238                <option name="s">
00239                   <para>Skip the playback of the channel type (i.e. SIP, IAX, etc) when
00240                   speaking the selected channel name.</para>
00241                </option>
00242                <option name="v">
00243                   <argument name="value" />
00244                   <para>Adjust the initial volume in the range from <literal>-4</literal> 
00245                   to <literal>4</literal>. A negative value refers to a quieter setting.</para>
00246                </option>
00247                <option name="w">
00248                   <para>Enable <literal>whisper</literal> mode, so the spying channel can talk to
00249                   the spied-on channel.</para>
00250                </option>
00251                <option name="W">
00252                   <para>Enable <literal>private whisper</literal> mode, so the spying channel can
00253                   talk to the spied-on channel but cannot listen to that channel.</para>
00254                </option>
00255                <option name="o">
00256                   <para>Only listen to audio coming from this channel.</para>
00257                </option>
00258                <option name="X">
00259                   <para>Allow the user to exit ChanSpy to a valid single digit
00260                   numeric extension in the current context or the context
00261                   specified by the <variable>SPY_EXIT_CONTEXT</variable> channel variable. The
00262                   name of the last channel that was spied on will be stored
00263                   in the <variable>SPY_CHANNEL</variable> variable.</para>
00264                </option>
00265                <option name="e">
00266                   <argument name="ext" required="true" />
00267                   <para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
00268                   only monitor extensions whose name is in the <replaceable>ext</replaceable> : delimited 
00269                   list.</para>
00270                </option>
00271             </optionlist>  
00272          </parameter>
00273       </syntax>
00274       <description>
00275          <para>This application is used to listen to the audio from an Asterisk channel. This includes 
00276          the audio coming in and out of the channel being spied on. Only channels created by outgoing calls for the
00277          specified extension will be selected for spying. If the optional context is not supplied, 
00278          the current channel's context will be used.</para>
00279          <para>While spying, the following actions may be performed:</para>
00280          <para> - Dialing <literal>#</literal> cycles the volume level.</para>
00281                         <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
00282          <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
00283          single digit extension exists in the correct context ChanSpy will exit to it.
00284          This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
00285       </description>
00286       <see-also>
00287          <ref type="application">ChanSpy</ref>
00288       </see-also>
00289    </application>
00290 
00291  ***/
00292 static const char *app_chan = "ChanSpy";
00293 
00294 static const char *app_ext = "ExtenSpy";
00295 
00296 enum {
00297    OPTION_QUIET             = (1 << 0),    /* Quiet, no announcement */
00298    OPTION_BRIDGED           = (1 << 1),    /* Only look at bridged calls */
00299    OPTION_VOLUME            = (1 << 2),    /* Specify initial volume */
00300    OPTION_GROUP             = (1 << 3),    /* Only look at channels in group */
00301    OPTION_RECORD            = (1 << 4),
00302    OPTION_WHISPER           = (1 << 5),
00303    OPTION_PRIVATE           = (1 << 6),    /* Private Whisper mode */
00304    OPTION_READONLY          = (1 << 7),    /* Don't mix the two channels */
00305    OPTION_EXIT              = (1 << 8),    /* Exit to a valid single digit extension */
00306    OPTION_ENFORCED          = (1 << 9),    /* Enforced mode */
00307    OPTION_NOTECH            = (1 << 10),   /* Skip technology name playback */
00308    OPTION_BARGE             = (1 << 11),   /* Barge mode (whisper to both channels) */
00309    OPTION_NAME              = (1 << 12),   /* Say the name of the person on whom we will spy */
00310    OPTION_DTMF_SWITCH_MODES = (1 << 13),   /*Allow numeric DTMF to switch between chanspy modes */
00311 } chanspy_opt_flags;
00312 
00313 enum {
00314    OPT_ARG_VOLUME = 0,
00315    OPT_ARG_GROUP,
00316    OPT_ARG_RECORD,
00317    OPT_ARG_ENFORCED,
00318    OPT_ARG_NAME,
00319    OPT_ARG_ARRAY_SIZE,
00320 } chanspy_opt_args;
00321 
00322 AST_APP_OPTIONS(spy_opts, {
00323    AST_APP_OPTION('q', OPTION_QUIET),
00324    AST_APP_OPTION('b', OPTION_BRIDGED),
00325    AST_APP_OPTION('B', OPTION_BARGE),
00326    AST_APP_OPTION('w', OPTION_WHISPER),
00327    AST_APP_OPTION('W', OPTION_PRIVATE),
00328    AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
00329    AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
00330    AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
00331    AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
00332    AST_APP_OPTION('o', OPTION_READONLY),
00333    AST_APP_OPTION('X', OPTION_EXIT),
00334    AST_APP_OPTION('s', OPTION_NOTECH),
00335    AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
00336    AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
00337 });
00338 
00339 static int next_unique_id_to_use = 0;
00340 
00341 struct chanspy_translation_helper {
00342    /* spy data */
00343    struct ast_audiohook spy_audiohook;
00344    struct ast_audiohook whisper_audiohook;
00345    struct ast_audiohook bridge_whisper_audiohook;
00346    int fd;
00347    int volfactor;
00348    struct ast_flags flags;
00349 };
00350 
00351 static void *spy_alloc(struct ast_channel *chan, void *data)
00352 {
00353    /* just store the data pointer in the channel structure */
00354    return data;
00355 }
00356 
00357 static void spy_release(struct ast_channel *chan, void *data)
00358 {
00359    /* nothing to do */
00360 }
00361 
00362 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
00363 {
00364    struct chanspy_translation_helper *csth = data;
00365    struct ast_frame *f, *cur;
00366 
00367    ast_audiohook_lock(&csth->spy_audiohook);
00368    if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00369       /* Channel is already gone more than likely */
00370       ast_audiohook_unlock(&csth->spy_audiohook);
00371       return -1;
00372    }
00373 
00374    if (ast_test_flag(&csth->flags, OPTION_READONLY)) {
00375       /* Option 'o' was set, so don't mix channel audio */
00376       f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, AST_FORMAT_SLINEAR);
00377    } else {
00378       f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
00379    }
00380 
00381    ast_audiohook_unlock(&csth->spy_audiohook);
00382 
00383    if (!f)
00384       return 0;
00385 
00386    for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00387       if (ast_write(chan, cur)) {
00388          ast_frfree(f);
00389          return -1;
00390       }
00391 
00392       if (csth->fd) {
00393          if (write(csth->fd, cur->data.ptr, cur->datalen) < 0) {
00394             ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00395          }
00396       }
00397    }
00398 
00399    ast_frfree(f);
00400 
00401    return 0;
00402 }
00403 
00404 static struct ast_generator spygen = {
00405    .alloc = spy_alloc,
00406    .release = spy_release,
00407    .generate = spy_generate,
00408 };
00409 
00410 static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
00411 {
00412    int res = 0;
00413    struct ast_channel *peer = NULL;
00414 
00415    ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
00416 
00417    ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
00418    res = ast_audiohook_attach(chan, audiohook);
00419 
00420    if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { 
00421       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00422    }
00423    return res;
00424 }
00425 
00426 struct chanspy_ds {
00427    struct ast_channel *chan;
00428    char unique_id[20];
00429    ast_mutex_t lock;
00430 };
00431 
00432 static void change_spy_mode(const char digit, struct ast_flags *flags)
00433 {
00434    if (digit == '4') {
00435       ast_clear_flag(flags, OPTION_WHISPER);
00436       ast_clear_flag(flags, OPTION_BARGE);
00437    } else if (digit == '5') {
00438       ast_clear_flag(flags, OPTION_BARGE);
00439       ast_set_flag(flags, OPTION_WHISPER);
00440    } else if (digit == '6') {
00441       ast_clear_flag(flags, OPTION_WHISPER);
00442       ast_set_flag(flags, OPTION_BARGE);
00443    }
00444 }
00445 
00446 static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, 
00447    int *volfactor, int fd, struct ast_flags *flags, char *exitcontext) 
00448 {
00449    struct chanspy_translation_helper csth;
00450    int running = 0, res, x = 0;
00451    char inp[24] = {0};
00452    char *name;
00453    struct ast_frame *f;
00454    struct ast_silence_generator *silgen = NULL;
00455    struct ast_channel *spyee = NULL, *spyee_bridge = NULL;
00456    const char *spyer_name;
00457 
00458    ast_channel_lock(chan);
00459    spyer_name = ast_strdupa(chan->name);
00460    ast_channel_unlock(chan);
00461 
00462    ast_mutex_lock(&spyee_chanspy_ds->lock);
00463    while ((spyee = spyee_chanspy_ds->chan) && ast_channel_trylock(spyee)) {
00464       /* avoid a deadlock here, just in case spyee is masqueraded and
00465        * chanspy_ds_chan_fixup() is called with the channel locked */
00466       DEADLOCK_AVOIDANCE(&spyee_chanspy_ds->lock);
00467    }
00468    ast_mutex_unlock(&spyee_chanspy_ds->lock);
00469 
00470    if (!spyee) {
00471       return 0;
00472    }
00473 
00474    /* We now hold the channel lock on spyee */
00475 
00476    if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
00477       ast_channel_unlock(spyee);
00478       return 0;
00479    }
00480 
00481    name = ast_strdupa(spyee->name);
00482 
00483    ast_verb(2, "Spying on channel %s\n", name);
00484    manager_event(EVENT_FLAG_CALL, "ChanSpyStart",
00485          "SpyerChannel: %s\r\n"
00486          "SpyeeChannel: %s\r\n",
00487          spyer_name, name);
00488 
00489    memset(&csth, 0, sizeof(csth));
00490    ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL);
00491 
00492    ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
00493 
00494    if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
00495       ast_audiohook_destroy(&csth.spy_audiohook);
00496       ast_channel_unlock(spyee);
00497       return 0;
00498    }
00499 
00500    ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
00501    ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
00502    if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) {
00503       ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name);
00504    }
00505    if ((spyee_bridge = ast_bridged_channel(spyee))) {
00506       ast_channel_lock(spyee_bridge);
00507       if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) {
00508          ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name);
00509       }
00510       ast_channel_unlock(spyee_bridge);
00511    }
00512    ast_channel_unlock(spyee);
00513    spyee = NULL;
00514 
00515    ast_channel_lock(chan);
00516    ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
00517    ast_channel_unlock(chan);
00518 
00519    csth.volfactor = *volfactor;
00520 
00521    if (csth.volfactor) {
00522       csth.spy_audiohook.options.read_volume = csth.volfactor;
00523       csth.spy_audiohook.options.write_volume = csth.volfactor;
00524    }
00525 
00526    csth.fd = fd;
00527 
00528    if (ast_test_flag(flags, OPTION_PRIVATE))
00529       silgen = ast_channel_start_silence_generator(chan);
00530    else
00531       ast_activate_generator(chan, &spygen, &csth);
00532 
00533    /* We can no longer rely on 'spyee' being an actual channel;
00534       it can be hung up and freed out from under us. However, the
00535       channel destructor will put NULL into our csth.spy.chan
00536       field when that happens, so that is our signal that the spyee
00537       channel has gone away.
00538    */
00539 
00540    /* Note: it is very important that the ast_waitfor() be the first
00541       condition in this expression, so that if we wait for some period
00542       of time before receiving a frame from our spying channel, we check
00543       for hangup on the spied-on channel _after_ knowing that a frame
00544       has arrived, since the spied-on channel could have gone away while
00545       we were waiting
00546    */
00547    while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00548       if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
00549          running = -1;
00550          break;
00551       }
00552 
00553       if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
00554          ast_audiohook_lock(&csth.whisper_audiohook);
00555          ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00556          ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00557          ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00558          ast_audiohook_unlock(&csth.whisper_audiohook);
00559          ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00560          ast_frfree(f);
00561          continue;
00562       } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
00563          ast_audiohook_lock(&csth.whisper_audiohook);
00564          ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00565          ast_audiohook_unlock(&csth.whisper_audiohook);
00566          ast_frfree(f);
00567          continue;
00568       }
00569       
00570       res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
00571       ast_frfree(f);
00572       if (!res)
00573          continue;
00574 
00575       if (x == sizeof(inp))
00576          x = 0;
00577 
00578       if (res < 0) {
00579          running = -1;
00580          break;
00581       }
00582 
00583       if (ast_test_flag(flags, OPTION_EXIT)) {
00584          char tmp[2];
00585          tmp[0] = res;
00586          tmp[1] = '\0';
00587          if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
00588             ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
00589             pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
00590             running = -2;
00591             break;
00592          } else {
00593             ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00594          }
00595       } else if (res >= '0' && res <= '9') {
00596          if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
00597             change_spy_mode(res, flags);
00598          } else {
00599             inp[x++] = res;
00600          }
00601       }
00602 
00603       if (res == '*') {
00604          running = 0;
00605          break;
00606       } else if (res == '#') {
00607          if (!ast_strlen_zero(inp)) {
00608             running = atoi(inp);
00609             break;
00610          }
00611 
00612          (*volfactor)++;
00613          if (*volfactor > 4)
00614             *volfactor = -4;
00615          ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
00616 
00617          csth.volfactor = *volfactor;
00618          csth.spy_audiohook.options.read_volume = csth.volfactor;
00619          csth.spy_audiohook.options.write_volume = csth.volfactor;
00620       }
00621    }
00622 
00623    if (ast_test_flag(flags, OPTION_PRIVATE))
00624       ast_channel_stop_silence_generator(chan, silgen);
00625    else
00626       ast_deactivate_generator(chan);
00627 
00628    ast_channel_lock(chan);
00629    ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00630    ast_channel_unlock(chan);
00631 
00632    ast_audiohook_lock(&csth.whisper_audiohook);
00633    ast_audiohook_detach(&csth.whisper_audiohook);
00634    ast_audiohook_unlock(&csth.whisper_audiohook);
00635    ast_audiohook_destroy(&csth.whisper_audiohook);
00636    
00637    ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00638    ast_audiohook_detach(&csth.bridge_whisper_audiohook);
00639    ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00640    ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
00641 
00642    ast_audiohook_lock(&csth.spy_audiohook);
00643    ast_audiohook_detach(&csth.spy_audiohook);
00644    ast_audiohook_unlock(&csth.spy_audiohook);
00645    ast_audiohook_destroy(&csth.spy_audiohook);
00646    
00647    ast_verb(2, "Done Spying on channel %s\n", name);
00648    manager_event(EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name);
00649 
00650    return running;
00651 }
00652 
00653 /*!
00654  * \note This relies on the embedded lock to be recursive, as it may be called
00655  * due to a call to chanspy_ds_free with the lock held there.
00656  */
00657 static void chanspy_ds_destroy(void *data)
00658 {
00659    struct chanspy_ds *chanspy_ds = data;
00660 
00661    /* Setting chan to be NULL is an atomic operation, but we don't want this
00662     * value to change while this lock is held.  The lock is held elsewhere
00663     * while it performs non-atomic operations with this channel pointer */
00664 
00665    ast_mutex_lock(&chanspy_ds->lock);
00666    chanspy_ds->chan = NULL;
00667    ast_mutex_unlock(&chanspy_ds->lock);
00668 }
00669 
00670 static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00671 {
00672    struct chanspy_ds *chanspy_ds = data;
00673    
00674    ast_mutex_lock(&chanspy_ds->lock);
00675    chanspy_ds->chan = new_chan;
00676    ast_mutex_unlock(&chanspy_ds->lock);
00677 }
00678 
00679 static const struct ast_datastore_info chanspy_ds_info = {
00680    .type = "chanspy",
00681    .destroy = chanspy_ds_destroy,
00682    .chan_fixup = chanspy_ds_chan_fixup,
00683 };
00684 
00685 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
00686 {
00687    struct ast_channel *chan;
00688 
00689    if (!chanspy_ds) {
00690       return NULL;
00691    }
00692 
00693    ast_mutex_lock(&chanspy_ds->lock);
00694    while ((chan = chanspy_ds->chan)) {
00695       struct ast_datastore *datastore;
00696 
00697       if (ast_channel_trylock(chan)) {
00698          DEADLOCK_AVOIDANCE(&chanspy_ds->lock);
00699          continue;
00700       }
00701       if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
00702          ast_channel_datastore_remove(chan, datastore);
00703          /* chanspy_ds->chan is NULL after this call */
00704          chanspy_ds_destroy(datastore->data);
00705          datastore->data = NULL;
00706          ast_datastore_free(datastore);
00707       }
00708       ast_channel_unlock(chan);
00709       break;
00710    }
00711    ast_mutex_unlock(&chanspy_ds->lock);
00712 
00713    return NULL;
00714 }
00715 
00716 /*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
00717 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
00718 {
00719    struct ast_datastore *datastore = NULL;
00720 
00721    ast_mutex_lock(&chanspy_ds->lock);
00722 
00723    if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
00724       ast_mutex_unlock(&chanspy_ds->lock);
00725       chanspy_ds = chanspy_ds_free(chanspy_ds);
00726       ast_channel_unlock(chan);
00727       return NULL;
00728    }
00729    
00730    chanspy_ds->chan = chan;
00731    datastore->data = chanspy_ds;
00732    ast_channel_datastore_add(chan, datastore);
00733 
00734    return chanspy_ds;
00735 }
00736 
00737 static struct chanspy_ds *next_channel(struct ast_channel *chan,
00738    const struct ast_channel *last, const char *spec,
00739    const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
00740 {
00741    struct ast_channel *next;
00742    const size_t pseudo_len = strlen("DAHDI/pseudo");
00743 
00744 redo:
00745    if (!ast_strlen_zero(spec))
00746       next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
00747    else if (!ast_strlen_zero(exten))
00748       next = ast_walk_channel_by_exten_locked(last, exten, context);
00749    else
00750       next = ast_channel_walk_locked(last);
00751 
00752    if (!next)
00753       return NULL;
00754 
00755    if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
00756       last = next;
00757       ast_channel_unlock(next);
00758       goto redo;
00759    } else if (next == chan) {
00760       last = next;
00761       ast_channel_unlock(next);
00762       goto redo;
00763    }
00764 
00765    return setup_chanspy_ds(next, chanspy_ds);
00766 }
00767 
00768 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
00769    int volfactor, const int fd, const char *mygroup, const char *myenforced,
00770    const char *spec, const char *exten, const char *context, const char *mailbox,
00771    const char *name_context)
00772 {
00773    char nameprefix[AST_NAME_STRLEN];
00774    char peer_name[AST_NAME_STRLEN + 5];
00775    char exitcontext[AST_MAX_CONTEXT] = "";
00776    signed char zero_volume = 0;
00777    int waitms;
00778    int res;
00779    char *ptr;
00780    int num;
00781    int num_spyed_upon = 1;
00782    struct chanspy_ds chanspy_ds = { 0, };
00783 
00784    if (ast_test_flag(flags, OPTION_EXIT)) {
00785       const char *c;
00786       ast_channel_lock(chan);
00787       if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
00788          ast_copy_string(exitcontext, c, sizeof(exitcontext));
00789       } else if (!ast_strlen_zero(chan->macrocontext)) {
00790          ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
00791       } else {
00792          ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
00793       }
00794       ast_channel_unlock(chan);
00795    }
00796 
00797    ast_mutex_init(&chanspy_ds.lock);
00798 
00799    snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
00800 
00801    if (chan->_state != AST_STATE_UP)
00802       ast_answer(chan);
00803 
00804    ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
00805 
00806    waitms = 100;
00807 
00808    for (;;) {
00809       struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
00810       struct ast_channel *prev = NULL, *peer = NULL;
00811 
00812       if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
00813          res = ast_streamfile(chan, "beep", chan->language);
00814          if (!res)
00815             res = ast_waitstream(chan, "");
00816          else if (res < 0) {
00817             ast_clear_flag(chan, AST_FLAG_SPYING);
00818             break;
00819          }
00820          if (!ast_strlen_zero(exitcontext)) {
00821             char tmp[2];
00822             tmp[0] = res;
00823             tmp[1] = '\0';
00824             if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00825                goto exit;
00826             else
00827                ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00828          }
00829       }
00830 
00831       res = ast_waitfordigit(chan, waitms);
00832       if (res < 0) {
00833          ast_clear_flag(chan, AST_FLAG_SPYING);
00834          break;
00835       }
00836       if (!ast_strlen_zero(exitcontext)) {
00837          char tmp[2];
00838          tmp[0] = res;
00839          tmp[1] = '\0';
00840          if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00841             goto exit;
00842          else
00843             ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00844       }
00845 
00846       /* reset for the next loop around, unless overridden later */
00847       waitms = 100;
00848       num_spyed_upon = 0;
00849 
00850       for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
00851            peer_chanspy_ds;
00852           chanspy_ds_free(peer_chanspy_ds), prev = peer,
00853            peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 
00854             next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
00855          int igrp = !mygroup;
00856          int ienf = !myenforced;
00857          char *s;
00858 
00859          peer = peer_chanspy_ds->chan;
00860 
00861          ast_mutex_unlock(&peer_chanspy_ds->lock);
00862 
00863          if (peer == prev) {
00864             ast_channel_unlock(peer);
00865             chanspy_ds_free(peer_chanspy_ds);
00866             break;
00867          }
00868 
00869          if (ast_check_hangup(chan)) {
00870             ast_channel_unlock(peer);
00871             chanspy_ds_free(peer_chanspy_ds);
00872             break;
00873          }
00874 
00875          if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
00876             ast_channel_unlock(peer);
00877             continue;
00878          }
00879 
00880          if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
00881             ast_channel_unlock(peer);
00882             continue;
00883          }
00884 
00885          if (mygroup) {
00886             int num_groups = 0;
00887             int num_mygroups = 0;
00888             char dup_group[512];
00889             char dup_mygroup[512];
00890             char *groups[NUM_SPYGROUPS];
00891             char *mygroups[NUM_SPYGROUPS];
00892             const char *group;
00893             int x;
00894             int y;
00895             ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
00896             num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
00897                ARRAY_LEN(mygroups));
00898 
00899             if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
00900                ast_copy_string(dup_group, group, sizeof(dup_group));
00901                num_groups = ast_app_separate_args(dup_group, ':', groups,
00902                   ARRAY_LEN(groups));
00903             }
00904 
00905             for (y = 0; y < num_mygroups; y++) {
00906                for (x = 0; x < num_groups; x++) {
00907                   if (!strcmp(mygroups[y], groups[x])) {
00908                      igrp = 1;
00909                      break;
00910                   }
00911                }
00912             }
00913          }
00914 
00915          if (!igrp) {
00916             ast_channel_unlock(peer);
00917             continue;
00918          }
00919 
00920          if (myenforced) {
00921             char ext[AST_CHANNEL_NAME + 3];
00922             char buffer[512];
00923             char *end;
00924 
00925             snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
00926 
00927             ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1);
00928             if ((end = strchr(ext, '-'))) {
00929                *end++ = ':';
00930                *end = '\0';
00931             }
00932 
00933             ext[0] = ':';
00934 
00935             if (strcasestr(buffer, ext)) {
00936                ienf = 1;
00937             }
00938          }
00939 
00940          if (!ienf) {
00941             ast_channel_unlock(peer);
00942             continue;
00943          }
00944 
00945          strcpy(peer_name, "spy-");
00946          strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
00947          ptr = strchr(peer_name, '/');
00948          *ptr++ = '\0';
00949          ptr = strsep(&ptr, "-");
00950 
00951          for (s = peer_name; s < ptr; s++)
00952             *s = tolower(*s);
00953          /* We have to unlock the peer channel here to avoid a deadlock.
00954           * So, when we need to dereference it again, we have to lock the 
00955           * datastore and get the pointer from there to see if the channel 
00956           * is still valid. */
00957          ast_channel_unlock(peer);
00958 
00959          if (!ast_test_flag(flags, OPTION_QUIET)) {
00960             if (ast_test_flag(flags, OPTION_NAME)) {
00961                const char *local_context = S_OR(name_context, "default");
00962                const char *local_mailbox = S_OR(mailbox, ptr);
00963                res = ast_app_sayname(chan, local_mailbox, local_context);
00964             }
00965             if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
00966                if (!ast_test_flag(flags, OPTION_NOTECH)) {
00967                   if (ast_fileexists(peer_name, NULL, NULL) > 0) {
00968                      res = ast_streamfile(chan, peer_name, chan->language);
00969                      if (!res) {
00970                         res = ast_waitstream(chan, "");
00971                      }
00972                      if (res) {
00973                         chanspy_ds_free(peer_chanspy_ds);
00974                         break;
00975                      }
00976                   } else {
00977                      res = ast_say_character_str(chan, peer_name, "", chan->language);
00978                   }
00979                }
00980                if ((num = atoi(ptr)))
00981                   ast_say_digits(chan, atoi(ptr), "", chan->language);
00982             }
00983          }
00984 
00985          res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
00986          num_spyed_upon++; 
00987 
00988          if (res == -1) {
00989             chanspy_ds_free(peer_chanspy_ds);
00990             goto exit;
00991          } else if (res == -2) {
00992             res = 0;
00993             chanspy_ds_free(peer_chanspy_ds);
00994             goto exit;
00995          } else if (res > 1 && spec) {
00996             struct ast_channel *next;
00997 
00998             snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
00999 
01000             if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
01001                peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
01002                next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
01003             } else {
01004                /* stay on this channel, if it is still valid */
01005 
01006                ast_mutex_lock(&peer_chanspy_ds->lock);
01007                if (peer_chanspy_ds->chan) {
01008                   ast_channel_lock(peer_chanspy_ds->chan);
01009                   next_chanspy_ds = peer_chanspy_ds;
01010                   peer_chanspy_ds = NULL;
01011                } else {
01012                   /* the channel is gone */
01013                   ast_mutex_unlock(&peer_chanspy_ds->lock);
01014                   next_chanspy_ds = NULL;
01015                }
01016             }
01017 
01018             peer = NULL;
01019          }
01020       }
01021       if (res == -1 || ast_check_hangup(chan))
01022          break;
01023    }
01024 exit:
01025 
01026    ast_clear_flag(chan, AST_FLAG_SPYING);
01027 
01028    ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
01029 
01030    ast_mutex_lock(&chanspy_ds.lock);
01031    ast_mutex_unlock(&chanspy_ds.lock);
01032    ast_mutex_destroy(&chanspy_ds.lock);
01033 
01034    return res;
01035 }
01036 
01037 static int chanspy_exec(struct ast_channel *chan, void *data)
01038 {
01039    char *myenforced = NULL;
01040    char *mygroup = NULL;
01041    char *recbase = NULL;
01042    int fd = 0;
01043    struct ast_flags flags;
01044    int oldwf = 0;
01045    int volfactor = 0;
01046    int res;
01047    char *mailbox = NULL;
01048    char *name_context = NULL;
01049    AST_DECLARE_APP_ARGS(args,
01050       AST_APP_ARG(spec);
01051       AST_APP_ARG(options);
01052    );
01053    char *opts[OPT_ARG_ARRAY_SIZE];
01054 
01055    data = ast_strdupa(data);
01056    AST_STANDARD_APP_ARGS(args, data);
01057 
01058    if (args.spec && !strcmp(args.spec, "all"))
01059       args.spec = NULL;
01060 
01061    if (args.options) {
01062       ast_app_parse_options(spy_opts, &flags, opts, args.options);
01063       if (ast_test_flag(&flags, OPTION_GROUP))
01064          mygroup = opts[OPT_ARG_GROUP];
01065 
01066       if (ast_test_flag(&flags, OPTION_RECORD) &&
01067          !(recbase = opts[OPT_ARG_RECORD]))
01068          recbase = "chanspy";
01069 
01070       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
01071          int vol;
01072 
01073          if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
01074             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
01075          else
01076             volfactor = vol;
01077       }
01078 
01079       if (ast_test_flag(&flags, OPTION_PRIVATE))
01080          ast_set_flag(&flags, OPTION_WHISPER);
01081 
01082       if (ast_test_flag(&flags, OPTION_ENFORCED))
01083          myenforced = opts[OPT_ARG_ENFORCED];
01084       
01085       if (ast_test_flag(&flags, OPTION_NAME)) {
01086          if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
01087             char *delimiter;
01088             if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
01089                mailbox = opts[OPT_ARG_NAME];
01090                *delimiter++ = '\0';
01091                name_context = delimiter;
01092             } else {
01093                mailbox = opts[OPT_ARG_NAME];
01094             }
01095          }
01096       }
01097 
01098 
01099    } else
01100       ast_clear_flag(&flags, AST_FLAGS_ALL);
01101 
01102    oldwf = chan->writeformat;
01103    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01104       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01105       return -1;
01106    }
01107 
01108    if (recbase) {
01109       char filename[PATH_MAX];
01110 
01111       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
01112       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
01113          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
01114          fd = 0;
01115       }
01116    }
01117 
01118    res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
01119 
01120    if (fd)
01121       close(fd);
01122 
01123    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
01124       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01125 
01126    return res;
01127 }
01128 
01129 static int extenspy_exec(struct ast_channel *chan, void *data)
01130 {
01131    char *ptr, *exten = NULL;
01132    char *mygroup = NULL;
01133    char *recbase = NULL;
01134    int fd = 0;
01135    struct ast_flags flags;
01136    int oldwf = 0;
01137    int volfactor = 0;
01138    int res;
01139    char *mailbox = NULL;
01140    char *name_context = NULL;
01141    AST_DECLARE_APP_ARGS(args,
01142       AST_APP_ARG(context);
01143       AST_APP_ARG(options);
01144    );
01145 
01146    data = ast_strdupa(data);
01147 
01148    AST_STANDARD_APP_ARGS(args, data);
01149    if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
01150       exten = args.context;
01151       *ptr++ = '\0';
01152       args.context = ptr;
01153    }
01154 
01155    if (ast_strlen_zero(args.context))
01156       args.context = ast_strdupa(chan->context);
01157 
01158    if (args.options) {
01159       char *opts[OPT_ARG_ARRAY_SIZE];
01160 
01161       ast_app_parse_options(spy_opts, &flags, opts, args.options);
01162       if (ast_test_flag(&flags, OPTION_GROUP))
01163          mygroup = opts[OPT_ARG_GROUP];
01164 
01165       if (ast_test_flag(&flags, OPTION_RECORD) &&
01166          !(recbase = opts[OPT_ARG_RECORD]))
01167          recbase = "chanspy";
01168 
01169       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
01170          int vol;
01171 
01172          if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
01173             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
01174          else
01175             volfactor = vol;
01176       }
01177 
01178       if (ast_test_flag(&flags, OPTION_PRIVATE))
01179          ast_set_flag(&flags, OPTION_WHISPER);
01180 
01181       
01182       if (ast_test_flag(&flags, OPTION_NAME)) {
01183          if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
01184             char *delimiter;
01185             if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
01186                mailbox = opts[OPT_ARG_NAME];
01187                *delimiter++ = '\0';
01188                name_context = delimiter;
01189             } else {
01190                mailbox = opts[OPT_ARG_NAME];
01191             }
01192          }
01193       }
01194 
01195    } else
01196       ast_clear_flag(&flags, AST_FLAGS_ALL);
01197 
01198    oldwf = chan->writeformat;
01199    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01200       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01201       return -1;
01202    }
01203 
01204    if (recbase) {
01205       char filename[PATH_MAX];
01206 
01207       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
01208       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
01209          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
01210          fd = 0;
01211       }
01212    }
01213 
01214 
01215    res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
01216 
01217    if (fd)
01218       close(fd);
01219 
01220    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
01221       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01222 
01223    return res;
01224 }
01225 
01226 static int unload_module(void)
01227 {
01228    int res = 0;
01229 
01230    res |= ast_unregister_application(app_chan);
01231    res |= ast_unregister_application(app_ext);
01232 
01233    return res;
01234 }
01235 
01236 static int load_module(void)
01237 {
01238    int res = 0;
01239 
01240    res |= ast_register_application_xml(app_chan, chanspy_exec);
01241    res |= ast_register_application_xml(app_ext, extenspy_exec);
01242 
01243    return res;
01244 }
01245 
01246 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");