Sat Feb 11 06:33:00 2012

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

Generated on Sat Feb 11 06:33:00 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6