Sun Oct 16 2011 08:41:27

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