res_rtp_asterisk: Avoid close the rtp/rtcp fd twice.
[asterisk/asterisk.git] / apps / app_chanspy.c
index ad5ed70..95ebace 100644 (file)
  * \ingroup applications
  */
 
-#include "asterisk.h"
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include "asterisk.h"
 
 #include <ctype.h>
 #include <errno.h>
@@ -51,6 +53,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/lock.h"
 #include "asterisk/options.h"
 #include "asterisk/autochan.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/json.h"
+#include "asterisk/format_cache.h"
 
 #define AST_NAME_STRLEN 256
 #define NUM_SPYGROUPS 128
@@ -110,6 +115,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                                either a single group or a colon-delimited list of groups, such
                                                as <literal>sales:support:accounting</literal>.</para></note>
                                        </option>
+                                       <option name="l">
+                                               <para>Allow usage of a long queue to store audio frames.</para>
+                                               <note><para>This may introduce some delay in the received audio feed, but will improve the audio quality.</para></note>
+                                       </option>
                                        <option name="n" argsep="@">
                                                <para>Say the name of the person being spied on if that person has recorded
                                                his/her name. If a context is specified, then that voicemail context will
@@ -139,6 +148,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                        <option name="S">
                                                <para>Stop when no more channels are left to spy on.</para>
                                        </option>
+                                       <option name="u">
+                                               <para>The <literal>chanprefix</literal> parameter is a channel uniqueid
+                                               or fully specified channel name.</para>
+                                       </option>
                                        <option name="v">
                                                <argument name="value" />
                                                <para>Adjust the initial volume in the range from <literal>-4</literal> 
@@ -154,7 +167,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                        </option>
                                        <option name="x">
                                                <argument name="digit" required="true">
-                                                       <para>Specify a DTMF digit that can be used to exit the application.</para>
+                                                       <para>Specify a DTMF digit that can be used to exit the application while actively
+                                                       spying on a channel. If there is no channel being spied on, the DTMF digit will be
+                                                       ignored.</para>
                                                </argument>
                                        </option>
                                        <option name="X">
@@ -169,21 +184,23 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </syntax>
                <description>
                        <para>This application is used to listen to the audio from an Asterisk channel. This includes the audio 
-                       coming in and "out of the channel being spied on. If the <literal>chanprefix</literal> parameter is specified,
+                       coming in and out of the channel being spied on. If the <literal>chanprefix</literal> parameter is specified,
                        only channels beginning with this string will be spied upon.</para>
                        <para>While spying, the following actions may be performed:</para>
                        <para> - Dialing <literal>#</literal> cycles the volume level.</para>
                        <para> - Dialing <literal>*</literal> will stop spying and look for another channel to spy on.</para>
                        <para> - Dialing a series of digits followed by <literal>#</literal> builds a channel name to append
-                       to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing the digits '1234#' 
-                       while spying will begin spying on the channel 'Agent/1234'. Note that this feature will be overridden if the 'd' option
-                       is used</para>
+                       to <literal>chanprefix</literal>. For example, executing ChanSpy(Agent) and then dialing the digits '1234#'
+                       while spying will begin spying on the channel 'Agent/1234'. Note that this feature will be overridden
+                       if the 'd' or 'u' options are used.</para>
                        <note><para>The <replaceable>X</replaceable> option supersedes the three features above in that if a valid
                        single digit extension exists in the correct context ChanSpy will exit to it.
                        This also disables choosing a channel based on <literal>chanprefix</literal> and a digit sequence.</para></note>
                </description>
                <see-also>
                        <ref type="application">ExtenSpy</ref>
+                       <ref type="managerEvent">ChanSpyStart</ref>
+                       <ref type="managerEvent">ChanSpyStop</ref>
                </see-also>
        </application>
        <application name="ExtenSpy" language="en_US">
@@ -247,6 +264,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                                either a single group or a colon-delimited list of groups, such
                                                as <literal>sales:support:accounting</literal>.</para></note>
                                        </option>
+                                       <option name="l">
+                                               <para>Allow usage of a long queue to store audio frames.</para>
+                                               <note><para>This may introduce some delay in the received audio feed, but will improve the audio quality.</para></note>
+                                       </option>
                                        <option name="n" argsep="@">
                                                <para>Say the name of the person being spied on if that person has recorded
                                                his/her name. If a context is specified, then that voicemail context will
@@ -291,7 +312,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                        </option>
                                        <option name="x">
                                                <argument name="digit" required="true">
