Thu Sep 7 01:02:50 2017

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

Generated on 7 Sep 2017 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1