summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Rose <jrose@digium.com>2014-07-18 16:28:10 +0000
committerJonathan Rose <jrose@digium.com>2014-07-18 16:28:10 +0000
commitaf4cd651431b5a2a0dadebf4331be4fe61389e1b (patch)
tree989983f58c5a8ebca0b7588c9592a87f26db23db
parent5c988cc4e6c5693f03080f88e3057cb7a5358597 (diff)
Channels: Masquerades to automatically move frame/audio hooks
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
-rw-r--r--CHANGES5
-rw-r--r--bridges/bridge_native_rtp.c1
-rw-r--r--funcs/func_audiohookinherit.c251
-rw-r--r--include/asterisk/audiohook.h11
-rw-r--r--include/asterisk/framehook.h33
-rw-r--r--include/asterisk/res_fax.h4
-rw-r--r--main/audiohook.c49
-rw-r--r--main/bridge_basic.c1
-rw-r--r--main/channel.c14
-rw-r--r--main/framehook.c53
-rw-r--r--res/res_fax.c39
-rw-r--r--res/res_pjsip_refer.c1
12 files changed, 217 insertions, 245 deletions
diff --git a/CHANGES b/CHANGES
index 7f1e98f16..b992edf4c 100644
--- 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 ------------
diff --git a/bridges/bridge_native_rtp.c b/bridges/bridge_native_rtp.c
index 725a6f67f..2362ad296 100644
--- a/bridges/bridge_native_rtp.c
+++ b/bridges/bridge_native_rtp.c
@@ -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) {
diff --git a/funcs/func_audiohookinherit.c b/funcs/func_audiohookinherit.c
index ea5c5e9ab..28a2a4550 100644
--- a/funcs/func_audiohookinherit.c
+++ b/funcs/func_audiohookinherit.c
@@ -29,260 +29,43 @@
*/
/*** 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");
diff --git a/include/asterisk/audiohook.h b/include/asterisk/audiohook.h
index 1a4e4696a..6b0716092 100644
--- a/include/asterisk/audiohook.h
+++ b/include/asterisk/audiohook.h
@@ -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
*
diff --git a/include/asterisk/framehook.h b/include/asterisk/framehook.h
index f33927c7b..8a58323f2 100644
--- a/include/asterisk/framehook.h
+++ b/include/asterisk/framehook.h
@@ -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
*
diff --git a/include/asterisk/res_fax.h b/include/asterisk/res_fax.h
index 4d1ada6ef..b0a1a221b 100644
--- a/include/asterisk/res_fax.h
+++ b/include/asterisk/res_fax.h
@@ -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;
diff --git a/main/audiohook.c b/main/audiohook.c
index 4dc7c136b..549ad31eb 100644
--- a/main/audiohook.c
+++ b/main/audiohook.c
@@ -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
diff --git a/main/bridge_basic.c b/main/bridge_basic.c
index eb44867f1..60ce37a98 100644
--- a/main/bridge_basic.c
+++ b/main/bridge_basic.c
@@ -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);
diff --git a/main/channel.c b/main/channel.c
index 7d9f04837..15f2cbce0 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -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;
diff --git a/main/framehook.c b/main/framehook.c
index 84719ef49..0d42b4990 100644
--- a/main/framehook.c
+++ b/main/framehook.c
@@ -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) {
diff --git a/res/res_fax.c b/res/res_fax.c
index b9a3b2ec4..758309822 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -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) {
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index efcee67a2..b88396f7a 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -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));