-                                                       <para>Specify a DTMF digit that can be used to exit the application.</para>
+                                                       <para>Specify a DTMF digit that can be used to exit the application while actively
+                                                       spying on a channel. If there is no channel being spied on, the DTMF digit will be
+                                                       ignored.</para>
                                                </argument>
                                        </option>
                                        <option name="X">
@@ -318,9 +341,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </description>
                <see-also>
                        <ref type="application">ChanSpy</ref>
+                       <ref type="managerEvent">ChanSpyStart</ref>
+                       <ref type="managerEvent">ChanSpyStop</ref>
                </see-also>
        </application>
-       
        <application name="DAHDIScan" language="en_US">
                <synopsis>
                        Scan DAHDI channels to monitor calls.
@@ -334,6 +358,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <para>Allows a call center manager to monitor DAHDI channels in a
                        convenient way.  Use <literal>#</literal> to select the next channel and use <literal>*</literal> to exit.</para>
                </description>
+               <see-also>
+                       <ref type="managerEvent">ChanSpyStart</ref>
+                       <ref type="managerEvent">ChanSpyStop</ref>
+               </see-also>
        </application>
  ***/
 
@@ -359,10 +387,12 @@ enum {
        OPTION_NAME              = (1 << 12),   /* Say the name of the person on whom we will spy */
        OPTION_DTMF_SWITCH_MODES = (1 << 13),   /* Allow numeric DTMF to switch between chanspy modes */
        OPTION_DTMF_EXIT         = (1 << 14),   /* Set DTMF to exit, added for DAHDIScan integration */
-       OPTION_DTMF_CYCLE        = (1 << 15),   /* Custom DTMF for cycling next avaliable channel, (default is '*') */
+       OPTION_DTMF_CYCLE        = (1 << 15),   /* Custom DTMF for cycling next available channel, (default is '*') */
        OPTION_DAHDI_SCAN        = (1 << 16),   /* Scan groups in DAHDIScan mode */
        OPTION_STOP              = (1 << 17),
        OPTION_EXITONHANGUP      = (1 << 18),   /* Hang up when the spied-on channel hangs up. */
+       OPTION_UNIQUEID          = (1 << 19),   /* The chanprefix is a channel uniqueid or fully specified channel name. */
+       OPTION_LONG_QUEUE        = (1 << 20),   /* Allow usage of a long queue to store audio frames. */
 };
 
 enum {
@@ -384,12 +414,14 @@ AST_APP_OPTIONS(spy_opts, {
        AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
        AST_APP_OPTION('E', OPTION_EXITONHANGUP),
        AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
+       AST_APP_OPTION('l', OPTION_LONG_QUEUE),
        AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
        AST_APP_OPTION('o', OPTION_READONLY),
        AST_APP_OPTION('q', OPTION_QUIET),
        AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
        AST_APP_OPTION('s', OPTION_NOTECH),
        AST_APP_OPTION('S', OPTION_STOP),
+       AST_APP_OPTION('u', OPTION_UNIQUEID),
        AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
        AST_APP_OPTION('w', OPTION_WHISPER),
        AST_APP_OPTION('W', OPTION_PRIVATE),
@@ -404,6 +436,7 @@ struct chanspy_translation_helper {
        struct ast_audiohook bridge_whisper_audiohook;
        int fd;
        int volfactor;
+       struct ast_flags flags;
 };
 
 struct spy_dtmf_options {
@@ -435,11 +468,11 @@ static int spy_generate(struct ast_channel *chan, void *data, int len, int sampl
                return -1;
        }
 
-       if (ast_test_flag(chan, OPTION_READONLY)) {
+       if (ast_test_flag(&csth->flags, OPTION_READONLY)) {
                /* Option 'o' was set, so don't mix channel audio */
-               f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, AST_FORMAT_SLINEAR);
+               f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, ast_format_slin);
        } else {
-               f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
+               f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin);
        }
 
        ast_audiohook_unlock(&csth->spy_audiohook);
@@ -471,19 +504,25 @@ static struct ast_generator spygen = {
        .generate = spy_generate,
 };
 
-static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook)
+static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook, struct ast_flags *flags)
 {
-       int res = 0;
-       struct ast_channel *peer = NULL;
-
-       ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, autochan->chan->name);
+       int res;
 
-       ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
-       res = ast_audiohook_attach(autochan->chan, audiohook);
+       ast_autochan_channel_lock(autochan);
+       ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, ast_channel_name(autochan->chan));
 
