Merge "stasis: No need to keep a stasis type ref in a stasis msg or cache object."
[asterisk/asterisk.git] / main / framehook.c
index f85a4c2..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"
@@ -49,11 +47,23 @@ struct ast_framehook {
 };
 
 struct ast_framehook_list {
+       /*! the number of hooks currently present */
+       unsigned int count;
+       /*! id for next framehook added */
        unsigned int id_count;
        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);
@@ -64,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);
@@ -73,21 +83,49 @@ static void framehook_detach_and_destroy(struct ast_framehook *framehook)
 static struct ast_frame *framehook_list_push_event(struct ast_framehook_list *framehooks, struct ast_frame *frame, enum ast_framehook_event event)
 {
        struct ast_framehook *framehook;
+       struct ast_frame *original_frame;
+       int *skip;
+       size_t skip_size;
 
        if (!framehooks) {
                return frame;
        }
 
-       AST_LIST_TRAVERSE_SAFE_BEGIN(&framehooks->list, framehook, list) {
-               if (framehook->detach_and_destroy_me) {
-                       /* this guy is signaled for destruction */
-                       AST_LIST_REMOVE_CURRENT(list);
-                       framehook_detach_and_destroy(framehook);
-               } else {
+       skip_size = sizeof(int) * framehooks->count;
+       skip = ast_alloca(skip_size);
+       memset(skip, 0, skip_size);
+
+       do {
+               unsigned int num = 0;
+               original_frame = frame;
+
+               AST_LIST_TRAVERSE_SAFE_BEGIN(&framehooks->list, framehook, list) {
+                       if (framehook->detach_and_destroy_me) {
+                               /* this guy is signaled for destruction */
+                               AST_LIST_REMOVE_CURRENT(list);
+                               framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
+                               continue;
+                       }
+
+                       /* If this framehook has been marked as needing to be skipped, do so */
+                       if (skip[num]) {
+                               num++;
+                               continue;
+                       }
+
                        frame = framehook->i.event_cb(framehook->chan, frame, event, framehook->i.data);
+
+                       if (frame != original_frame) {
+                               /* To prevent looping we skip any framehooks that have already provided a modified frame */
+                               skip[num] = 1;
+                               break;
+                       }
+
+                       num++;
                }
-       }
-       AST_LIST_TRAVERSE_SAFE_END;
+               AST_LIST_TRAVERSE_SAFE_END;
+       } while (frame != original_frame);
+
        return frame;
 }
 
@@ -97,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;
        }
@@ -116,6 +154,7 @@ int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interfac
                ast_channel_framehooks_set(chan, fh_list);
        }
 
+       ast_channel_framehooks(chan)->count++;
        framehook->id = ++ast_channel_framehooks(chan)->id_count;
        AST_LIST_INSERT_TAIL(&ast_channel_framehooks(chan)->list, framehook, list);
 
@@ -128,6 +167,10 @@ int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interfac
                ast_frfree(frame);
        }
 
+       if (ast_channel_is_bridged(chan)) {
+               ast_channel_set_unbridged_nolock(chan, 1);
+       }
+
        return framehook->id;
 }
 
@@ -153,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;
 }
 
@@ -165,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));
@@ -173,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) {
@@ -183,6 +281,12 @@ int ast_framehook_list_is_empty(struct ast_framehook_list *framehooks)
 
 int ast_framehook_list_contains_no_active(struct ast_framehook_list *framehooks)
 {
+       return ast_framehook_list_contains_no_active_of_type(framehooks, 0);
+}
+
+int ast_framehook_list_contains_no_active_of_type(struct ast_framehook_list *framehooks,
+       enum ast_frame_type type)
+{
        struct ast_framehook *cur;
 
        if (!framehooks) {
@@ -197,6 +301,9 @@ int ast_framehook_list_contains_no_active(struct ast_framehook_list *framehooks)
                if (cur->detach_and_destroy_me) {
                        continue;
                }
+               if (type && cur->i.consume_cb && !cur->i.consume_cb(cur->i.data, type)) {
+                       continue;
+               }
                return 0;
        }