Wed Apr 6 11:29:37 2011

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

Generated on Wed Apr 6 11:29:37 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7