-       if (!res && ast_test_flag(autochan->chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(autochan->chan))) {
-               ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
+       if (ast_test_flag(flags, OPTION_READONLY)) {
+               ast_set_flag(audiohook, AST_AUDIOHOOK_MUTE_WRITE);
+       } else {
+               ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
        }
+       if (ast_test_flag(flags, OPTION_LONG_QUEUE)) {
+               ast_debug(9, "Using a long queue to store audio frames in spy audiohook\n");
+       } else {
+               ast_set_flag(audiohook, AST_AUDIOHOOK_SMALL_QUEUE);
+       }
+       res = ast_audiohook_attach(autochan->chan, audiohook);
+       ast_autochan_channel_unlock(autochan);
        return res;
 }
 
@@ -501,12 +540,108 @@ static void change_spy_mode(const char digit, struct ast_flags *flags)
        }
 }
 
+static int pack_channel_into_message(struct ast_channel *chan, const char *role,
+                                                                        struct ast_multi_channel_blob *payload)
+{
+       RAII_VAR(struct ast_channel_snapshot *, snapshot,
+                       ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)),
+                       ao2_cleanup);
+
+       if (!snapshot) {
+               return -1;
+       }
+       ast_multi_channel_blob_add_channel(payload, role, snapshot);
+       return 0;
+}
+
+/*! \internal
+ * \brief Publish the chanspy message over Stasis-Core
+ * \param spyer The channel doing the spying
+ * \param spyee Who is being spied upon
+ * \start start If non-zero, the spying is starting. Otherwise, the spyer is
+ * finishing
+ */
+static void publish_chanspy_message(struct ast_channel *spyer,
+                                                                       struct ast_channel *spyee,
+                                                                       int start)
+{
+       RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+       RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       struct stasis_message_type *type = start ? ast_channel_chanspy_start_type(): ast_channel_chanspy_stop_type();
+
+       if (!spyer) {
+               ast_log(AST_LOG_WARNING, "Attempt to publish ChanSpy message for NULL spyer channel\n");
+               return;
+       }
+       blob = ast_json_null();
+       if (!blob || !type) {
+               return;
+       }
+
+       payload = ast_multi_channel_blob_create(blob);
+       if (!payload) {
+               return;
+       }
+
+       if (pack_channel_into_message(spyer, "spyer_channel", payload)) {
+               return;
+       }
+
+       if (spyee) {
+               if (pack_channel_into_message(spyee, "spyee_channel", payload)) {
+                       return;
+               }
+       }
+
+       message = stasis_message_create(type, payload);
+       if (!message) {
+               return;
+       }
+       stasis_publish(ast_channel_topic(spyer), message);
+}
+
+static int attach_barge(struct ast_autochan *spyee_autochan,
+       struct ast_autochan **spyee_bridge_autochan, struct ast_audiohook *bridge_whisper_audiohook,
+       const char *spyer_name, const char *name, struct ast_flags *flags)
+{
+       int retval = 0;
+       struct ast_autochan *internal_bridge_autochan;
+       struct ast_channel *spyee_chan;
+       RAII_VAR(struct ast_channel *, bridged, NULL, ast_channel_cleanup);
+
+       ast_autochan_channel_lock(spyee_autochan);
+       spyee_chan = ast_channel_ref(spyee_autochan->chan);
+       ast_autochan_channel_unlock(spyee_autochan);
+       bridged = ast_channel_bridge_peer(spyee_chan);
+       ast_channel_unref(spyee_chan);
+       if (!bridged) {
+               return -1;
+       }
+
+       ast_audiohook_init(bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy", 0);
+
+       internal_bridge_autochan = ast_autochan_setup(bridged);
+       if (!internal_bridge_autochan) {
+               return -1;
+       }
+
+       if (start_spying(internal_bridge_autochan, spyer_name, bridge_whisper_audiohook, flags)) {
+               ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee '%s'. Barge mode disabled.\n", name);
+               retval = -1;
+       }
+
+       *spyee_bridge_autochan = internal_bridge_autochan;
+
+       return retval;
+}
+
 static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan,
        int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags,
        char *exitcontext)
 {
        struct chanspy_translation_helper csth;
-       int running = 0, res, x = 0;
+       int running = 0, bridge_connected = 0, res, x = 0;
        char inp[24] = {0};
        char *name;
        struct ast_frame *f;
@@ -515,50 +650,50 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto
        const char *spyer_name;
 
        ast_channel_lock(chan);
-       spyer_name = ast_strdupa(chan->name);
+       if (ast_check_hangup(chan)) {
+               ast_channel_unlock(chan);
+               return 0;
+       }
+       spyer_name = ast_strdupa(ast_channel_name(chan));
        ast_channel_unlock(chan);
 
-       /* We now hold the channel lock on spyee */
-
-       if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan)) {
+       ast_autochan_channel_lock(spyee_autochan);
+       if (ast_check_hangup(spyee_autochan->chan)
+               || ast_test_flag(ast_channel_flags(spyee_autochan->chan), AST_FLAG_ZOMBIE)) {
+               ast_autochan_channel_unlock(spyee_autochan);
                return 0;
        }
-
-       ast_channel_lock(spyee_autochan->chan);
-       name = ast_strdupa(spyee_autochan->chan->name);
-       ast_channel_unlock(spyee_autochan->chan);
+       name = ast_strdupa(ast_channel_name(spyee_autochan->chan));
 
        ast_verb(2, "Spying on channel %s\n", name);
-       manager_event(EVENT_FLAG_CALL, "ChanSpyStart",
-                       "SpyerChannel: %s\r\n"
-                       "SpyeeChannel: %s\r\n",
-                       spyer_name, name);
+       publish_chanspy_message(chan, spyee_autochan->chan, 1);
+       ast_autochan_channel_unlock(spyee_autochan);
 
        memset(&csth, 0, sizeof(csth));
+       ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL);
 
