Channels: Masquerades to automatically move frame/audio hooks
authorJonathan Rose <jrose@digium.com>
Fri, 18 Jul 2014 16:28:10 +0000 (16:28 +0000)
committerJonathan Rose <jrose@digium.com>
Fri, 18 Jul 2014 16:28:10 +0000 (16:28 +0000)
Whenever possible, audiohooks and framehooks will now be copied over
to the channel that the masquerading channel gets cloned into. This
should occur for all audiohooks and most framehooks. As a result,
in Asterisk 12.5 and up, the AUDIOHOOK_INHERIT function is now
deprecated and its behavior is essentially the new default for all
audiohooks, plus some additional audiohooks/framehooks.

Review: https://reviewboard.asterisk.org/r/3721/
........

Merged revisions 418914 from http://svn.asterisk.org/svn/asterisk/branches/12

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@418936 65c4cc65-6c06-0410-ace0-fbb531ad65f3

12 files changed:
CHANGES
bridges/bridge_native_rtp.c
funcs/func_audiohookinherit.c
include/asterisk/audiohook.h
include/asterisk/framehook.h
include/asterisk/res_fax.h
main/audiohook.c
main/bridge_basic.c
main/channel.c
main/framehook.c
res/res_fax.c
res/res_pjsip_refer.c

diff --git a/CHANGES b/CHANGES
index 7f1e98f..b992edf 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -192,6 +192,11 @@ res_pjsip
    created for an endpoint with this setting will have its accountcode set
    to the specified value.
 
