Sat Apr 26 2014 22:01:27

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