-       ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
+       /* This is the audiohook which gives us the audio off the channel we are
+          spying on.
+       */
+       ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy", 0);
 
-       if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) {
+       if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook, flags)) {
                ast_audiohook_destroy(&csth.spy_audiohook);
                return 0;
        }
 
-       ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
-       ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
-       if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) {
-               ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name);
-       }
-       if ((spyee_bridge_autochan = ast_autochan_setup(ast_bridged_channel(spyee_autochan->chan)))) {
-               ast_channel_lock(spyee_bridge_autochan->chan);
-               if (start_spying(spyee_bridge_autochan, spyer_name, &csth.bridge_whisper_audiohook)) {
-                       ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", name);
+       if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
+               /* This audiohook will let us inject audio from our channel into the
+                  channel we are currently spying on.
+               */
+               ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy", 0);
+
+               if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook, flags)) {
+                       ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name);
                }
-               ast_channel_unlock(spyee_bridge_autochan->chan);
        }
 
-       ast_channel_lock(chan);
-       ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
-       ast_channel_unlock(chan);
+       ast_channel_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
 
        csth.volfactor = *volfactor;
 
@@ -588,19 +723,35 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto
           has arrived, since the spied-on channel could have gone away while
           we were waiting
        */
-       while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
+       while (ast_waitfor(chan, -1) > -1 && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
                if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
                        running = -1;
+                       if (f) {
+                               ast_frfree(f);
+                       }
                        break;
                }
 
                if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
+                       /* This hook lets us inject audio into the channel that the spyee is currently
+                        * bridged with. If the spyee isn't bridged with anything yet, nothing will
+                        * be attached and we'll need to continue attempting to attach the barge
+                        * audio hook. */
+                       if (!bridge_connected && attach_barge(spyee_autochan, &spyee_bridge_autochan,
+                                       &csth.bridge_whisper_audiohook, spyer_name, name, flags) == 0) {
+                               bridge_connected = 1;
+                       }
+
                        ast_audiohook_lock(&csth.whisper_audiohook);
-                       ast_audiohook_lock(&csth.bridge_whisper_audiohook);
                        ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
-                       ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
                        ast_audiohook_unlock(&csth.whisper_audiohook);
-                       ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
+
+                       if (bridge_connected) {
+                               ast_audiohook_lock(&csth.bridge_whisper_audiohook);
+                               ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
+                               ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
+                       }
+
                        ast_frfree(f);
                        continue;
                } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
@@ -610,8 +761,8 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto
                        ast_frfree(f);
                        continue;
                }
-               
-               res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
+
+               res = (f->frametype == AST_FRAME_DTMF) ? f->subclass.integer : 0;
                ast_frfree(f);
                if (!res)
                        continue;
@@ -659,7 +810,7 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto
                        (*volfactor)++;
                        if (*volfactor > 4)
                                *volfactor = -4;
-                       ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
+                       ast_verb(3, "Setting spy volume on %s to %d\n", ast_channel_name(chan), *volfactor);
 
                        csth.volfactor = *volfactor;
                        csth.spy_audiohook.options.read_volume = csth.volfactor;
@@ -672,19 +823,21 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto
        else
                ast_deactivate_generator(chan);
 
-       ast_channel_lock(chan);
-       ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
-       ast_channel_unlock(chan);
+       ast_channel_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
 
