Thu Oct 11 06:33:32 2012

Asterisk developer's documentation


app_mixmonitor.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2005, Anthony Minessale II
00005  * Copyright (C) 2005 - 2006, Digium, Inc.
00006  *
00007  * Mark Spencer <markster@digium.com>
00008  * Kevin P. Fleming <kpfleming@digium.com>
00009  *
00010  * Based on app_muxmon.c provided by
00011  * Anthony Minessale II <anthmct@yahoo.com>
00012  *
00013  * See http://www.asterisk.org for more information about
00014  * the Asterisk project. Please do not directly contact
00015  * any of the maintainers of this project for assistance;
00016  * the project provides a web site, mailing lists and IRC
00017  * channels for your use.
00018  *
00019  * This program is free software, distributed under the terms of
00020  * the GNU General Public License Version 2. See the LICENSE file
00021  * at the top of the source tree.
00022  */
00023 
00024 /*! \file
00025  *
00026  * \brief MixMonitor() - Record a call and mix the audio during the recording
00027  * \ingroup applications
00028  *
00029  * \author Mark Spencer <markster@digium.com>
00030  * \author Kevin P. Fleming <kpfleming@digium.com>
00031  *
00032  * \note Based on app_muxmon.c provided by
00033  * Anthony Minessale II <anthmct@yahoo.com>
00034  */
00035 
00036 /*** MODULEINFO
00037    <support_level>core</support_level>
00038  ***/
00039 
00040 #include "asterisk.h"
00041 
00042 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 373479 $")
00043 
00044 #include "asterisk/paths.h"   /* use ast_config_AST_MONITOR_DIR */
00045 #include "asterisk/stringfields.h"
00046 #include "asterisk/file.h"
00047 #include "asterisk/audiohook.h"
00048 #include "asterisk/pbx.h"
00049 #include "asterisk/module.h"
00050 #include "asterisk/cli.h"
00051 #include "asterisk/app.h"
00052 #include "asterisk/channel.h"
00053 #include "asterisk/autochan.h"
00054 #include "asterisk/manager.h"
00055 #include "asterisk/callerid.h"
00056 #include "asterisk/mod_format.h"
00057 #include "asterisk/linkedlists.h"
00058 
00059 /*** DOCUMENTATION
00060    <application name="MixMonitor" language="en_US">
00061       <synopsis>
00062          Record a call and mix the audio during the recording.  Use of StopMixMonitor is required
00063          to guarantee the audio file is available for processing during dialplan execution.
00064       </synopsis>
00065       <syntax>
00066          <parameter name="file" required="true" argsep=".">
00067             <argument name="filename" required="true">
00068                <para>If <replaceable>filename</replaceable> is an absolute path, uses that path, otherwise
00069                creates the file in the configured monitoring directory from <filename>asterisk.conf.</filename></para>
00070             </argument>
00071             <argument name="extension" required="true" />
00072          </parameter>
00073          <parameter name="options">
00074             <optionlist>
00075                <option name="a">
00076                   <para>Append to the file instead of overwriting it.</para>
00077                </option>
00078                <option name="b">
00079                   <para>Only save audio to the file while the channel is bridged.</para>
00080                   <note><para>Does not include conferences or sounds played to each bridged party</para></note>
00081                   <note><para>If you utilize this option inside a Local channel, you must make sure the Local
00082                   channel is not optimized away. To do this, be sure to call your Local channel with the
00083                   <literal>/n</literal> option. For example: Dial(Local/start@mycontext/n)</para></note>
00084                </option>
00085                <option name="v">
00086                   <para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
00087                   (range <literal>-4</literal> to <literal>4</literal>)</para>
00088                   <argument name="x" required="true" />
00089                </option>
00090                <option name="V">
00091                   <para>Adjust the <emphasis>spoken</emphasis> volume by a factor
00092                   of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
00093                   <argument name="x" required="true" />
00094                </option>
00095                <option name="W">
00096                   <para>Adjust both, <emphasis>heard and spoken</emphasis> volumes by a factor
00097                   of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
00098                   <argument name="x" required="true" />
00099                </option>
00100                <option name="r">
00101                   <argument name="file" required="true" />
00102                   <para>Use the specified file to record the <emphasis>receive</emphasis> audio feed.
00103                   Like with the basic filename argument, if an absolute path isn't given, it will create
00104                   the file in the configured monitoring directory.</para>
00105 
00106                </option>
00107                <option name="t">
00108                   <argument name="file" required="true" />
00109                   <para>Use the specified file to record the <emphasis>transmit</emphasis> audio feed.
00110                   Like with the basic filename argument, if an absolute path isn't given, it will create
00111                   the file in the configured monitoring directory.</para>
00112                </option>
00113                <option name="i">
00114                   <argument name="chanvar" required="true" />
00115                   <para>Stores the MixMonitor's ID on this channel variable.</para>
00116                </option>
00117                <option name="m">
00118                   <argument name="mailbox" required="true" />
00119                   <para>Create a copy of the recording as a voicemail in the indicated <emphasis>mailbox</emphasis>(es)
00120                   separated by commas eg. m(1111@default,2222@default,...).  Folders can be optionally specified using
00121                   the syntax: mailbox@context/folder</para>
00122                </option>
00123             </optionlist>
00124          </parameter>
00125          <parameter name="command">
00126             <para>Will be executed when the recording is over.</para>
00127             <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
00128             <para>All variables will be evaluated at the time MixMonitor is called.</para>
00129          </parameter>
00130       </syntax>
00131       <description>
00132          <para>Records the audio on the current channel to the specified file.</para>
00133          <para>This application does not automatically answer and should be preceeded by
00134          an application such as Answer or Progress().</para>
00135          <note><para>MixMonitor runs as an audiohook. In order to keep it running through
00136          a transfer, AUDIOHOOK_INHERIT must be set for the channel which ran mixmonitor.
00137          For more information, including dialplan configuration set for using
00138          AUDIOHOOK_INHERIT with MixMonitor, see the function documentation for
00139          AUDIOHOOK_INHERIT.</para></note>
00140          <variablelist>
00141             <variable name="MIXMONITOR_FILENAME">
00142                <para>Will contain the filename used to record.</para>
00143             </variable>
00144          </variablelist>   
00145       </description>
00146       <see-also>
00147          <ref type="application">Monitor</ref>
00148          <ref type="application">StopMixMonitor</ref>
00149          <ref type="application">PauseMonitor</ref>
00150          <ref type="application">UnpauseMonitor</ref>
00151          <ref type="function">AUDIOHOOK_INHERIT</ref>
00152       </see-also>
00153    </application>
00154    <application name="StopMixMonitor" language="en_US">
00155       <synopsis>
00156          Stop recording a call through MixMonitor, and free the recording's file handle.
00157       </synopsis>
00158       <syntax>
00159          <parameter name="MixMonitorID" required="false">
00160             <para>If a valid ID is provided, then this command will stop only that specific
00161             MixMonitor.</para>
00162          </parameter>
00163       </syntax>
00164       <description>
00165          <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
00166          on the current channel.</para>
00167       </description>
00168       <see-also>
00169          <ref type="application">MixMonitor</ref>
00170       </see-also>
00171    </application>
00172    <manager name="MixMonitorMute" language="en_US">
00173       <synopsis>
00174          Mute / unMute a Mixmonitor recording.
00175       </synopsis>
00176       <syntax>
00177          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00178          <parameter name="Channel" required="true">
00179             <para>Used to specify the channel to mute.</para>
00180          </parameter>
00181          <parameter name="Direction">
00182             <para>Which part of the recording to mute:  read, write or both (from channel, to channel or both channels).</para>
00183          </parameter>
00184          <parameter name="State">
00185             <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
00186          </parameter>
00187       </syntax>
00188       <description>
00189          <para>This action may be used to mute a MixMonitor recording.</para>
00190       </description>
00191    </manager>
00192    <manager name="MixMonitor" language="en_US">
00193       <synopsis>
00194          Record a call and mix the audio during the recording.  Use of StopMixMonitor is required
00195          to guarantee the audio file is available for processing during dialplan execution.
00196       </synopsis>
00197       <syntax>
00198          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00199          <parameter name="Channel" required="true">
00200             <para>Used to specify the channel to record.</para>
00201          </parameter>
00202          <parameter name="File">
00203             <para>Is the name of the file created in the monitor spool directory.
00204             Defaults to the same name as the channel (with slashes replaced with dashes).
00205             This argument is optional if you specify to record unidirectional audio with
00206             either the r(filename) or t(filename) options in the options field. If
00207             neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
00208             be recorded.</para>
00209          </parameter>
00210          <parameter name="options">
00211             <para>Options that apply to the MixMonitor in the same way as they
00212             would apply if invoked from the MixMonitor application. For a list of
00213             available options, see the documentation for the mixmonitor application. </para>
00214          </parameter>
00215       </syntax>
00216       <description>
00217          <para>This action records the audio on the current channel to the specified file.</para>
00218          <variablelist>
00219             <variable name="MIXMONITOR_FILENAME">
00220                <para>Will contain the filename used to record the mixed stream.</para>
00221             </variable>
00222          </variablelist>
00223       </description>
00224    </manager>
00225    <manager name="StopMixMonitor" language="en_US">
00226       <synopsis>
00227          Stop recording a call through MixMonitor, and free the recording's file handle.
00228       </synopsis>
00229       <syntax>
00230          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00231          <parameter name="Channel" required="true">
00232             <para>The name of the channel monitored.</para>
00233          </parameter>
00234          <parameter name="MixMonitorID" required="false">
00235             <para>If a valid ID is provided, then this command will stop only that specific
00236             MixMonitor.</para>
00237          </parameter>
00238       </syntax>
00239       <description>
00240          <para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
00241          action on the current channel.</para>
00242       </description>
00243    </manager>
00244 
00245  ***/
00246 
00247 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
00248 
00249 static const char * const app = "MixMonitor";
00250 
00251 static const char * const stop_app = "StopMixMonitor";
00252 
00253 static const char * const mixmonitor_spy_type = "MixMonitor";
00254 
00255 /*!
00256  * \internal
00257  * \brief This struct is a list item holds data needed to find a vm_recipient within voicemail
00258  */
00259 struct vm_recipient {
00260    char mailbox[AST_MAX_CONTEXT];
00261    char context[AST_MAX_EXTENSION];
00262    char folder[80];
00263    AST_LIST_ENTRY(vm_recipient) list;
00264 };
00265 
00266 struct mixmonitor {
00267    struct ast_audiohook audiohook;
00268    struct ast_callid *callid;
00269    char *filename;
00270    char *filename_read;
00271    char *filename_write;
00272    char *post_process;
00273    char *name;
00274    unsigned int flags;
00275    struct ast_autochan *autochan;
00276    struct mixmonitor_ds *mixmonitor_ds;
00277 
00278    /* the below string fields describe data used for creating voicemails from the recording */
00279    AST_DECLARE_STRING_FIELDS(
00280       AST_STRING_FIELD(call_context);
00281       AST_STRING_FIELD(call_macrocontext);
00282       AST_STRING_FIELD(call_extension);
00283       AST_STRING_FIELD(call_callerchan);
00284       AST_STRING_FIELD(call_callerid);
00285    );
00286    int call_priority;
00287 
00288    /* FUTURE DEVELOPMENT NOTICE
00289     * recipient_list will need locks if we make it editable after the monitor is started */
00290    AST_LIST_HEAD_NOLOCK(, vm_recipient) recipient_list;
00291 };
00292 
00293 enum mixmonitor_flags {
00294    MUXFLAG_APPEND = (1 << 1),
00295    MUXFLAG_BRIDGED = (1 << 2),
00296    MUXFLAG_VOLUME = (1 << 3),
00297    MUXFLAG_READVOLUME = (1 << 4),
00298    MUXFLAG_WRITEVOLUME = (1 << 5),
00299    MUXFLAG_READ = (1 << 6),
00300    MUXFLAG_WRITE = (1 << 7),
00301    MUXFLAG_COMBINED = (1 << 8),
00302    MUXFLAG_UID = (1 << 9),
00303    MUXFLAG_VMRECIPIENTS = (1 << 10),
00304 };
00305 
00306 enum mixmonitor_args {
00307    OPT_ARG_READVOLUME = 0,
00308    OPT_ARG_WRITEVOLUME,
00309    OPT_ARG_VOLUME,
00310    OPT_ARG_WRITENAME,
00311    OPT_ARG_READNAME,
00312    OPT_ARG_UID,
00313    OPT_ARG_VMRECIPIENTS,
00314    OPT_ARG_ARRAY_SIZE,  /* Always last element of the enum */
00315 };
00316 
00317 AST_APP_OPTIONS(mixmonitor_opts, {
00318    AST_APP_OPTION('a', MUXFLAG_APPEND),
00319    AST_APP_OPTION('b', MUXFLAG_BRIDGED),
00320    AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
00321    AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
00322    AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
00323    AST_APP_OPTION_ARG('r', MUXFLAG_READ, OPT_ARG_READNAME),
00324    AST_APP_OPTION_ARG('t', MUXFLAG_WRITE, OPT_ARG_WRITENAME),
00325    AST_APP_OPTION_ARG('i', MUXFLAG_UID, OPT_ARG_UID),
00326    AST_APP_OPTION_ARG('m', MUXFLAG_VMRECIPIENTS, OPT_ARG_VMRECIPIENTS),
00327 });
00328 
00329 struct mixmonitor_ds {
00330    unsigned int destruction_ok;
00331    ast_cond_t destruction_condition;
00332    ast_mutex_t lock;
00333 
00334    /* The filestream is held in the datastore so it can be stopped
00335     * immediately during stop_mixmonitor or channel destruction. */
00336    int fs_quit;
00337 
00338    struct ast_filestream *fs;
00339    struct ast_filestream *fs_read;
00340    struct ast_filestream *fs_write;
00341 
00342    struct ast_audiohook *audiohook;
00343 
00344    unsigned int samp_rate;
00345 };
00346 
00347 /*!
00348  * \internal
00349  * \pre mixmonitor_ds must be locked before calling this function
00350  */
00351 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
00352 {
00353    unsigned char quitting = 0;
00354 
00355    if (mixmonitor_ds->fs) {
00356       quitting = 1;
00357       ast_closestream(mixmonitor_ds->fs);
00358       mixmonitor_ds->fs = NULL;
00359       ast_verb(2, "MixMonitor close filestream (mixed)\n");
00360    }
00361 
00362    if (mixmonitor_ds->fs_read) {
00363       quitting = 1;
00364       ast_closestream(mixmonitor_ds->fs_read);
00365       mixmonitor_ds->fs_read = NULL;
00366       ast_verb(2, "MixMonitor close filestream (read)\n");
00367    }
00368 
00369    if (mixmonitor_ds->fs_write) {
00370       quitting = 1;
00371       ast_closestream(mixmonitor_ds->fs_write);
00372       mixmonitor_ds->fs_write = NULL;
00373       ast_verb(2, "MixMonitor close filestream (write)\n");
00374    }
00375 
00376    if (quitting) {
00377       mixmonitor_ds->fs_quit = 1;
00378    }
00379 }
00380 
00381 static void mixmonitor_ds_destroy(void *data)
00382 {
00383    struct mixmonitor_ds *mixmonitor_ds = data;
00384 
00385    ast_mutex_lock(&mixmonitor_ds->lock);
00386    mixmonitor_ds->audiohook = NULL;
00387    mixmonitor_ds->destruction_ok = 1;
00388    ast_cond_signal(&mixmonitor_ds->destruction_condition);
00389    ast_mutex_unlock(&mixmonitor_ds->lock);
00390 }
00391 
00392 static const struct ast_datastore_info mixmonitor_ds_info = {
00393    .type = "mixmonitor",
00394    .destroy = mixmonitor_ds_destroy,
00395 };
00396 
00397 static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
00398 {
00399    if (mixmonitor->mixmonitor_ds) {
00400       ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00401       mixmonitor->mixmonitor_ds->audiohook = NULL;
00402       ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00403    }
00404    /* kill the audiohook.*/
00405    ast_audiohook_lock(&mixmonitor->audiohook);
00406    ast_audiohook_detach(&mixmonitor->audiohook);
00407    ast_audiohook_unlock(&mixmonitor->audiohook);
00408    ast_audiohook_destroy(&mixmonitor->audiohook);
00409 }
00410 
00411 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook) 
00412 {
00413    struct ast_channel *peer = NULL;
00414    int res = 0;
00415 
00416    if (!chan)
00417       return -1;
00418 
00419    ast_audiohook_attach(chan, audiohook);
00420 
00421    if (!res && ast_test_flag(ast_channel_flags(chan), AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
00422       ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);  
00423 
00424    return res;
00425 }
00426 
00427 /*!
00428  * \internal
00429  * \brief adds recipients to a mixmonitor's recipient list
00430  * \param mixmonitor mixmonitor being affected
00431  * \param vm_recipients string containing the desired recipients to add
00432  */
00433 static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
00434 {
00435    /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
00436    char *cur_mailbox = ast_strdupa(vm_recipients);
00437    char *cur_context;
00438    char *cur_folder;
00439    char *next;
00440    int elements_processed = 0;
00441 
00442    while (!ast_strlen_zero(cur_mailbox)) {
00443       ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
00444       if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
00445          *(next++) = '\0';
00446       }
00447 
00448       if ((cur_folder = strchr(cur_mailbox, '/'))) {
00449          *(cur_folder++) = '\0';
00450       } else {
00451          cur_folder = "INBOX";
00452       }
00453 
00454       if ((cur_context = strchr(cur_mailbox, '@'))) {
00455          *(cur_context++) = '\0';
00456       } else {
00457          cur_context = "default";
00458       }
00459 
00460       if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
00461          struct vm_recipient *recipient;
00462          if (!(recipient = ast_malloc(sizeof(*recipient)))) {
00463             ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
00464             return;
00465          }
00466          ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
00467          ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
00468          ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
00469 
00470          /* Add to list */
00471          ast_verb(5, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
00472          AST_LIST_INSERT_HEAD(&mixmonitor->recipient_list, recipient, list);
00473       } else {
00474          ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
00475       }
00476 
00477       cur_mailbox = next;
00478       elements_processed++;
00479    }
00480 }
00481 
00482 static void clear_mixmonitor_recipient_list(struct mixmonitor *mixmonitor)
00483 {
00484    struct vm_recipient *current;
00485    while ((current = AST_LIST_REMOVE_HEAD(&mixmonitor->recipient_list, list))) {
00486       /* Clear list element data */
00487       ast_free(current);
00488    }
00489 }
00490 
00491 #define SAMPLES_PER_FRAME 160
00492 
00493 static void mixmonitor_free(struct mixmonitor *mixmonitor)
00494 {
00495    if (mixmonitor) {
00496       if (mixmonitor->mixmonitor_ds) {
00497          ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
00498          ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
00499          ast_free(mixmonitor->filename_write);
00500          ast_free(mixmonitor->filename_read);
00501          ast_free(mixmonitor->mixmonitor_ds);
00502          ast_free(mixmonitor->name);
00503          ast_free(mixmonitor->post_process);
00504       }
00505 
00506       /* Free everything in the recipient list */
00507       clear_mixmonitor_recipient_list(mixmonitor);
00508 
00509       /* clean stringfields */
00510       ast_string_field_free_memory(mixmonitor);
00511 
00512       if (mixmonitor->callid) {
00513          ast_callid_unref(mixmonitor->callid);
00514       }
00515       ast_free(mixmonitor);
00516    }
00517 }
00518 
00519 /*!
00520  * \internal
00521  * \brief Copies the mixmonitor to all voicemail recipients
00522  * \param mixmonitor The mixmonitor that needs to forward its file to recipients
00523  * \param ext Format of the file that was saved
00524  */
00525 static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
00526 {
00527    struct vm_recipient *recipient = NULL;
00528    struct ast_vm_recording_data recording_data;
00529    if (ast_string_field_init(&recording_data, 512)) {
00530       ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
00531       return;
00532    }
00533 
00534    /* Copy strings to stringfields that will be used for all recipients */
00535    ast_string_field_set(&recording_data, recording_file, filename);
00536    ast_string_field_set(&recording_data, recording_ext, ext);
00537    ast_string_field_set(&recording_data, call_context, mixmonitor->call_context);
00538    ast_string_field_set(&recording_data, call_macrocontext, mixmonitor->call_macrocontext);
00539    ast_string_field_set(&recording_data, call_extension, mixmonitor->call_extension);
00540    ast_string_field_set(&recording_data, call_callerchan, mixmonitor->call_callerchan);
00541    ast_string_field_set(&recording_data, call_callerid, mixmonitor->call_callerid);
00542    /* and call_priority gets copied too */
00543    recording_data.call_priority = mixmonitor->call_priority;
00544 
00545    AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
00546       /* context, mailbox, and folder need to be set per recipient */
00547       ast_string_field_set(&recording_data, context, recipient->context);
00548       ast_string_field_set(&recording_data, mailbox, recipient->mailbox);
00549       ast_string_field_set(&recording_data, folder, recipient->folder);
00550 
00551       ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
00552          recording_data.context);
00553       ast_app_copy_recording_to_vm(&recording_data);
00554    }
00555 
00556    /* Free the string fields for recording_data before exiting the function. */
00557    ast_string_field_free_memory(&recording_data);
00558 }
00559 
00560 static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
00561 {
00562    /* Initialize the file if not already done so */
00563    char *last_slash = NULL;
00564    if (!ast_strlen_zero(filename)) {
00565       if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
00566          *oflags = O_CREAT | O_WRONLY;
00567          *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
00568 
00569          last_slash = strrchr(filename, '/');
00570 
00571          if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
00572             **ext = '\0';
00573             *ext = *ext + 1;
00574          } else {
00575             *ext = "raw";
00576          }
00577 
00578          if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
00579             ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
00580             *errflag = 1;
00581          } else {
00582             struct ast_filestream *tmp = *fs;
00583             mixmonitor->mixmonitor_ds->samp_rate = MAX(mixmonitor->mixmonitor_ds->samp_rate, ast_format_rate(&tmp->fmt->format));
00584          }
00585       }
00586    }
00587 }
00588 
00589 static void *mixmonitor_thread(void *obj) 
00590 {
00591    struct mixmonitor *mixmonitor = obj;
00592    char *fs_ext = "";
00593    char *fs_read_ext = "";
00594    char *fs_write_ext = "";
00595 
00596    struct ast_filestream **fs = NULL;
00597    struct ast_filestream **fs_read = NULL;
00598    struct ast_filestream **fs_write = NULL;
00599 
00600    unsigned int oflags;
00601    int errflag = 0;
00602    struct ast_format format_slin;
00603 
00604    /* Keep callid association before any log messages */
00605    if (mixmonitor->callid) {
00606       ast_callid_threadassoc_add(mixmonitor->callid);
00607    }
00608 
00609    ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
00610 
00611    fs = &mixmonitor->mixmonitor_ds->fs;
00612    fs_read = &mixmonitor->mixmonitor_ds->fs_read;
00613    fs_write = &mixmonitor->mixmonitor_ds->fs_write;
00614 
00615    ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00616    mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
00617    mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
00618    mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
00619 
00620    ast_format_set(&format_slin, ast_format_slin_by_rate(mixmonitor->mixmonitor_ds->samp_rate), 0);
00621 
00622    ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00623 
00624 
00625    /* The audiohook must enter and exit the loop locked */
00626    ast_audiohook_lock(&mixmonitor->audiohook);
00627    while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
00628       struct ast_frame *fr = NULL;
00629       struct ast_frame *fr_read = NULL;
00630       struct ast_frame *fr_write = NULL;
00631 
00632       if (!(fr = ast_audiohook_read_frame_all(&mixmonitor->audiohook, SAMPLES_PER_FRAME, &format_slin,
00633                   &fr_read, &fr_write))) {
00634          ast_audiohook_trigger_wait(&mixmonitor->audiohook);
00635 
00636          if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00637             break;
00638          }
00639          continue;
00640       }
00641 
00642       /* audiohook lock is not required for the next block.
00643        * Unlock it, but remember to lock it before looping or exiting */
00644       ast_audiohook_unlock(&mixmonitor->audiohook);
00645 
00646       if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) {
00647          ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00648 
00649          /* Write out the frame(s) */
00650          if ((*fs_read) && (fr_read)) {
00651             struct ast_frame *cur;
00652 
00653             for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00654                ast_writestream(*fs_read, cur);
00655             }
00656          }
00657 
00658          if ((*fs_write) && (fr_write)) {
00659             struct ast_frame *cur;
00660 
00661             for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00662                ast_writestream(*fs_write, cur);
00663             }
00664          }
00665 
00666          if ((*fs) && (fr)) {
00667             struct ast_frame *cur;
00668 
00669             for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00670                ast_writestream(*fs, cur);
00671             }
00672          }
00673          ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00674       }
00675       /* All done! free it. */
00676       if (fr) {
00677          ast_frame_free(fr, 0);
00678       }
00679       if (fr_read) {
00680          ast_frame_free(fr_read, 0);
00681       }
00682       if (fr_write) {
00683          ast_frame_free(fr_write, 0);
00684       }
00685 
00686       fr = NULL;
00687       fr_write = NULL;
00688       fr_read = NULL;
00689 
00690       ast_audiohook_lock(&mixmonitor->audiohook);
00691    }
00692    ast_audiohook_unlock(&mixmonitor->audiohook);
00693 
00694    ast_autochan_destroy(mixmonitor->autochan);
00695 
00696    /* Datastore cleanup.  close the filestream and wait for ds destruction */
00697    ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
00698    mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
00699    if (!mixmonitor->mixmonitor_ds->destruction_ok) {
00700       ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
00701    }
00702    ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
00703 
00704    /* kill the audiohook */
00705    destroy_monitor_audiohook(mixmonitor);
00706 
00707    if (mixmonitor->post_process) {
00708       ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
00709       ast_safe_system(mixmonitor->post_process);
00710    }
00711 
00712    ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
00713 
00714    if (!AST_LIST_EMPTY(&mixmonitor->recipient_list)) {
00715       if (ast_strlen_zero(fs_ext)) {
00716          ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
00717             mixmonitor -> name);
00718       } else {
00719          ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
00720          copy_to_voicemail(mixmonitor, fs_ext, mixmonitor->filename);
00721       }
00722       if (!ast_strlen_zero(fs_read_ext)) {
00723          ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
00724          copy_to_voicemail(mixmonitor, fs_read_ext, mixmonitor->filename_read);
00725       }
00726       if (!ast_strlen_zero(fs_write_ext)) {
00727          ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
00728          copy_to_voicemail(mixmonitor, fs_write_ext, mixmonitor->filename_write);
00729       }
00730    } else {
00731       ast_debug(3, "No recipients to forward monitor to, moving on.\n");
00732    }
00733 
00734    mixmonitor_free(mixmonitor);
00735    return NULL;
00736 }
00737 
00738 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id)
00739 {
00740    struct ast_datastore *datastore = NULL;
00741    struct mixmonitor_ds *mixmonitor_ds;
00742 
00743    if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
00744       return -1;
00745    }
00746 
00747    if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
00748       ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
00749    }
00750 
00751    ast_mutex_init(&mixmonitor_ds->lock);
00752    ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
00753 
00754    if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
00755       ast_mutex_destroy(&mixmonitor_ds->lock);
00756       ast_cond_destroy(&mixmonitor_ds->destruction_condition);
00757       ast_free(mixmonitor_ds);
00758       return -1;
00759    }
00760 
00761 
00762    mixmonitor_ds->samp_rate = 8000;
00763    mixmonitor_ds->audiohook = &mixmonitor->audiohook;
00764    datastore->data = mixmonitor_ds;
00765 
00766    ast_channel_lock(chan);
00767    ast_channel_datastore_add(chan, datastore);
00768    ast_channel_unlock(chan);
00769 
00770    mixmonitor->mixmonitor_ds = mixmonitor_ds;
00771    return 0;
00772 }
00773 
00774 static void launch_monitor_thread(struct ast_channel *chan, const char *filename,
00775               unsigned int flags, int readvol, int writevol,
00776               const char *post_process, const char *filename_write,
00777               char *filename_read, const char *uid_channel_var,
00778               const char *recipients)
00779 {
00780    pthread_t thread;
00781    struct mixmonitor *mixmonitor;
00782    char postprocess2[1024] = "";
00783    char *datastore_id = NULL;
00784 
00785    postprocess2[0] = 0;
00786    /* If a post process system command is given attach it to the structure */
00787    if (!ast_strlen_zero(post_process)) {
00788       char *p1, *p2;
00789 
00790       p1 = ast_strdupa(post_process);
00791       for (p2 = p1; *p2; p2++) {
00792          if (*p2 == '^' && *(p2+1) == '{') {
00793             *p2 = '$';
00794          }
00795       }
00796       pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
00797    }
00798 
00799    /* Pre-allocate mixmonitor structure and spy */
00800    if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
00801       return;
00802    }
00803 
00804    /* Now that the struct has been calloced, go ahead and initialize the string fields. */
00805    if (ast_string_field_init(mixmonitor, 512)) {
00806       mixmonitor_free(mixmonitor);
00807       return;
00808    }
00809 
00810    /* Setup the actual spy before creating our thread */
00811    if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type, 0)) {
00812       mixmonitor_free(mixmonitor);
00813       return;
00814    }
00815 
00816    /* Copy over flags and channel name */
00817    mixmonitor->flags = flags;
00818    if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
00819       mixmonitor_free(mixmonitor);
00820       return;
00821    }
00822 
00823    if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id)) {
00824       ast_autochan_destroy(mixmonitor->autochan);
00825       mixmonitor_free(mixmonitor);
00826       ast_free(datastore_id);
00827       return;
00828    }
00829 
00830    if (!ast_strlen_zero(uid_channel_var)) {
00831       if (datastore_id) {
00832          pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
00833       }
00834    }
00835    ast_free(datastore_id);
00836 
00837    mixmonitor->name = ast_strdup(ast_channel_name(chan));
00838 
00839    if (!ast_strlen_zero(postprocess2)) {
00840       mixmonitor->post_process = ast_strdup(postprocess2);
00841    }
00842 
00843    if (!ast_strlen_zero(filename)) {
00844       mixmonitor->filename = ast_strdup(filename);
00845    }
00846 
00847    if (!ast_strlen_zero(filename_write)) {
00848       mixmonitor->filename_write = ast_strdup(filename_write);
00849    }
00850 
00851    if (!ast_strlen_zero(filename_read)) {
00852       mixmonitor->filename_read = ast_strdup(filename_read);
00853    }
00854 
00855    if (!ast_strlen_zero(recipients)) {
00856       char callerid[256];
00857       struct ast_party_connected_line *connected;
00858 
00859       ast_channel_lock(chan);
00860 
00861       /* We use the connected line of the invoking channel for caller ID. */
00862 
00863       connected = ast_channel_connected(chan);
00864       ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
00865          connected->id.name.str, connected->id.number.valid,
00866          connected->id.number.str);
00867       ast_callerid_merge(callerid, sizeof(callerid),
00868          S_COR(connected->id.name.valid, connected->id.name.str, NULL),
00869          S_COR(connected->id.number.valid, connected->id.number.str, NULL),
00870          "Unknown");
00871 
00872       ast_string_field_set(mixmonitor, call_context, ast_channel_context(chan));
00873       ast_string_field_set(mixmonitor, call_macrocontext, ast_channel_macrocontext(chan));
00874       ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
00875       ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
00876       ast_string_field_set(mixmonitor, call_callerid, callerid);
00877       mixmonitor->call_priority = ast_channel_priority(chan);
00878 
00879       ast_channel_unlock(chan);
00880 
00881       add_vm_recipients_from_string(mixmonitor, recipients);
00882    }
00883 
00884    ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
00885 
00886    if (readvol)
00887       mixmonitor->audiohook.options.read_volume = readvol;
00888    if (writevol)
00889       mixmonitor->audiohook.options.write_volume = writevol;
00890 
00891    if (startmon(chan, &mixmonitor->audiohook)) {
00892       ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
00893          mixmonitor_spy_type, ast_channel_name(chan));
00894       ast_audiohook_destroy(&mixmonitor->audiohook);
00895       mixmonitor_free(mixmonitor);
00896       return;
00897    }
00898 
00899    /* reference be released at mixmonitor destruction */
00900    mixmonitor->callid = ast_read_threadstorage_callid();
00901 
00902    ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
00903 }
00904 
00905 /* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
00906 /* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
00907 static char *filename_parse(char *filename, char *buffer, size_t len)
00908 {
00909    char *slash;
00910    if (ast_strlen_zero(filename)) {
00911       ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
00912    } else if (filename[0] != '/') {
00913       char *build;
00914       build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
00915       sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
00916       filename = build;
00917    }
00918 
00919    ast_copy_string(buffer, filename, len);
00920 
00921    if ((slash = strrchr(filename, '/'))) {
00922       *slash = '\0';
00923    }
00924    ast_mkdir(filename, 0777);
00925 
00926    return buffer;
00927 }
00928 
00929 static int mixmonitor_exec(struct ast_channel *chan, const char *data)
00930 {
00931    int x, readvol = 0, writevol = 0;
00932    char *filename_read = NULL;
00933    char *filename_write = NULL;
00934    char filename_buffer[1024] = "";
00935         char *uid_channel_var = NULL;
00936 
00937    struct ast_flags flags = { 0 };
00938    char *recipients = NULL;
00939    char *parse;
00940    AST_DECLARE_APP_ARGS(args,
00941       AST_APP_ARG(filename);
00942       AST_APP_ARG(options);
00943       AST_APP_ARG(post_process);
00944    );
00945 
00946    if (ast_strlen_zero(data)) {
00947       ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
00948       return -1;
00949    }
00950 
00951    parse = ast_strdupa(data);
00952 
00953    AST_STANDARD_APP_ARGS(args, parse);
00954 
00955    if (args.options) {
00956       char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
00957 
00958       ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
00959 
00960       if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
00961          if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
00962             ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
00963          } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00964             ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
00965          } else {
00966             readvol = get_volfactor(x);
00967          }
00968       }
00969 
00970       if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
00971          if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
00972             ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
00973          } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00974             ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
00975          } else {
00976             writevol = get_volfactor(x);
00977          }
00978       }
00979 
00980       if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
00981          if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
00982             ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
00983          } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
00984             ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
00985          } else {
00986             readvol = writevol = get_volfactor(x);
00987          }
00988       }
00989 
00990       if (ast_test_flag(&flags, MUXFLAG_VMRECIPIENTS)) {
00991          if (ast_strlen_zero(opts[OPT_ARG_VMRECIPIENTS])) {
00992             ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
00993          } else {
00994             recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
00995          }
00996       }
00997 
00998       if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
00999          filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
01000       }
01001 
01002       if (ast_test_flag(&flags, MUXFLAG_READ)) {
01003          filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
01004       }
01005 
01006       if (ast_test_flag(&flags, MUXFLAG_UID)) {
01007          uid_channel_var = opts[OPT_ARG_UID];
01008       }
01009    }
01010    /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
01011 
01012    if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
01013       ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
01014       return -1;
01015    }
01016 
01017    /* If filename exists, try to create directories for it */
01018    if (!(ast_strlen_zero(args.filename))) {
01019       args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
01020    }
01021 
01022    pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
01023    launch_monitor_thread(chan,
01024          args.filename,
01025          flags.flags,
01026          readvol,
01027          writevol,
01028          args.post_process,
01029          filename_write,
01030          filename_read,
01031          uid_channel_var,
01032          recipients);
01033 
01034    return 0;
01035 }
01036 
01037 static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
01038 {
01039    struct ast_datastore *datastore = NULL;
01040    char *parse = "";
01041    struct mixmonitor_ds *mixmonitor_ds;
01042 
01043    AST_DECLARE_APP_ARGS(args,
01044       AST_APP_ARG(mixmonid);
01045    );
01046 
01047    if (!ast_strlen_zero(data)) {
01048       parse = ast_strdupa(data);
01049    }
01050 
01051    AST_STANDARD_APP_ARGS(args, parse);
01052 
01053    ast_channel_lock(chan);
01054 
01055    if (!(datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.mixmonid))) {
01056       ast_channel_unlock(chan);
01057                 return -1;
01058    }
01059    mixmonitor_ds = datastore->data;
01060 
01061    ast_mutex_lock(&mixmonitor_ds->lock);
01062 
01063    /* closing the filestream here guarantees the file is avaliable to the dialplan
01064     * after calling StopMixMonitor */
01065    mixmonitor_ds_close_fs(mixmonitor_ds);
01066 
01067    /* The mixmonitor thread may be waiting on the audiohook trigger.
01068     * In order to exit from the mixmonitor loop before waiting on channel
01069     * destruction, poke the audiohook trigger. */
01070    if (mixmonitor_ds->audiohook) {
01071       if (mixmonitor_ds->audiohook->status != AST_AUDIOHOOK_STATUS_DONE) {
01072          ast_audiohook_update_status(mixmonitor_ds->audiohook, AST_AUDIOHOOK_STATUS_SHUTDOWN);
01073       }
01074       ast_audiohook_lock(mixmonitor_ds->audiohook);
01075       ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
01076       ast_audiohook_unlock(mixmonitor_ds->audiohook);
01077       mixmonitor_ds->audiohook = NULL;
01078    }
01079 
01080    ast_mutex_unlock(&mixmonitor_ds->lock);
01081 
01082    /* Remove the datastore so the monitor thread can exit */
01083    if (!ast_channel_datastore_remove(chan, datastore)) {
01084       ast_datastore_free(datastore);
01085    }
01086    ast_channel_unlock(chan);
01087 
01088    return 0;
01089 }
01090 
01091 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01092 {
01093    struct ast_channel *chan;
01094    struct ast_datastore *datastore = NULL;
01095    struct mixmonitor_ds *mixmonitor_ds = NULL;
01096 
01097    switch (cmd) {
01098    case CLI_INIT:
01099       e->command = "mixmonitor {start|stop|list}";
01100       e->usage =
01101          "Usage: mixmonitor <start|stop|list> <chan_name> [args]\n"
01102          "       The optional arguments are passed to the MixMonitor\n"
01103          "       application when the 'start' command is used.\n";
01104       return NULL;
01105    case CLI_GENERATE:
01106       return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
01107    }
01108 
01109    if (a->argc < 3) {
01110       return CLI_SHOWUSAGE;
01111    }
01112 
01113    if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
01114       ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
01115       /* Technically this is a failure, but we don't want 2 errors printing out */
01116       return CLI_SUCCESS;
01117    }
01118 
01119    ast_channel_lock(chan);
01120 
01121    if (!strcasecmp(a->argv[1], "start")) {
01122       mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
01123       ast_channel_unlock(chan);
01124    } else if (!strcasecmp(a->argv[1], "stop")){
01125       ast_channel_unlock(chan);
01126       stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
01127    } else if (!strcasecmp(a->argv[1], "list")) {
01128       ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
01129       ast_cli(a->fd, "=========================================================================\n");
01130       AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
01131          if (datastore->info == &mixmonitor_ds_info) {
01132             char *filename = "";
01133             char *filename_read = "";
01134             char *filename_write = "";
01135             mixmonitor_ds = datastore->data;
01136             if (mixmonitor_ds->fs)
01137                filename = ast_strdupa(mixmonitor_ds->fs->filename);
01138             if (mixmonitor_ds->fs_read)
01139                filename_read = ast_strdupa(mixmonitor_ds->fs_read->filename);
01140             if (mixmonitor_ds->fs_write)
01141                filename_write = ast_strdupa(mixmonitor_ds->fs_write->filename);
01142             ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
01143          }
01144       }
01145       ast_channel_unlock(chan);
01146    } else {
01147       ast_channel_unlock(chan);
01148       chan = ast_channel_unref(chan);
01149       return CLI_SHOWUSAGE;
01150    }
01151 
01152    chan = ast_channel_unref(chan);
01153 
01154    return CLI_SUCCESS;
01155 }
01156 
01157 /*! \brief  Mute / unmute  a MixMonitor channel */
01158 static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
01159 {
01160    struct ast_channel *c = NULL;
01161 
01162    const char *name = astman_get_header(m, "Channel");
01163    const char *id = astman_get_header(m, "ActionID");
01164    const char *state = astman_get_header(m, "State");
01165    const char *direction = astman_get_header(m,"Direction");
01166 
01167    int clearmute = 1;
01168 
01169    enum ast_audiohook_flags flag;
01170 
01171    if (ast_strlen_zero(direction)) {
01172       astman_send_error(s, m, "No direction specified. Must be read, write or both");
01173       return AMI_SUCCESS;
01174    }
01175 
01176    if (!strcasecmp(direction, "read")) {
01177       flag = AST_AUDIOHOOK_MUTE_READ;
01178    } else  if (!strcasecmp(direction, "write")) {
01179       flag = AST_AUDIOHOOK_MUTE_WRITE;
01180    } else  if (!strcasecmp(direction, "both")) {
01181       flag = AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE;
01182    } else {
01183       astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
01184       return AMI_SUCCESS;
01185    }
01186 
01187    if (ast_strlen_zero(name)) {
01188       astman_send_error(s, m, "No channel specified");
01189       return AMI_SUCCESS;
01190    }
01191 
01192    if (ast_strlen_zero(state)) {
01193       astman_send_error(s, m, "No state specified");
01194       return AMI_SUCCESS;
01195    }
01196 
01197    clearmute = ast_false(state);
01198    c = ast_channel_get_by_name(name);
01199 
01200    if (!c) {
01201       astman_send_error(s, m, "No such channel");
01202       return AMI_SUCCESS;
01203    }
01204 
01205    if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
01206       c = ast_channel_unref(c);
01207       astman_send_error(s, m, "Cannot set mute flag");
01208       return AMI_SUCCESS;
01209    }
01210 
01211    astman_append(s, "Response: Success\r\n");
01212 
01213    if (!ast_strlen_zero(id)) {
01214       astman_append(s, "ActionID: %s\r\n", id);
01215    }
01216 
01217    astman_append(s, "\r\n");
01218 
01219    c = ast_channel_unref(c);
01220 
01221    return AMI_SUCCESS;
01222 }
01223 
01224 static int manager_mixmonitor(struct mansession *s, const struct message *m)
01225 {
01226    struct ast_channel *c = NULL;
01227 
01228    const char *name = astman_get_header(m, "Channel");
01229    const char *id = astman_get_header(m, "ActionID");
01230    const char *file = astman_get_header(m, "File");
01231    const char *options = astman_get_header(m, "Options");
01232    char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
01233    struct ast_flags flags = { 0 };
01234    char *uid_channel_var = NULL;
01235    const char *mixmonitor_id = NULL;
01236 
01237    int res;
01238    char args[PATH_MAX] = "";
01239    if (ast_strlen_zero(name)) {
01240       astman_send_error(s, m, "No channel specified");
01241       return AMI_SUCCESS;
01242    }
01243 
01244    c = ast_channel_get_by_name(name);
01245 
01246    if (!c) {
01247       astman_send_error(s, m, "No such channel");
01248       return AMI_SUCCESS;
01249    }
01250 
01251    if (!ast_strlen_zero(options)) {
01252       ast_app_parse_options(mixmonitor_opts, &flags, opts, ast_strdupa(options));
01253    }
01254 
01255    snprintf(args, sizeof(args), "%s,%s", file, options);
01256 
01257    ast_channel_lock(c);
01258    res = mixmonitor_exec(c, args);
01259 
01260    if (ast_test_flag(&flags, MUXFLAG_UID)) {
01261       uid_channel_var = opts[OPT_ARG_UID];
01262       mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
01263    }
01264    ast_channel_unlock(c);
01265 
01266    if (res) {
01267       c = ast_channel_unref(c);
01268       astman_send_error(s, m, "Could not start monitoring channel");
01269       return AMI_SUCCESS;
01270    }
01271 
01272    astman_append(s, "Response: Success\r\n");
01273 
01274    if (!ast_strlen_zero(id)) {
01275       astman_append(s, "ActionID: %s\r\n", id);
01276    }
01277 
01278    if (!ast_strlen_zero(mixmonitor_id)) {
01279       astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
01280    }
01281 
01282    astman_append(s, "\r\n");
01283 
01284    c = ast_channel_unref(c);
01285 
01286    return AMI_SUCCESS;
01287 }
01288 
01289 static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
01290 {
01291    struct ast_channel *c = NULL;
01292 
01293    const char *name = astman_get_header(m, "Channel");
01294    const char *id = astman_get_header(m, "ActionID");
01295    const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
01296 
01297    int res;
01298    if (ast_strlen_zero(name)) {
01299       astman_send_error(s, m, "No channel specified");
01300       return AMI_SUCCESS;
01301    }
01302 
01303    c = ast_channel_get_by_name(name);
01304 
01305    if (!c) {
01306       astman_send_error(s, m, "No such channel");
01307       return AMI_SUCCESS;
01308    }
01309 
01310    res = stop_mixmonitor_exec(c, mixmonitor_id);
01311 
01312    if (res) {
01313       astman_send_error(s, m, "Could not stop monitoring channel");
01314       return AMI_SUCCESS;
01315    }
01316 
01317    astman_append(s, "Response: Success\r\n");
01318 
01319    if (!ast_strlen_zero(id)) {
01320       astman_append(s, "ActionID: %s\r\n", id);
01321    }
01322 
01323    astman_append(s, "\r\n");
01324 
01325    c = ast_channel_unref(c);
01326 
01327    return AMI_SUCCESS;
01328 }
01329 
01330 static struct ast_cli_entry cli_mixmonitor[] = {
01331    AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
01332 };
01333 
01334 static int unload_module(void)
01335 {
01336    int res;
01337 
01338    ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
01339    res = ast_unregister_application(stop_app);
01340    res |= ast_unregister_application(app);
01341    res |= ast_manager_unregister("MixMonitorMute");
01342    res |= ast_manager_unregister("MixMonitor");
01343    res |= ast_manager_unregister("StopMixMonitor");
01344 
01345    return res;
01346 }
01347 
01348 static int load_module(void)
01349 {
01350    int res;
01351 
01352    ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
01353    res = ast_register_application_xml(app, mixmonitor_exec);
01354    res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
01355    res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
01356    res |= ast_manager_register_xml("MixMonitor", 0, manager_mixmonitor);
01357    res |= ast_manager_register_xml("StopMixMonitor", 0, manager_stop_mixmonitor);
01358 
01359    return res;
01360 }
01361 
01362 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");

Generated on Thu Oct 11 06:33:32 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6