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

Generated on Thu Apr 16 06:27:08 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6