-       ast_audiohook_lock(&csth.whisper_audiohook);
-       ast_audiohook_detach(&csth.whisper_audiohook);
-       ast_audiohook_unlock(&csth.whisper_audiohook);
-       ast_audiohook_destroy(&csth.whisper_audiohook);
-       
-       ast_audiohook_lock(&csth.bridge_whisper_audiohook);
-       ast_audiohook_detach(&csth.bridge_whisper_audiohook);
-       ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
-       ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
+       if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
+               ast_audiohook_lock(&csth.whisper_audiohook);
+               ast_audiohook_detach(&csth.whisper_audiohook);
+               ast_audiohook_unlock(&csth.whisper_audiohook);
+               ast_audiohook_destroy(&csth.whisper_audiohook);
+       }
+
+       if (ast_test_flag(flags, OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) {
+               ast_audiohook_lock(&csth.bridge_whisper_audiohook);
+               ast_audiohook_detach(&csth.bridge_whisper_audiohook);
+               ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
+               ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
+       }
 
        ast_audiohook_lock(&csth.spy_audiohook);
        ast_audiohook_detach(&csth.spy_audiohook);
@@ -696,33 +849,43 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto
        }
 
        ast_verb(2, "Done Spying on channel %s\n", name);
-       manager_event(EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name);
+       publish_chanspy_message(chan, NULL, 0);
 
        return running;
 }
 
 static struct ast_autochan *next_channel(struct ast_channel_iterator *iter,
-               struct ast_autochan *autochan, struct ast_channel *chan)
+       struct ast_channel *chan)
 {
        struct ast_channel *next;
+       struct ast_autochan *autochan_store;
        const size_t pseudo_len = strlen("DAHDI/pseudo");
 
        if (!iter) {
                return NULL;
        }
 
-redo:
-       if (!(next = ast_channel_iterator_next(iter))) {
-               return NULL;
-       }
+       for (; (next = ast_channel_iterator_next(iter)); ast_channel_unref(next)) {
+               if (!strncmp(ast_channel_name(next), "DAHDI/pseudo", pseudo_len)
+                       || next == chan) {
+                       continue;
+               }
+
+               autochan_store = ast_autochan_setup(next);
+               ast_channel_unref(next);
 
-       if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
-               goto redo;
-       } else if (next == chan) {
-               goto redo;
+               return autochan_store;
        }
+       return NULL;
+}
 
-       return ast_autochan_setup(next);
+static int spy_sayname(struct ast_channel *chan, const char *mailbox, const char *context)
+{
+       char *mailbox_id;
+
+       mailbox_id = ast_alloca(strlen(mailbox) + strlen(context) + 2);
+       sprintf(mailbox_id, "%s@%s", mailbox, context); /* Safe */
+       return ast_app_sayname(chan, mailbox_id);
 }
 
 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
@@ -731,13 +894,10 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
        const char *context, const char *mailbox, const char *name_context)
 {
        char nameprefix[AST_NAME_STRLEN];
-       char peer_name[AST_NAME_STRLEN + 5];
        char exitcontext[AST_MAX_CONTEXT] = "";
        signed char zero_volume = 0;
        int waitms;
        int res;
-       char *ptr;
-       int num;
        int num_spyed_upon = 1;
        struct ast_channel_iterator *iter = NULL;
 
@@ -746,18 +906,18 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
                ast_channel_lock(chan);
                if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
                        ast_copy_string(exitcontext, c, sizeof(exitcontext));
-               } else if (!ast_strlen_zero(chan->macrocontext)) {
-                       ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
+               } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
+                       ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
                } else {
-                       ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
+                       ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
                }
                ast_channel_unlock(chan);
        }
 
-       if (chan->_state != AST_STATE_UP)
+       if (ast_channel_state(chan) != AST_STATE_UP)
                ast_answer(chan);
 
-       ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
+       ast_channel_set_flag(chan, AST_FLAG_SPYING);
 
        waitms = 100;
 
@@ -766,11 +926,11 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
                struct ast_channel *prev = NULL;
 
                if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
