Mon Mar 12 2012 21:18:19

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: 349044 $")
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    /* This is the audiohook which gives us the audio off the channel we are
00547       spying on.
00548    */
00549    ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
00550 
00551    if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) {
00552       ast_audiohook_destroy(&csth.spy_audiohook);
00553       return 0;
00554    }
00555 
00556    if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
00557       /* This audiohook will let us inject audio from our channel into the
00558          channel we are currently spying on.
00559       */
00560       ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
00561 
00562       if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) {
00563          ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name);
00564       }
00565    }
00566 
00567    if (ast_test_flag(flags, OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
00568       /* And this hook lets us inject audio into the channel that the spied on
00569          channel is currently bridged with.
00570       */
00571       ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
00572 
00573       if ((spyee_bridge_autochan = ast_autochan_setup(ast_bridged_channel(spyee_autochan->chan)))) {
00574          ast_channel_lock(spyee_bridge_autochan->chan);
00575          if (start_spying(spyee_bridge_autochan, spyer_name, &csth.bridge_whisper_audiohook)) {
00576             ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", name);
00577          }
00578          ast_channel_unlock(spyee_bridge_autochan->chan);
00579       }
00580    }
00581 
00582    ast_channel_lock(chan);
00583    ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
00584    ast_channel_unlock(chan);
00585 
00586    csth.volfactor = *volfactor;
00587 
00588    if (csth.volfactor) {
00589       csth.spy_audiohook.options.read_volume = csth.volfactor;
00590       csth.spy_audiohook.options.write_volume = csth.volfactor;
00591    }
00592 
00593    csth.fd = fd;
00594 
00595    if (ast_test_flag(flags, OPTION_PRIVATE))
00596       silgen = ast_channel_start_silence_generator(chan);
00597    else
00598       ast_activate_generator(chan, &spygen, &csth);
00599 
00600    /* We can no longer rely on 'spyee' being an actual channel;
00601       it can be hung up and freed out from under us. However, the
00602       channel destructor will put NULL into our csth.spy.chan
00603       field when that happens, so that is our signal that the spyee
00604       channel has gone away.
00605    */
00606 
00607    /* Note: it is very important that the ast_waitfor() be the first
00608       condition in this expression, so that if we wait for some period
00609       of time before receiving a frame from our spying channel, we check
00610       for hangup on the spied-on channel _after_ knowing that a frame
00611       has arrived, since the spied-on channel could have gone away while
00612       we were waiting
00613    */
00614    while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00615       if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
00616          running = -1;
00617          break;
00618       }
00619 
00620       if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
00621          ast_audiohook_lock(&csth.whisper_audiohook);
00622          ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00623          ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00624          ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00625          ast_audiohook_unlock(&csth.whisper_audiohook);
00626          ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00627          ast_frfree(f);
00628          continue;
00629       } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
00630          ast_audiohook_lock(&csth.whisper_audiohook);
00631          ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00632          ast_audiohook_unlock(&csth.whisper_audiohook);
00633          ast_frfree(f);
00634          continue;
00635       }
00636       
00637       res = (f->frametype == AST_FRAME_DTMF) ? f->subclass.integer : 0;
00638       ast_frfree(f);
00639       if (!res)
00640          continue;
00641 
00642       if (x == sizeof(inp))
00643          x = 0;
00644 
00645       if (res < 0) {
00646          running = -1;
00647          break;
00648       }
00649 
00650       if (ast_test_flag(flags, OPTION_EXIT)) {
00651          char tmp[2];
00652          tmp[0] = res;
00653          tmp[1] = '\0';
00654          if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
00655             ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
00656             pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
00657             running = -2;
00658             break;
00659          } else {
00660             ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00661          }
00662       } else if (res >= '0' && res <= '9') {
00663          if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
00664             change_spy_mode(res, flags);
00665          } else {
00666             inp[x++] = res;
00667          }
00668       }
00669 
00670       if (res == user_options->cycle) {
00671          running = 0;
00672          break;
00673       } else if (res == user_options->exit) {
00674          running = -2;
00675          break;
00676       } else if (res == user_options->volume) {
00677          if (!ast_strlen_zero(inp)) {
00678             running = atoi(inp);
00679             break;
00680          }
00681 
00682          (*volfactor)++;
00683          if (*volfactor > 4)
00684             *volfactor = -4;
00685          ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
00686 
00687          csth.volfactor = *volfactor;
00688          csth.spy_audiohook.options.read_volume = csth.volfactor;
00689          csth.spy_audiohook.options.write_volume = csth.volfactor;
00690       }
00691    }
00692 
00693    if (ast_test_flag(flags, OPTION_PRIVATE))
00694       ast_channel_stop_silence_generator(chan, silgen);
00695    else
00696       ast_deactivate_generator(chan);
00697 
00698    ast_channel_lock(chan);
00699    ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00700    ast_channel_unlock(chan);
00701 
00702    if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
00703       ast_audiohook_lock(&csth.whisper_audiohook);
00704       ast_audiohook_detach(&csth.whisper_audiohook);
00705       ast_audiohook_unlock(&csth.whisper_audiohook);
00706       ast_audiohook_destroy(&csth.whisper_audiohook);
00707    }
00708 
00709    if (ast_test_flag(flags, OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
00710       ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00711       ast_audiohook_detach(&csth.bridge_whisper_audiohook);
00712       ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00713       ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
00714    }
00715 
00716    ast_audiohook_lock(&csth.spy_audiohook);
00717    ast_audiohook_detach(&csth.spy_audiohook);
00718    ast_audiohook_unlock(&csth.spy_audiohook);
00719    ast_audiohook_destroy(&csth.spy_audiohook);
00720 
00721    if (spyee_bridge_autochan) {
00722       ast_autochan_destroy(spyee_bridge_autochan);
00723    }
00724 
00725    ast_verb(2, "Done Spying on channel %s\n", name);
00726    ast_manager_event(chan, EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name);
00727 
00728    return running;
00729 }
00730 
00731 static struct ast_autochan *next_channel(struct ast_channel_iterator *iter,
00732       struct ast_autochan *autochan, struct ast_channel *chan)
00733 {
00734    struct ast_channel *next;
00735    struct ast_autochan *autochan_store;
00736    const size_t pseudo_len = strlen("DAHDI/pseudo");
00737 
00738    if (!iter) {
00739       return NULL;
00740    }
00741 
00742 redo:
00743    if (!(next = ast_channel_iterator_next(iter))) {
00744       return NULL;
00745    }
00746 
00747    if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
00748       goto redo;
00749    } else if (next == chan) {
00750       goto redo;
00751    }
00752 
00753    autochan_store = ast_autochan_setup(next);
00754    ast_channel_unref(next);
00755 
00756    return autochan_store;
00757 }
00758 
00759 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
00760    int volfactor, const int fd, struct spy_dtmf_options *user_options,
00761    const char *mygroup, const char *myenforced, const char *spec, const char *exten,
00762    const char *context, const char *mailbox, const char *name_context)
00763 {
00764    char nameprefix[AST_NAME_STRLEN];
00765    char peer_name[AST_NAME_STRLEN + 5];
00766    char exitcontext[AST_MAX_CONTEXT] = "";
00767    signed char zero_volume = 0;
00768    int waitms;
00769    int res;
00770    char *ptr;
00771    int num;
00772    int num_spyed_upon = 1;
00773    struct ast_channel_iterator *iter = NULL;
00774 
00775    if (ast_test_flag(flags, OPTION_EXIT)) {
00776       const char *c;
00777       ast_channel_lock(chan);
00778       if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
00779          ast_copy_string(exitcontext, c, sizeof(exitcontext));
00780       } else if (!ast_strlen_zero(chan->macrocontext)) {
00781          ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
00782       } else {
00783          ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
00784       }
00785       ast_channel_unlock(chan);
00786    }
00787 
00788    if (chan->_state != AST_STATE_UP)
00789       ast_answer(chan);
00790 
00791    ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
00792 
00793    waitms = 100;
00794 
00795    for (;;) {
00796       struct ast_autochan *autochan = NULL, *next_autochan = NULL;
00797       struct ast_channel *prev = NULL;
00798 
00799       if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
00800          res = ast_streamfile(chan, "beep", chan->language);
00801          if (!res)
00802             res = ast_waitstream(chan, "");
00803          else if (res < 0) {
00804             ast_clear_flag(chan, AST_FLAG_SPYING);
00805             break;
00806          }
00807          if (!ast_strlen_zero(exitcontext)) {
00808             char tmp[2];
00809             tmp[0] = res;
00810             tmp[1] = '\0';
00811             if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00812                goto exit;
00813             else
00814                ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00815          }
00816       }
00817 
00818       /* Set up the iterator we'll be using during this call */
00819       if (!ast_strlen_zero(spec)) {
00820          iter = ast_channel_iterator_by_name_new(spec, strlen(spec));
00821       } else if (!ast_strlen_zero(exten)) {
00822          iter = ast_channel_iterator_by_exten_new(exten, context);
00823       } else {
00824          iter = ast_channel_iterator_all_new();
00825       }
00826 
00827       if (!iter) {
00828          return -1;
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 (autochan = next_channel(iter, autochan, chan);
00851            autochan;
00852           prev = autochan->chan, ast_autochan_destroy(autochan),
00853            autochan = next_autochan ? next_autochan : 
00854             next_channel(iter, autochan, chan), next_autochan = NULL) {
00855          int igrp = !mygroup;
00856          int ienf = !myenforced;
00857          char *s;
00858 
00859          if (autochan->chan == prev) {
00860             ast_autochan_destroy(autochan);
00861             break;
00862          }
00863 
00864          if (ast_check_hangup(chan)) {
00865             ast_autochan_destroy(autochan);
00866             break;
00867          }
00868 
00869          if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(autochan->chan)) {
00870             continue;
00871          }
00872 
00873          if (ast_check_hangup(autochan->chan) || ast_test_flag(autochan->chan, AST_FLAG_SPYING)) {
00874             continue;
00875          }
00876 
00877          if (mygroup) {
00878             int num_groups = 0;
00879             int num_mygroups = 0;
00880             char dup_group[512];
00881             char dup_mygroup[512];
00882             char *groups[NUM_SPYGROUPS];
00883             char *mygroups[NUM_SPYGROUPS];
00884             const char *group = NULL;
00885             int x;
00886             int y;
00887             ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
00888             num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
00889                ARRAY_LEN(mygroups));
00890 
00891             /* Before dahdi scan was part of chanspy, it would use the "GROUP" variable 
00892              * rather than "SPYGROUP", this check is done to preserve expected behavior */
00893             if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) {
00894                group = pbx_builtin_getvar_helper(autochan->chan, "GROUP");
00895             } else {
00896                group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP");
00897             }
00898 
00899             if (!ast_strlen_zero(group)) {
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             continue;
00917          }
00918          if (myenforced) {
00919             char ext[AST_CHANNEL_NAME + 3];
00920             char buffer[512];
00921             char *end;
00922 
00923             snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
00924 
00925             ast_copy_string(ext + 1, autochan->chan->name, sizeof(ext) - 1);
00926             if ((end = strchr(ext, '-'))) {
00927                *end++ = ':';
00928                *end = '\0';
00929             }
00930 
00931             ext[0] = ':';
00932 
00933             if (strcasestr(buffer, ext)) {
00934                ienf = 1;
00935             }
00936          }
00937 
00938          if (!ienf) {
00939             continue;
00940          }
00941 
00942          strcpy(peer_name, "spy-");
00943          strncat(peer_name, autochan->chan->name, AST_NAME_STRLEN - 4 - 1);
00944          ptr = strchr(peer_name, '/');
00945          *ptr++ = '\0';
00946          ptr = strsep(&ptr, "-");
00947 
00948          for (s = peer_name; s < ptr; s++)
00949             *s = tolower(*s);
00950 
00951          if (!ast_test_flag(flags, OPTION_QUIET)) {
00952             if (ast_test_flag(flags, OPTION_NAME)) {
00953                const char *local_context = S_OR(name_context, "default");
00954                const char *local_mailbox = S_OR(mailbox, ptr);
00955                res = ast_app_sayname(chan, local_mailbox, local_context);
00956             }
00957             if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
00958                if (!ast_test_flag(flags, OPTION_NOTECH)) {
00959                   if (ast_fileexists(peer_name, NULL, NULL) > 0) {
00960                      res = ast_streamfile(chan, peer_name, chan->language);
00961                      if (!res) {
00962                         res = ast_waitstream(chan, "");
00963                      }
00964                      if (res) {
00965                         ast_autochan_destroy(autochan);
00966                         break;
00967                      }
00968                   } else {
00969                      res = ast_say_character_str(chan, peer_name, "", chan->language);
00970                   }
00971                }
00972                if ((num = atoi(ptr)))
00973                   ast_say_digits(chan, atoi(ptr), "", chan->language);
00974             }
00975          }
00976 
00977          res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext);
00978          num_spyed_upon++;
00979 
00980          if (res == -1) {
00981             ast_autochan_destroy(autochan);
00982             goto exit;
00983          } else if (res == -2) {
00984             res = 0;
00985             ast_autochan_destroy(autochan);
00986             goto exit;
00987          } else if (res > 1 && spec) {
00988             struct ast_channel *next;
00989 
00990             snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
00991 
00992             if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) {
00993                next_autochan = ast_autochan_setup(next);
00994                next = ast_channel_unref(next);
00995             } else {
00996                /* stay on this channel, if it is still valid */
00997                if (!ast_check_hangup(autochan->chan)) {
00998                   next_autochan = ast_autochan_setup(autochan->chan);
00999                } else {
01000                   /* the channel is gone */
01001                   next_autochan = NULL;
01002                }
01003             }
01004          } else if (res == 0 && ast_test_flag(flags, OPTION_EXITONHANGUP)) {
01005             goto exit;
01006          }
01007       }
01008 
01009       iter = ast_channel_iterator_destroy(iter);
01010 
01011       if (res == -1 || ast_check_hangup(chan))
01012          break;
01013       if (ast_test_flag(flags, OPTION_STOP) && !next_autochan) {
01014          break;
01015       }
01016    }
01017 exit:
01018 
01019    ast_clear_flag(chan, AST_FLAG_SPYING);
01020 
01021    ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
01022 
01023    return res;
01024 }
01025 
01026 static int chanspy_exec(struct ast_channel *chan, const char *data)
01027 {
01028    char *myenforced = NULL;
01029    char *mygroup = NULL;
01030    char *recbase = NULL;
01031    int fd = 0;
01032    struct ast_flags flags;
01033    struct spy_dtmf_options user_options = {
01034       .cycle = '*',
01035       .volume = '#',
01036       .exit = '\0',
01037    };
01038    int oldwf = 0;
01039    int volfactor = 0;
01040    int res;
01041    char *mailbox = NULL;
01042    char *name_context = NULL;
01043    AST_DECLARE_APP_ARGS(args,
01044       AST_APP_ARG(spec);
01045       AST_APP_ARG(options);
01046    );
01047    char *opts[OPT_ARG_ARRAY_SIZE];
01048    char *parse = ast_strdupa(data);
01049 
01050    AST_STANDARD_APP_ARGS(args, parse);
01051 
01052    if (args.spec && !strcmp(args.spec, "all"))
01053       args.spec = NULL;
01054 
01055    if (args.options) {
01056       char tmp;
01057       ast_app_parse_options(spy_opts, &flags, opts, args.options);
01058       if (ast_test_flag(&flags, OPTION_GROUP))
01059          mygroup = opts[OPT_ARG_GROUP];
01060 
01061       if (ast_test_flag(&flags, OPTION_RECORD) &&
01062          !(recbase = opts[OPT_ARG_RECORD]))
01063          recbase = "chanspy";
01064 
01065       if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
01066          tmp = opts[OPT_ARG_EXIT][0];
01067          if (strchr("0123456789*#", tmp) && tmp != '\0') {
01068             user_options.exit = tmp;
01069          } else {
01070             ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.");
01071          }
01072       }
01073 
01074       if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
01075          tmp = opts[OPT_ARG_CYCLE][0];
01076          if (strchr("0123456789*#", tmp) && tmp != '\0') {
01077             user_options.cycle = tmp;
01078          } else {
01079             ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.");
01080          }
01081       }
01082 
01083       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
01084          int vol;
01085 
01086          if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
01087             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
01088          else
01089             volfactor = vol;
01090       }
01091 
01092       if (ast_test_flag(&flags, OPTION_PRIVATE))
01093          ast_set_flag(&flags, OPTION_WHISPER);
01094 
01095       if (ast_test_flag(&flags, OPTION_ENFORCED))
01096          myenforced = opts[OPT_ARG_ENFORCED];
01097 
01098       if (ast_test_flag(&flags, OPTION_NAME)) {
01099          if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
01100             char *delimiter;
01101             if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
01102                mailbox = opts[OPT_ARG_NAME];
01103                *delimiter++ = '\0';
01104                name_context = delimiter;
01105             } else {
01106                mailbox = opts[OPT_ARG_NAME];
01107             }
01108          }
01109       }
01110    } else {
01111       ast_clear_flag(&flags, AST_FLAGS_ALL);
01112    }
01113 
01114    oldwf = chan->writeformat;
01115    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01116       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01117       return -1;
01118    }
01119 
01120    if (recbase) {
01121       char filename[PATH_MAX];
01122 
01123       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
01124       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
01125          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
01126          fd = 0;
01127       }
01128    }
01129 
01130    res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
01131 
01132    if (fd)
01133       close(fd);
01134 
01135    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
01136       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01137 
01138    if (ast_test_flag(&flags, OPTION_EXITONHANGUP)) {
01139       ast_verb(3, "Stopped spying due to the spied-on channel hanging up.\n");
01140    }
01141 
01142    return res;
01143 }
01144 
01145 static int extenspy_exec(struct ast_channel *chan, const char *data)
01146 {
01147    char *ptr, *exten = NULL;
01148    char *mygroup = NULL;
01149    char *recbase = NULL;
01150    int fd = 0;
01151    struct ast_flags flags;
01152    struct spy_dtmf_options user_options = {
01153       .cycle = '*',
01154       .volume = '#',
01155       .exit = '\0',
01156    };
01157    int oldwf = 0;
01158    int volfactor = 0;
01159    int res;
01160    char *mailbox = NULL;
01161    char *name_context = NULL;
01162    AST_DECLARE_APP_ARGS(args,
01163       AST_APP_ARG(context);
01164       AST_APP_ARG(options);
01165    );
01166    char *parse = ast_strdupa(data);
01167 
01168    AST_STANDARD_APP_ARGS(args, parse);
01169    if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
01170       exten = args.context;
01171       *ptr++ = '\0';
01172       args.context = ptr;
01173    }
01174 
01175    if (ast_strlen_zero(args.context))
01176       args.context = ast_strdupa(chan->context);
01177 
01178    if (args.options) {
01179       char *opts[OPT_ARG_ARRAY_SIZE];
01180       char tmp;
01181 
01182       ast_app_parse_options(spy_opts, &flags, opts, args.options);
01183       if (ast_test_flag(&flags, OPTION_GROUP))
01184          mygroup = opts[OPT_ARG_GROUP];
01185 
01186       if (ast_test_flag(&flags, OPTION_RECORD) &&
01187          !(recbase = opts[OPT_ARG_RECORD]))
01188          recbase = "chanspy";
01189 
01190       if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
01191          tmp = opts[OPT_ARG_EXIT][0];
01192          if (strchr("0123456789*#", tmp) && tmp != '\0') {
01193             user_options.exit = tmp;
01194          } else {
01195             ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.");
01196          }
01197       }
01198 
01199       if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
01200          tmp = opts[OPT_ARG_CYCLE][0];
01201          if (strchr("0123456789*#", tmp) && tmp != '\0') {
01202             user_options.cycle = tmp;
01203          } else {
01204             ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.");
01205          }
01206       }
01207 
01208       if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
01209          int vol;
01210 
01211          if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
01212             ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
01213          else
01214             volfactor = vol;
01215       }
01216 
01217       if (ast_test_flag(&flags, OPTION_PRIVATE))
01218          ast_set_flag(&flags, OPTION_WHISPER);
01219 
01220       if (ast_test_flag(&flags, OPTION_NAME)) {
01221          if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
01222             char *delimiter;
01223             if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
01224                mailbox = opts[OPT_ARG_NAME];
01225                *delimiter++ = '\0';
01226                name_context = delimiter;
01227             } else {
01228                mailbox = opts[OPT_ARG_NAME];
01229             }
01230          }
01231       }
01232 
01233    } else {
01234       ast_clear_flag(&flags, AST_FLAGS_ALL);
01235    }
01236 
01237    oldwf = chan->writeformat;
01238    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01239       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01240       return -1;
01241    }
01242 
01243    if (recbase) {
01244       char filename[PATH_MAX];
01245 
01246       snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
01247       if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
01248          ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
01249          fd = 0;
01250       }
01251    }
01252 
01253 
01254    res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
01255 
01256    if (fd)
01257       close(fd);
01258 
01259    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
01260       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01261 
01262    return res;
01263 }
01264 
01265 static int dahdiscan_exec(struct ast_channel *chan, const char *data)
01266 {
01267    const char *spec = "DAHDI";
01268    struct ast_flags flags;
01269    struct spy_dtmf_options user_options = {
01270       .cycle = '#',
01271       .volume = '\0',
01272       .exit = '*',
01273    };
01274    int oldwf = 0;
01275    int res;
01276    char *mygroup = NULL;
01277 
01278    ast_clear_flag(&flags, AST_FLAGS_ALL);
01279 
01280    if (!ast_strlen_zero(data)) {
01281       mygroup = ast_strdupa(data);
01282    }
01283    ast_set_flag(&flags, OPTION_DTMF_EXIT);
01284    ast_set_flag(&flags, OPTION_DTMF_CYCLE);
01285    ast_set_flag(&flags, OPTION_DAHDI_SCAN);
01286 
01287    oldwf = chan->writeformat;
01288    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01289       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01290       return -1;
01291    }
01292 
01293    res = common_exec(chan, &flags, 0, 0, &user_options, mygroup, NULL, spec, NULL, NULL, NULL, NULL);
01294 
01295    if (oldwf && ast_set_write_format(chan, oldwf) < 0)
01296       ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01297 
01298    return res;
01299 }
01300 
01301 static int unload_module(void)
01302 {
01303    int res = 0;
01304 
01305    res |= ast_unregister_application(app_chan);
01306    res |= ast_unregister_application(app_ext);
01307    res |= ast_unregister_application(app_dahdiscan);
01308 
01309    return res;
01310 }
01311 
01312 static int load_module(void)
01313 {
01314    int res = 0;
01315 
01316    res |= ast_register_application_xml(app_chan, chanspy_exec);
01317    res |= ast_register_application_xml(app_ext, extenspy_exec);
01318    res |= ast_register_application_xml(app_dahdiscan, dahdiscan_exec);
01319 
01320    return res;
01321 }
01322 
01323 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");