Merge "stasis: No need to keep a stasis type ref in a stasis msg or cache object."
[asterisk/asterisk.git] / main / framehook.c
index de8c612..d17066d 100644 (file)
@@ -29,8 +29,6 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #include "asterisk/channel.h"
 #include "asterisk/linkedlists.h"
 #include "asterisk/framehook.h"
@@ -56,7 +54,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 +74,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);
@@ -85,7 +92,7 @@ static struct ast_frame *framehook_list_push_event(struct ast_framehook_list *fr
        }
 
        skip_size = sizeof(int) * framehooks->count;
-       skip = alloca(skip_size);
+       skip = ast_alloca(skip_size);
        memset(skip, 0, skip_size);
 
        do {
@@ -96,7 +103,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;
                        }
 
@@ -128,7 +135,7 @@ int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interfac
        struct ast_framehook_list *fh_list;
        struct ast_frame *frame;
        if (i->version != AST_FRAMEHOOK_INTERFACE_VERSION) {
-               ast_log(LOG_ERROR, "Version '%hu' of framehook interface not what we compiled against (%hu)\n",
+               ast_log(LOG_ERROR, "Version '%hu' of framehook interface not what we compiled against (%i)\n",
                        i->version, AST_FRAMEHOOK_INTERFACE_VERSION);
                return -1;
        }
@@ -161,7 +168,7 @@ int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interfac
        }
 
        if (ast_channel_is_bridged(chan)) {
-               ast_softhangup_nolock(chan, AST_SOFTHANGUP_UNBRIDGE);
+               ast_channel_set_unbridged_nolock(chan, 1);
        }
 
        return framehook->id;
@@ -189,6 +196,10 @@ int ast_framehook_detach(struct ast_channel *chan, int id)
        }
        AST_LIST_TRAVERSE_SAFE_END;
 
+       if (!res && ast_channel_is_bridged(chan)) {
+               ast_channel_set_unbridged_nolock(chan, 1);
+       }
+
        return res;
 }
 
@@ -201,7 +212,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));
@@ -209,6 +220,57 @@ 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(new_chan)) {
+               AST_LIST_TRAVERSE_SAFE_BEGIN(&ast_channel_framehooks(new_chan)->list, framehook, list) {
+                       if (framehook->i.disable_inheritance) {
+                               ast_framehook_detach(new_chan, framehook->id);
+                               continue;
+                       }
+
+                       if (framehook->i.chan_breakdown_cb) {
+                               framehook->i.chan_breakdown_cb(framehook->i.data, framehook->id,
+                                       old_chan, new_chan);
+                       }
+               }
+               AST_LIST_TRAVERSE_SAFE_END;
+       }
+
+       if (!ast_channel_framehooks(old_chan)) {
+               return;
+       }
+
+       if (!AST_LIST_EMPTY(&ast_channel_framehooks(old_chan)->list)
+               && ast_channel_is_bridged(old_chan)) {
+               ast_channel_set_unbridged_nolock(old_chan, 1);
+       }
+       while ((framehook = AST_LIST_REMOVE_HEAD(&ast_channel_framehooks(old_chan)->list, 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);
+               }
+       }
+}
+
 int ast_framehook_list_is_empty(struct ast_framehook_list *framehooks)
 {
        if (!framehooks) {