-                       res = ast_streamfile(chan, "beep", chan->language);
+                       res = ast_streamfile(chan, "beep", ast_channel_language(chan));
                        if (!res)
                                res = ast_waitstream(chan, "");
                        else if (res < 0) {
-                               ast_clear_flag(chan, AST_FLAG_SPYING);
+                               ast_channel_clear_flag(chan, AST_FLAG_SPYING);
                                break;
                        }
                        if (!ast_strlen_zero(exitcontext)) {
@@ -786,7 +946,19 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
 
                /* Set up the iterator we'll be using during this call */
                if (!ast_strlen_zero(spec)) {
-                       iter = ast_channel_iterator_by_name_new(spec, strlen(spec));
+                       if (ast_test_flag(flags, OPTION_UNIQUEID)) {
+                               struct ast_channel *unique_chan;
+
+                               unique_chan = ast_channel_get_by_name(spec);
+                               if (!unique_chan) {
+                                       res = -1;
+                                       goto exit;
+                               }
+                               iter = ast_channel_iterator_by_name_new(ast_channel_name(unique_chan), 0);
+                               ast_channel_unref(unique_chan);
+                       } else {
+                               iter = ast_channel_iterator_by_name_new(spec, strlen(spec));
+                       }
                } else if (!ast_strlen_zero(exten)) {
                        iter = ast_channel_iterator_by_exten_new(exten, context);
                } else {
@@ -794,36 +966,40 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
                }
 
                if (!iter) {
-                       return -1;
+                       res = -1;
+                       goto exit;
                }
 
                res = ast_waitfordigit(chan, waitms);
                if (res < 0) {
-                       ast_clear_flag(chan, AST_FLAG_SPYING);
+                       iter = ast_channel_iterator_destroy(iter);
+                       ast_channel_clear_flag(chan, AST_FLAG_SPYING);
                        break;
                }
                if (!ast_strlen_zero(exitcontext)) {
                        char tmp[2];
                        tmp[0] = res;
                        tmp[1] = '\0';
-                       if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
+                       if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
+                               iter = ast_channel_iterator_destroy(iter);
                                goto exit;
-                       else
+                       } else {
                                ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
+                       }
                }
 
                /* reset for the next loop around, unless overridden later */
                waitms = 100;
                num_spyed_upon = 0;
 