+Functions
+------------------
+ * Function AUDIOHOOK_INHERIT has been deprecated. Audiohooks are now
+   unconditionally inhereted through masquerades. As a side benefit, more
+   than one audiohook of a given type may persist through a masquerade now.
 
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 12.3.0 to Asterisk 12.4.0 ------------
index 725a6f6..2362ad2 100644 (file)
@@ -405,6 +405,7 @@ static int native_rtp_bridge_framehook_attach(struct ast_bridge_channel *bridge_
                .version = AST_FRAMEHOOK_INTERFACE_VERSION,
                .event_cb = native_rtp_framehook,
                .consume_cb = native_rtp_framehook_consume,
+               .disable_inheritance = 1,
        };
 
        if (!data) {
index ea5c5e9..28a2a45 100644 (file)
  */
 
 /*** MODULEINFO
-       <support_level>core</support_level>
+       <support_level>deprecated</support_level>
  ***/
 
 #include "asterisk.h"
-#include "asterisk/datastore.h"
 #include "asterisk/channel.h"
 #include "asterisk/logger.h"
-#include "asterisk/audiohook.h"
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
 
 /*** DOCUMENTATION
-       <function name = "AUDIOHOOK_INHERIT" language="en_US">
+       <function name = "AUDIOHOOK_INHERIT" language="en_US">
                <synopsis>
-                       Set whether an audiohook may be inherited to another channel
+                       DEPRECATED: Used to set whether an audiohook may be inherited to another
+                       channel. Due to architectural changes in Asterisk 12, audiohook inheritance
+                       is performed automatically and this function now lacks function.
                </synopsis>
-               <syntax>
-                       <parameter name="source" required="true">
-                               <para>The built-in sources in Asterisk are</para>
-                               <enumlist>
-                                       <enum name="MixMonitor" />
-                                       <enum name="Chanspy" />
-                                       <enum name="Volume" />
-                                       <enum name="Speex" />
-                                       <enum name="pitch_shift" />
-                                       <enum name="JACK_HOOK" />
-                                       <enum name="Mute" />
-                               </enumlist>
-                               <para>Note that the names are not case-sensitive</para>
-                       </parameter>
-               </syntax>
                <description>
-                       <para>By enabling audiohook inheritance on the channel, you are giving
-                       permission for an audiohook to be inherited by a descendent channel.
-                       Inheritance may be be disabled at any point as well.</para>
-
-                       <para>Example scenario:</para>
-                       <para>exten => 2000,1,MixMonitor(blah.wav)</para>
-                       <para>exten => 2000,n,Set(AUDIOHOOK_INHERIT(MixMonitor)=yes)</para>
-                       <para>exten => 2000,n,Dial(SIP/2000)</para>
-                       <para>
-                       </para>
-                       <para>exten => 4000,1,Dial(SIP/4000)</para>
-                       <para>
-                       </para>
-                       <para>exten => 5000,1,MixMonitor(blah2.wav)</para>
-                       <para>exten => 5000,n,Dial(SIP/5000)</para>
-                       <para>
-                       </para>
-                       <para>In this basic dialplan scenario, let's consider the following sample calls</para>
-                       <para>Call 1: Caller dials 2000. The person who answers then executes an attended</para>
-                       <para>        transfer to 4000.</para>
-                       <para>Result: Since extension 2000 set MixMonitor to be inheritable, after the</para>
-                       <para>        transfer to 4000 has completed, the call will continue to be recorded
-                       to blah.wav</para>
-                       <para>
-                       </para>
-                       <para>Call 2: Caller dials 5000. The person who answers then executes an attended</para>
-                       <para>        transfer to 4000.</para>
-                       <para>Result: Since extension 5000 did not set MixMonitor to be inheritable, the</para>
-                       <para>        recording will stop once the call has been transferred to 4000.</para>
+                       <para>Prior to Asterisk 12, masquerades would occur under all sorts of
+                       situations which were hard to predict. In Asterisk 12, masquerades now only
+                       occur as a result of small set of similar operations for which inheriting
+                       all     audiohooks from the original channel is now safe, so in Asterisk 12.5+,
+                       all audiohooks are inherited without needing other controls expressing
+                       which audiohooks should be inherited under which which conditions.</para>
                </description>
        </function>
  ***/
 
-struct inheritable_audiohook {
-       AST_LIST_ENTRY(inheritable_audiohook) list;
-       char source[1];
-};
-
-struct audiohook_inheritance_datastore {
-       AST_LIST_HEAD (, inheritable_audiohook) allowed_list;
-};
-
-static void audiohook_inheritance_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
-static void audiohook_inheritance_destroy (void *data);
-static const struct ast_datastore_info audiohook_inheritance_info = {
-       .type = "audiohook inheritance",
-       .destroy = audiohook_inheritance_destroy,
-       .chan_fixup = audiohook_inheritance_fixup,
-};
-
-/*! \brief Move audiohooks as defined by previous calls to the AUDIOHOOK_INHERIT function
- *
- * Move allowed audiohooks from the old channel to the new channel.
- *
- * \param data The ast_datastore containing audiohook inheritance information that will be moved
- * \param old_chan The "clone" channel from a masquerade. We are moving the audiohook in question off of this channel
- * \param new_chan The "original" channel from a masquerade. We are moving the audiohook in question to this channel
- * \return Void
- */
-static void audiohook_inheritance_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
-{
-       struct inheritable_audiohook *audiohook = NULL;
-       struct audiohook_inheritance_datastore *datastore = data;
-
-       ast_debug(2, "inheritance fixup occurring for channels %s(%p) and %s(%p)", ast_channel_name(old_chan), old_chan, ast_channel_name(new_chan), new_chan);
-
-       AST_LIST_TRAVERSE(&datastore->allowed_list, audiohook, list) {
-               ast_audiohook_move_by_source(old_chan, new_chan, audiohook->source);
-               ast_debug(3, "Moved audiohook %s from %s(%p) to %s(%p)\n",
-                       audiohook->source, ast_channel_name(old_chan), old_chan, ast_channel_name(new_chan), new_chan);
-       }
-       return;
-}
-
-/*! \brief Destroy dynamically allocated data on an audiohook_inheritance_datastore
- *
- * \param data Pointer to the audiohook_inheritance_datastore in question.
- * \return Void
- */
-static void audiohook_inheritance_destroy(void *data)
-{
-       struct audiohook_inheritance_datastore *audiohook_inheritance_datastore = data;
-       struct inheritable_audiohook *inheritable_audiohook = NULL;
-
-       while ((inheritable_audiohook = AST_LIST_REMOVE_HEAD(&audiohook_inheritance_datastore->allowed_list, list))) {
-               ast_free(inheritable_audiohook);
-       }
-
-       ast_free(audiohook_inheritance_datastore);
-}
-
-/*! \brief create an audiohook_inheritance_datastore and attach it to a channel
- *
- * \param chan The channel to which we wish to attach the new datastore
- * \return Returns the newly created audiohook_inheritance_datastore or NULL on error
- */
-static struct audiohook_inheritance_datastore *setup_inheritance_datastore(struct ast_channel *chan)
-{
-       struct ast_datastore *datastore = NULL;
-       struct audiohook_inheritance_datastore *audiohook_inheritance_datastore = NULL;
-
-       if (!(datastore = ast_datastore_alloc(&audiohook_inheritance_info, NULL))) {
-               return NULL;
-       }
-
-       if (!(audiohook_inheritance_datastore = ast_calloc(1, sizeof(*audiohook_inheritance_datastore)))) {
-               ast_datastore_free(datastore);
-               return NULL;
-       }
-
-       datastore->data = audiohook_inheritance_datastore;
-       ast_channel_lock(chan);
-       ast_channel_datastore_add(chan, datastore);
-       ast_channel_unlock(chan);
-       return audiohook_inheritance_datastore;
-}
-
-/*! \brief Create a new inheritable_audiohook structure and add it to an audiohook_inheritance_datastore
- *
- * \param audiohook_inheritance_datastore The audiohook_inheritance_datastore we want to add the new inheritable_audiohook to
- * \param source The audiohook source for the newly created inheritable_audiohook
- * \retval 0 Success
- * \retval non-zero Failure
- */
-static int setup_inheritable_audiohook(struct audiohook_inheritance_datastore *audiohook_inheritance_datastore, const char *source)
-{
-       struct inheritable_audiohook *inheritable_audiohook = NULL;
-
-       inheritable_audiohook = ast_calloc(1, sizeof(*inheritable_audiohook) + strlen(source));
-
-       if (!inheritable_audiohook) {
-               return -1;
-       }
-
-       strcpy(inheritable_audiohook->source, source);
-       AST_LIST_INSERT_TAIL(&audiohook_inheritance_datastore->allowed_list, inheritable_audiohook, list);
-       ast_debug(3, "Set audiohook %s to be inheritable\n", source);
-       return 0;
-}
-
-/*! \brief Set the permissibility of inheritance for a particular audiohook source on a channel
- *
- * For details regarding what happens in the function, see the inline comments
- *
- * \param chan The channel we are operating on
- * \param function The name of the dialplan function (AUDIOHOOK_INHERIT)
- * \param data The audiohook source for which we are setting inheritance permissions
- * \param value The value indicating the permission for audiohook inheritance
- */
 static int func_inheritance_write(struct ast_channel *chan, const char *function, char *data, const char *value)
 {
-       int allow;
-       struct ast_datastore *datastore = NULL;
-       struct audiohook_inheritance_datastore *inheritance_datastore = NULL;
-       struct inheritable_audiohook *inheritable_audiohook;
+       static int warned = 0;
 
-       /* Step 1: Get data from function call */
-       if (ast_strlen_zero(data)) {
-               ast_log(LOG_WARNING, "No argument provided to INHERITANCE function.\n");
-               return -1;
+       if (!warned) {
+               ast_log(LOG_NOTICE, "AUDIOHOOK_INHERIT is deprecated and now does nothing.\n");
+               warned++;
        }
 
-       if (ast_strlen_zero(value)) {
-               ast_log(LOG_WARNING, "No value provided to INHERITANCE function.\n");
-               return -1;
-       }
-
-       if (!chan) {
-               ast_log(LOG_WARNING, "No channel was provided to INHERITANCE function.\n");
-               return -1;
-       }
-
-       allow = ast_true(value);
-
-       /* Step 2: retrieve or set up datastore */
-       ast_channel_lock(chan);
-       if (!(datastore = ast_channel_datastore_find(chan, &audiohook_inheritance_info, NULL))) {
-               ast_channel_unlock(chan);
-               /* In the case where we cannot find the datastore, we can take a few shortcuts */
-               if (!allow) {
-                       ast_debug(1, "Audiohook %s is already set to not be inheritable on channel %s\n", data, ast_channel_name(chan));
-                       return 0;
-               } else if (!(inheritance_datastore = setup_inheritance_datastore(chan))) {
-                       ast_log(LOG_WARNING, "Unable to set up audiohook inheritance datastore on channel %s\n", ast_channel_name(chan));
-                       return -1;
-               } else {
-                       return setup_inheritable_audiohook(inheritance_datastore, data);
-               }
-       } else {
-               inheritance_datastore = datastore->data;
-       }
-       ast_channel_unlock(chan);
-
-       /* Step 3: Traverse the list to see if we're trying something redundant */
-
-       AST_LIST_TRAVERSE_SAFE_BEGIN(&inheritance_datastore->allowed_list, inheritable_audiohook, list) {
-               if (!strcasecmp(inheritable_audiohook->source, data)) {
-                       if (allow) {
-                               ast_debug(2, "Audiohook source %s is already set up to be inherited from channel %s\n", data, ast_channel_name(chan));
-                               return 0;
-                       } else {
-                               ast_debug(2, "Removing inheritability of audiohook %s from channel %s\n", data, ast_channel_name(chan));
-                               AST_LIST_REMOVE_CURRENT(list);
-                               ast_free(inheritable_audiohook);
-                               return 0;
-                       }
-               }
-       }
-       AST_LIST_TRAVERSE_SAFE_END;
-
-       /* Step 4: There is no step 4 */
-
-       /* Step 5: This means we are addressing an audiohook source which we have not encountered yet for the channel. Create a new inheritable
-        * audiohook structure if we're allowing inheritance, or just return if not
-        */
-
-       if (allow) {
-               return setup_inheritable_audiohook(inheritance_datastore, data);
-       } else {
-               ast_debug(1, "Audiohook %s is already set to not be inheritable on channel %s\n", data, ast_channel_name(chan));
-               return 0;
-       }
+       return 0;
 }
 
 static struct ast_custom_function inheritance_function = {
@@ -303,4 +86,4 @@ static int load_module(void)
                return AST_MODULE_LOAD_SUCCESS;
        }
 }
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Audiohook inheritance function");
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Audiohook inheritance placeholder function");
index 1a4e469..6b07160 100644 (file)
@@ -197,6 +197,17 @@ void ast_audiohook_detach_list(struct ast_audiohook_list *audiohook_list);
  */
 void ast_audiohook_move_by_source(struct ast_channel *old_chan, struct ast_channel *new_chan, const char *source);
 
+/*! \brief Move all audiohooks from one channel to another
+ *
+ * \note It is required that both old_chan and new_chan are locked prior to calling
+ * this function. Besides needing to protect the data within the channels, not locking
+ * these channels can lead to a potential deadlock.
+ *
+ * \param old_chan The source of the audiohooks being moved
+ * \param new_chan The destination channel for the audiohooks to be moved to
+ */
+void ast_audiohook_move_all(struct ast_channel *old_chan, struct ast_channel *new_chan);
+
 /*!
  * \brief Detach specified source audiohook from channel
  *
index f33927c..8a58323 100644 (file)
@@ -212,7 +212,19 @@ typedef void (*ast_framehook_destroy_callback)(void *data);
  */
 typedef int (*ast_framehook_consume_callback)(void *data, enum ast_frame_type type);
 
-#define AST_FRAMEHOOK_INTERFACE_VERSION 2
+/*!
+ * \brief This callback is called when a masquerade occurs on a channel with a framehook
+ * \since 12
+ *
+ * \param data, The data pointer provided at framehook initialization.
+ * \param framehook_id, The framehook ID where the framehook lives now
+ * \param old_chan, The channel that was masqueraded.
+ * \param new_chan, The channel that the masqueraded channel became.
+ */
+typedef void (*ast_framehook_chan_fixup_callback)(void *data, int framehook_id,
+       struct ast_channel *old_chan, struct ast_channel *new_chan);
+
+#define AST_FRAMEHOOK_INTERFACE_VERSION 3
 /*! This interface is required for attaching a framehook to a channel. */
 struct ast_framehook_interface {
        /*! framehook interface version number */
@@ -226,6 +238,13 @@ struct ast_framehook_interface {
        * frames of a specific type at this time. If this callback is not implemented it is assumed that the
        * framehook will consume frames of all types. */
        ast_framehook_consume_callback consume_cb;
+       /*! chan_fixup_cb is optional. This function is called when the channel that a framehook is running
+        * on is masqueraded and should be used to move any essential framehook data onto the channel the
+        * old channel was masqueraded to. */
+       ast_framehook_chan_fixup_callback chan_fixup_cb;
+       /*! disable_inheritance is optional. If set to non-zero, when a channel using this framehook is
+        * masqueraded, detach and destroy the framehook instead of moving it to the new channel. */
+       int disable_inheritance;
         /*! This pointer can represent any custom data to be stored on the !framehook. This
         * data pointer will be provided during each event callback which allows the framehook
         * to store any stateful data associated with the application using the hook. */
@@ -282,6 +301,18 @@ int ast_framehook_detach(struct ast_channel *chan, int framehook_id);
 int ast_framehook_list_destroy(struct ast_channel *chan);
 
 /*!
+ * \brief This is used by the channel API during a masquerade operation
+ * to move all mobile framehooks from the original channel to the clone channel.
+ * \since 12.5.0
+ *
+ * \pre Both channels must be locked prior to this function call.
+ *
+ * \param old_chan The channel being cloned from
+ * \param new_chan The channel being cloned to
+ */
+void ast_framehook_list_fixup(struct ast_channel *old_chan, struct ast_channel *new_chan);
+
+/*!
  * \brief This is used by the channel API push a frame read event to a channel's framehook list.
  * \since 1.8
  *
index 4d1ada6..b0a1a22 100644 (file)
@@ -181,6 +181,10 @@ struct ast_fax_session_details {
        int gateway_timeout;
        /*! the id of the faxdetect framehook for this channel */
        int faxdetect_id;
+       /*! The timeout for this fax detect in seconds */
+       int faxdetect_timeout;
+       /*! flags used for fax detection */
+       int faxdetect_flags;
 };
 
 struct ast_fax_tech;
index 4dc7c13..549ad31 100644 (file)
@@ -586,15 +586,10 @@ static struct ast_audiohook *find_audiohook_by_source(struct ast_audiohook_list
        return NULL;
 }
 
-void ast_audiohook_move_by_source(struct ast_channel *old_chan, struct ast_channel *new_chan, const char *source)
+static void audiohook_move(struct ast_channel *old_chan, struct ast_channel *new_chan, struct ast_audiohook *audiohook)
 {
-       struct ast_audiohook *audiohook;
        enum ast_audiohook_status oldstatus;
 
-       if (!ast_channel_audiohooks(old_chan) || !(audiohook = find_audiohook_by_source(ast_channel_audiohooks(old_chan), source))) {
-               return;
-       }
-
        /* By locking both channels and the audiohook, we can assure that
         * another thread will not have a chance to read the audiohook's status
         * as done, even though ast_audiohook_remove signals the trigger
@@ -610,6 +605,48 @@ void ast_audiohook_move_by_source(struct ast_channel *old_chan, struct ast_chann
        ast_audiohook_unlock(audiohook);
 }
 
+void ast_audiohook_move_by_source(struct ast_channel *old_chan, struct ast_channel *new_chan, const char *source)
+{
+       struct ast_audiohook *audiohook;
+
+       if (!ast_channel_audiohooks(old_chan)) {
+               return;
+       }
+
+       audiohook = find_audiohook_by_source(ast_channel_audiohooks(old_chan), source);
+       if (!audiohook) {
+               return;
+       }
+
+       audiohook_move(old_chan, new_chan, audiohook);
+}
+
+void ast_audiohook_move_all(struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+       struct ast_audiohook *audiohook;
+       struct ast_audiohook_list *audiohook_list;
+
+       audiohook_list = ast_channel_audiohooks(old_chan);
+       if (!audiohook_list) {
+               return;
+       }
+
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->spy_list, audiohook, list) {
+               audiohook_move(old_chan, new_chan, audiohook);
+       }
+       AST_LIST_TRAVERSE_SAFE_END;
+
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->whisper_list, audiohook, list) {
+               audiohook_move(old_chan, new_chan, audiohook);
+       }
+       AST_LIST_TRAVERSE_SAFE_END;
+
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) {
+               audiohook_move(old_chan, new_chan, audiohook);
+       }
+       AST_LIST_TRAVERSE_SAFE_END;
+}
+
 /*! \brief Detach specified source audiohook from channel
  * \param chan Channel to detach from
  * \param source Name of source to detach
index eb44867..60ce37a 100644 (file)
@@ -2858,6 +2858,7 @@ static int attach_framehook(struct attended_transfer_properties *props, struct a
                .event_cb = transfer_target_framehook_cb,
                .destroy_cb = transfer_target_framehook_destroy_cb,
                .consume_cb = transfer_target_framehook_consume,
+               .disable_inheritance = 1,
        };
 
        ao2_ref(props, +1);
index 7d9f048..15f2cbc 100644 (file)
@@ -6578,6 +6578,12 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
                AST_LIST_APPEND_LIST(ast_channel_datastores(original), ast_channel_datastores(clonechan), entry);
        }
 
+       /* Move framehooks over */
+       ast_framehook_list_fixup(clonechan, original);
+
+       /* Move audiohooks over */
+       ast_audiohook_move_all(clonechan, original);
+
        ast_autochan_new_channel(clonechan, original);
 
        clone_variables(original, clonechan);
@@ -10295,6 +10301,13 @@ struct suppress_data {
        int framehook_id;
 };
 
+static void suppress_framehook_fixup_cb(void *data, int framehook_id, struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+       struct suppress_data *suppress = data;
+
+       suppress->framehook_id = framehook_id;
+}
+
 static struct ast_frame *suppress_framehook_event_cb(struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data)
 {
        struct suppress_data *suppress = data;
@@ -10346,6 +10359,7 @@ int ast_channel_suppress(struct ast_channel *chan, unsigned int direction, enum
                .version = AST_FRAMEHOOK_INTERFACE_VERSION,
                .event_cb = suppress_framehook_event_cb,
                .destroy_cb = suppress_framehook_destroy_cb,
+               .chan_fixup_cb = suppress_framehook_fixup_cb,
        };
        int framehook_id;
 
index 84719ef..0d42b49 100644 (file)
@@ -56,7 +56,16 @@ struct ast_framehook_list {
        AST_LIST_HEAD_NOLOCK(, ast_framehook) list;
 };
 
-static void framehook_detach_and_destroy(struct ast_framehook *framehook)
+enum framehook_detachment_mode
+{
+       /*! Destroy the framehook outright. */
+       FRAMEHOOK_DETACH_DESTROY = 0,
+       /*! Remove the framehook from the channel, but don't destroy the data since
+        *  it will be used by a replacement framehook on another channel. */
+       FRAMEHOOK_DETACH_PRESERVE,
+};
+
+static void framehook_detach(struct ast_framehook *framehook, enum framehook_detachment_mode mode)
 {
        struct ast_frame *frame;
        frame = framehook->i.event_cb(framehook->chan, NULL, AST_FRAMEHOOK_EVENT_DETACHED, framehook->i.data);
@@ -67,7 +76,7 @@ static void framehook_detach_and_destroy(struct ast_framehook *framehook)
        }
        framehook->chan = NULL;
 
-       if (framehook->i.destroy_cb) {
+       if (mode == FRAMEHOOK_DETACH_DESTROY && framehook->i.destroy_cb) {
                framehook->i.destroy_cb(framehook->i.data);
        }
        ast_free(framehook);
@@ -96,7 +105,7 @@ static struct ast_frame *framehook_list_push_event(struct ast_framehook_list *fr
                        if (framehook->detach_and_destroy_me) {
                                /* this guy is signaled for destruction */
                                AST_LIST_REMOVE_CURRENT(list);
-                               framehook_detach_and_destroy(framehook);
+                               framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
                                continue;
                        }
 
@@ -205,7 +214,7 @@ int ast_framehook_list_destroy(struct ast_channel *chan)
        }
        AST_LIST_TRAVERSE_SAFE_BEGIN(&ast_channel_framehooks(chan)->list, framehook, list) {
                AST_LIST_REMOVE_CURRENT(list);
-               framehook_detach_and_destroy(framehook);
+               framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
        }
        AST_LIST_TRAVERSE_SAFE_END;
        ast_free(ast_channel_framehooks(chan));
@@ -213,6 +222,42 @@ int ast_framehook_list_destroy(struct ast_channel *chan)
        return 0;
 }
 
+void ast_framehook_list_fixup(struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+       struct ast_framehook *framehook;
+       int moved_framehook_id;
+
+       if (!ast_channel_framehooks(old_chan)) {
+               return;
+       }
+
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&ast_channel_framehooks(old_chan)->list, framehook, list) {
+               AST_LIST_REMOVE_CURRENT(list);
+
+               /* If inheritance is not allowed for this framehook, just destroy it. */
+               if (framehook->i.disable_inheritance) {
+                       framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
+                       continue;
+               }
+
+               /* Otherwise move it to the other channel and perform any fixups set by the framehook interface */
+               moved_framehook_id = ast_framehook_attach(new_chan, &framehook->i);
+
+               if (moved_framehook_id < 0) {
+                       ast_log(LOG_WARNING, "Failed framehook copy during masquerade. Expect loss of features.\n");
+                       framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
+               } else {
+                       if (framehook->i.chan_fixup_cb) {
+                               framehook->i.chan_fixup_cb(framehook->i.data, moved_framehook_id,
+                                       old_chan, new_chan);
+                       }
+
+                       framehook_detach(framehook, FRAMEHOOK_DETACH_PRESERVE);
+               }
+       }
+       AST_LIST_TRAVERSE_SAFE_END;
+}
+
 int ast_framehook_list_is_empty(struct ast_framehook_list *framehooks)
 {
        if (!framehooks) {
index b9a3b2e..7583098 100644 (file)
@@ -607,11 +607,47 @@ static void destroy_callback(void *data)
        }
 }
 
+static void fixup_callback(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
+
 static const struct ast_datastore_info fax_datastore = {
        .type = "res_fax",
        .destroy = destroy_callback,
+       .chan_fixup = fixup_callback,
 };
 
+static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_details *details);
+static int fax_detect_attach(struct ast_channel *chan, int timeout, int flags);
+static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan);
+
+/*! \brief Copies fax detection and gateway framehooks during masquerades
+ *
+ * \note must be called with both old_chan and new_chan locked. Since this
+ *       is only called by do_masquerade, that shouldn't be an issue.
+ */
+static void fixup_callback(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+       struct ast_fax_session_details *old_details = data;
+       struct ast_datastore *datastore = ast_channel_datastore_find(old_chan, &fax_datastore, NULL);
+
+       if (old_details->gateway_id >= 0) {
+               struct ast_fax_session_details *new_details = find_or_create_details(new_chan);
+
+               ast_framehook_detach(old_chan, old_details->gateway_id);
+               fax_gateway_attach(new_chan, new_details);
+               ao2_cleanup(new_details);
+       }
+
+       if (old_details->faxdetect_id >= 0) {
+               ast_framehook_detach(old_chan, old_details->faxdetect_id);
+               fax_detect_attach(new_chan, old_details->faxdetect_timeout, old_details->faxdetect_flags);
+       }
+
+       if (datastore) {
+               ast_channel_datastore_remove(old_chan, datastore);
+               ast_datastore_free(datastore);
+       }
+}
+
 /*! \brief returns a reference counted pointer to a fax datastore, if it exists */
 static struct ast_fax_session_details *find_details(struct ast_channel *chan)
 {
@@ -3431,6 +3467,7 @@ static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_d
                .version = AST_FRAMEHOOK_INTERFACE_VERSION,
                .event_cb = fax_gateway_framehook,
                .destroy_cb = fax_gateway_framehook_destroy,
+               .disable_inheritance = 1, /* Masquerade inheritance is handled through the datastore fixup */
        };
 
        ast_string_field_set(details, result, "SUCCESS");
@@ -3700,6 +3737,8 @@ static int fax_detect_attach(struct ast_channel *chan, int timeout, int flags)
        faxdetect->details = details;
        ast_channel_lock(chan);
        details->faxdetect_id = ast_framehook_attach(chan, &fr_hook);
+       details->faxdetect_timeout = timeout;
+       details->faxdetect_flags = flags;
        ast_channel_unlock(chan);
 
        if (details->faxdetect_id < 0) {
index efcee67..b88396f 100644 (file)
@@ -508,6 +508,7 @@ static void refer_blind_callback(struct ast_channel *chan, struct transfer_chann
                        .event_cb = refer_progress_framehook,
                        .destroy_cb = refer_progress_framehook_destroy,
                        .data = refer->progress,
+                       .disable_inheritance = 1,
                };
 
                refer->progress->transferee = ast_strdup(ast_channel_uniqueid(chan));