Mon Jun 18 2012 03:55:18

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