Sat Jul 12 2014 17:18:23

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