-               for (autochan = next_channel(iter, autochan, chan);
-                    autochan;
-                        prev = autochan->chan, ast_autochan_destroy(autochan),
-                    autochan = next_autochan ? next_autochan : 
-                               next_channel(iter, autochan, chan), next_autochan = NULL) {
+               for (autochan = next_channel(iter, chan);
+                       autochan;
+                       prev = autochan->chan,
+                               ast_autochan_destroy(autochan),
+                               autochan = next_autochan ?: next_channel(iter, chan),
+                               next_autochan = NULL) {
                        int igrp = !mygroup;
                        int ienf = !myenforced;
-                       char *s;
 
                        if (autochan->chan == prev) {
                                ast_autochan_destroy(autochan);
@@ -835,13 +1011,19 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
                                break;
                        }
 
-                       if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(autochan->chan)) {
+                       ast_autochan_channel_lock(autochan);
+                       if (ast_test_flag(flags, OPTION_BRIDGED)
+                               && !ast_channel_is_bridged(autochan->chan)) {
+                               ast_autochan_channel_unlock(autochan);
                                continue;
                        }
 
-                       if (ast_check_hangup(autochan->chan) || ast_test_flag(autochan->chan, AST_FLAG_SPYING)) {
+                       if (ast_check_hangup(autochan->chan)
+                               || ast_test_flag(ast_channel_flags(autochan->chan), AST_FLAG_SPYING)) {
+                               ast_autochan_channel_unlock(autochan);
                                continue;
                        }
+                       ast_autochan_channel_unlock(autochan);
 
                        if (mygroup) {
                                int num_groups = 0;
@@ -859,11 +1041,13 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
 
                                /* Before dahdi scan was part of chanspy, it would use the "GROUP" variable 
                                 * rather than "SPYGROUP", this check is done to preserve expected behavior */
+                               ast_autochan_channel_lock(autochan);
                                if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) {
                                        group = pbx_builtin_getvar_helper(autochan->chan, "GROUP");
                                } else {
                                        group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP");
                                }
+                               ast_autochan_channel_unlock(autochan);
 
                                if (!ast_strlen_zero(group)) {
                                        ast_copy_string(dup_group, group, sizeof(dup_group));
@@ -891,7 +1075,9 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
 
                                snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
 
-                               ast_copy_string(ext + 1, autochan->chan->name, sizeof(ext) - 1);
+                               ast_autochan_channel_lock(autochan);
+                               ast_copy_string(ext + 1, ast_channel_name(autochan->chan), sizeof(ext) - 1);
+                               ast_autochan_channel_unlock(autochan);
                                if ((end = strchr(ext, '-'))) {
                                        *end++ = ':';
                                        *end = '\0';
@@ -908,25 +1094,39 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
                                continue;
                        }
 
-                       strcpy(peer_name, "spy-");
-                       strncat(peer_name, autochan->chan->name, AST_NAME_STRLEN - 4 - 1);
-                       ptr = strchr(peer_name, '/');
-                       *ptr++ = '\0';
-                       ptr = strsep(&ptr, "-");
-
-                       for (s = peer_name; s < ptr; s++)
-                               *s = tolower(*s);
-
                        if (!ast_test_flag(flags, OPTION_QUIET)) {
+                               char peer_name[AST_NAME_STRLEN + 5];
+                               char *ptr, *s;
+
+                               strcpy(peer_name, "spy-");
+                               ast_autochan_channel_lock(autochan);
+                               strncat(peer_name, ast_channel_name(autochan->chan), AST_NAME_STRLEN - 4 - 1);
+                               ast_autochan_channel_unlock(autochan);
+                               if ((ptr = strchr(peer_name, '/'))) {
+                                       *ptr++ = '\0';
+                                       for (s = peer_name; s < ptr; s++) {
+                                               *s = tolower(*s);
+                                       }
+                                       if ((s = strchr(ptr, '-'))) {
+                                               *s = '\0';
+                                       }
+                               }
+
                                if (ast_test_flag(flags, OPTION_NAME)) {
                                        const char *local_context = S_OR(name_context, "default");
                                        const char *local_mailbox = S_OR(mailbox, ptr);
-                                       res = ast_app_sayname(chan, local_mailbox, local_context);
+
+                                       if (local_mailbox) {
+                                               res = spy_sayname(chan, local_mailbox, local_context);
+                                       } else {
+                                               res = -1;
+                                       }
                                }
                                if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
+                                       int num;
                                        if (!ast_test_flag(flags, OPTION_NOTECH)) {
-                                               if (ast_fileexists(peer_name, NULL, NULL) != -1) {
-                                                       res = ast_streamfile(chan, peer_name, chan->language);
+                                               if (ast_fileexists(peer_name, NULL, NULL) > 0) {
+                                                       res = ast_streamfile(chan, peer_name, ast_channel_language(chan));
                                                        if (!res) {
                                                                res = ast_waitstream(chan, "");
                                                        }
@@ -935,11 +1135,12 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
                                                                break;
                                                        }
                                                } else {
-                                                       res = ast_say_character_str(chan, peer_name, "", chan->language);
+                                                       res = ast_say_character_str(chan, peer_name, "", ast_channel_language(chan), AST_SAY_CASE_NONE);
                                                }
                                        }
-                                       if ((num = atoi(ptr)))
-                                               ast_say_digits(chan, atoi(ptr), "", chan->language);
+                                       if (ptr && (num = atoi(ptr))) {
+                                               ast_say_digits(chan, num, "", ast_channel_language(chan));
+                                       }
                                }
                        }
 
@@ -948,12 +1149,14 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
 
                        if (res == -1) {
                                ast_autochan_destroy(autochan);
+                               iter = ast_channel_iterator_destroy(iter);
                                goto exit;
                        } else if (res == -2) {
                                res = 0;
                                ast_autochan_destroy(autochan);
+                               iter = ast_channel_iterator_destroy(iter);
                                goto exit;
-                       } else if (res > 1 && spec) {
+                       } else if (res > 1 && spec && !ast_test_flag(flags, OPTION_UNIQUEID)) {
                                struct ast_channel *next;
 
                                snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
@@ -963,14 +1166,18 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
                                        next = ast_channel_unref(next);
                                } else {
                                        /* stay on this channel, if it is still valid */
+                                       ast_autochan_channel_lock(autochan);
                                        if (!ast_check_hangup(autochan->chan)) {
                                                next_autochan = ast_autochan_setup(autochan->chan);
                                        } else {
                                                /* the channel is gone */
                                                next_autochan = NULL;
                                        }
+                                       ast_autochan_channel_unlock(autochan);
                                }
                        } else if (res == 0 && ast_test_flag(flags, OPTION_EXITONHANGUP)) {
+                               ast_autochan_destroy(autochan);
+                               iter = ast_channel_iterator_destroy(iter);
                                goto exit;
                        }
                }
@@ -985,7 +1192,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
        }
 exit:
 
-       ast_clear_flag(chan, AST_FLAG_SPYING);
+       ast_channel_clear_flag(chan, AST_FLAG_SPYING);
 
        ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
 
@@ -1004,7 +1211,7 @@ static int chanspy_exec(struct ast_channel *chan, const char *data)
                .volume = '#',
                .exit = '\0',
        };
-       int oldwf = 0;
+       RAII_VAR(struct ast_format *, oldwf, NULL, ao2_cleanup);
        int volfactor = 0;
        int res;
        char *mailbox = NULL;
