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

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