Sat Apr 26 2014 22:01:26

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