@@ -1036,7 +1243,7 @@ static int chanspy_exec(struct ast_channel *chan, const char *data)
                        if (strchr("0123456789*#", tmp) && tmp != '\0') {
                                user_options.exit = tmp;
                        } else {
-                               ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.");
+                               ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.\n");
                        }
                }
 
@@ -1045,7 +1252,7 @@ static int chanspy_exec(struct ast_channel *chan, const char *data)
                        if (strchr("0123456789*#", tmp) && tmp != '\0') {
                                user_options.cycle = tmp;
                        } else {
-                               ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.");
+                               ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.\n");
                        }
                }
 
@@ -1080,8 +1287,8 @@ static int chanspy_exec(struct ast_channel *chan, const char *data)
                ast_clear_flag(&flags, AST_FLAGS_ALL);
        }
 
-       oldwf = chan->writeformat;
-       if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
+       oldwf = ao2_bump(ast_channel_writeformat(chan));
+       if (ast_set_write_format(chan, ast_format_slin) < 0) {
                ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
                return -1;
        }
@@ -1123,7 +1330,7 @@ static int extenspy_exec(struct ast_channel *chan, const char *data)
                .volume = '#',
                .exit = '\0',
        };
-       int oldwf = 0;
+       RAII_VAR(struct ast_format *, oldwf, NULL, ao2_cleanup);
        int volfactor = 0;
        int res;
        char *mailbox = NULL;
@@ -1135,14 +1342,14 @@ static int extenspy_exec(struct ast_channel *chan, const char *data)
        char *parse = ast_strdupa(data);
 
        AST_STANDARD_APP_ARGS(args, parse);
+
        if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
                exten = args.context;
                *ptr++ = '\0';
                args.context = ptr;
        }
-
        if (ast_strlen_zero(args.context))
-               args.context = ast_strdupa(chan->context);
+               args.context = ast_strdupa(ast_channel_context(chan));
 
        if (args.options) {
                char *opts[OPT_ARG_ARRAY_SIZE];
@@ -1161,7 +1368,7 @@ static int extenspy_exec(struct ast_channel *chan, const char *data)
                        if (strchr("0123456789*#", tmp) && tmp != '\0') {
                                user_options.exit = tmp;
                        } else {
-                               ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.");
+                               ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.\n");
                        }
                }
 
@@ -1170,7 +1377,7 @@ static int extenspy_exec(struct ast_channel *chan, const char *data)
                        if (strchr("0123456789*#", tmp) && tmp != '\0') {
                                user_options.cycle = tmp;
                        } else {
-                               ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.");
+                               ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.\n");
                        }
                }
 
@@ -1200,11 +1407,12 @@ static int extenspy_exec(struct ast_channel *chan, const char *data)
                }
 
        } else {
+               /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
                ast_clear_flag(&flags, AST_FLAGS_ALL);
        }
 
-       oldwf = chan->writeformat;
-       if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
+       oldwf = ao2_bump(ast_channel_writeformat(chan));
+       if (ast_set_write_format(chan, ast_format_slin) < 0) {
                ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
                return -1;
        }
@@ -1234,16 +1442,17 @@ static int extenspy_exec(struct ast_channel *chan, const char *data)
 static int dahdiscan_exec(struct ast_channel *chan, const char *data)
 {
        const char *spec = "DAHDI";
-       struct ast_flags flags;
+       struct ast_flags flags = {0};
        struct spy_dtmf_options user_options = {
                .cycle = '#',
                .volume = '\0',
                .exit = '*',
        };
-       int oldwf = 0;
+       struct ast_format *oldwf;
        int res;
        char *mygroup = NULL;
 
+       /* Coverity - This uninit_use should be ignored since this macro initializes the flags */
        ast_clear_flag(&flags, AST_FLAGS_ALL);
 
        if (!ast_strlen_zero(data)) {
@@ -1253,9 +1462,10 @@ static int dahdiscan_exec(struct ast_channel *chan, const char *data)
        ast_set_flag(&flags, OPTION_DTMF_CYCLE);
        ast_set_flag(&flags, OPTION_DAHDI_SCAN);
 
-       oldwf = chan->writeformat;
-       if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
+       oldwf = ao2_bump(ast_channel_writeformat(chan));
+       if (ast_set_write_format(chan, ast_format_slin) < 0) {
                ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+               ao2_cleanup(oldwf);
                return -1;
        }
 
@@ -1263,6 +1473,7 @@ static int dahdiscan_exec(struct ast_channel *chan, const char *data)
 
        if (oldwf && ast_set_write_format(chan, oldwf) < 0)
                ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+       ao2_cleanup(oldwf);
 
        return res;
 }