Restore Dial, Queue, and FollowMe 'I' option support.
[asterisk/asterisk.git] / main / core_unreal.c
index 71d0f6c..7e457f4 100644 (file)
@@ -39,7 +39,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/pbx.h"
 #include "asterisk/musiconhold.h"
 #include "asterisk/astobj2.h"
-#include "asterisk/bridging.h"
+#include "asterisk/bridge.h"
 #include "asterisk/core_unreal.h"
 
 static unsigned int name_sequence = 0;
@@ -441,10 +441,18 @@ static int unreal_queue_indicate(struct ast_unreal_pvt *p, struct ast_channel *a
  */
 static int unreal_colp_redirect_indicate(struct ast_unreal_pvt *p, struct ast_channel *ast, int condition)
 {
+       struct ast_channel *my_chan;
+       struct ast_channel *my_owner;
        struct ast_channel *this_channel;
        struct ast_channel *the_other_channel;
        int isoutbound;
        int res = 0;
+       unsigned char frame_data[1024];
+       struct ast_frame f = {
+               .frametype = AST_FRAME_CONTROL,
+               .subclass.integer = condition,
+               .data.ptr = frame_data,
+       };
 
        /*
         * A connected line update frame may only contain a partial
@@ -458,7 +466,8 @@ static int unreal_colp_redirect_indicate(struct ast_unreal_pvt *p, struct ast_ch
         * redirecting information, which is why it is handled here as
         * well.
         */
-       ao2_lock(p);
+       ast_channel_unlock(ast);
+       ast_unreal_lock_all(p, &my_chan, &my_owner);
        isoutbound = AST_UNREAL_IS_OUTBOUND(ast, p);
        if (isoutbound) {
                this_channel = p->chan;
@@ -468,13 +477,6 @@ static int unreal_colp_redirect_indicate(struct ast_unreal_pvt *p, struct ast_ch
                the_other_channel = p->chan;
        }
        if (the_other_channel) {
-               unsigned char frame_data[1024];
-               struct ast_frame f = {
-                       .frametype = AST_FRAME_CONTROL,
-                       .subclass.integer = condition,
-                       .data.ptr = frame_data,
-               };
-
                if (condition == AST_CONTROL_CONNECTED_LINE) {
                        ast_connected_line_copy_to_caller(ast_channel_caller(the_other_channel),
                                ast_channel_connected(this_channel));
@@ -484,9 +486,20 @@ static int unreal_colp_redirect_indicate(struct ast_unreal_pvt *p, struct ast_ch
                        f.datalen = ast_redirecting_build_data(frame_data, sizeof(frame_data),
                                ast_channel_redirecting(this_channel), NULL);
                }
-               res = unreal_queue_frame(p, isoutbound, &f, ast, 1);
+       }
+       if (my_chan) {
+               ast_channel_unlock(my_chan);
+               ast_channel_unref(my_chan);
+       }
+       if (my_owner) {
+               ast_channel_unlock(my_owner);
+               ast_channel_unref(my_owner);
+       }
+       if (the_other_channel) {
+               res = unreal_queue_frame(p, isoutbound, &f, ast, 0);
        }
        ao2_unlock(p);
+       ast_channel_lock(ast);
 
        return res;
 }
@@ -668,6 +681,98 @@ void ast_unreal_call_setup(struct ast_channel *semi1, struct ast_channel *semi2)
        ast_channel_datastore_inherit(semi1, semi2);
 }
 
+int ast_unreal_channel_push_to_bridge(struct ast_channel *ast, struct ast_bridge *bridge, unsigned int flags)
+{
+       struct ast_bridge_features *features;
+       struct ast_channel *chan;
+       struct ast_channel *owner;
+       RAII_VAR(struct ast_unreal_pvt *, p, NULL, ao2_cleanup);
+
+       RAII_VAR(struct ast_callid *, bridge_callid, NULL, ast_callid_cleanup);
+
+       ast_bridge_lock(bridge);
+       bridge_callid = bridge->callid ? ast_callid_ref(bridge->callid) : NULL;
+       ast_bridge_unlock(bridge);
+
+       {
+               SCOPED_CHANNELLOCK(lock, ast);
+               p = ast_channel_tech_pvt(ast);
+               if (!p) {
+                       return -1;
+               }
+               ao2_ref(p, +1);
+       }
+
+       {
+               SCOPED_AO2LOCK(lock, p);
+               chan = p->chan;
+               if (!chan) {
+                       return -1;
+               }
+
+               owner = p->owner;
+               if (!owner) {
+                       return -1;
+               }
+
+               ast_channel_ref(chan);
+               ast_channel_ref(owner);
+       }
+
+       if (bridge_callid) {
+               struct ast_callid *chan_callid;
+               struct ast_callid *owner_callid;
+
+               /* chan side call ID setting */
+               ast_channel_lock(chan);
+
+               chan_callid = ast_channel_callid(chan);
+               if (!chan_callid) {
+                       ast_channel_callid_set(chan, bridge_callid);
+               }
+               ast_channel_unlock(chan);
+               ast_callid_cleanup(chan_callid);
+
+               /* owner side call ID setting */
+               ast_channel_lock(owner);
+
+               owner_callid = ast_channel_callid(owner);
+               if (!owner_callid) {
+                       ast_channel_callid_set(owner, bridge_callid);
+               }
+
+               ast_channel_unlock(owner);
+               ast_callid_cleanup(owner_callid);
+       }
+
+       /* We are done with the owner now that its call ID matches the bridge */
+       ast_channel_unref(owner);
+       owner = NULL;
+
+       features = ast_bridge_features_new();
+       if (!features) {
+               ast_channel_unref(chan);
+               return -1;
+       }
+
+       ast_set_flag(&features->feature_flags, flags);
+
+       /* Impart the semi2 channel into the bridge */
+       if (ast_bridge_impart(bridge, chan, NULL, features,
+               AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
+               ast_bridge_features_destroy(features);
+               ast_channel_unref(chan);
+               return -1;
+       }
+
+       ao2_lock(p);
+       ast_set_flag(p, AST_UNREAL_CARETAKER_THREAD);
+       ao2_unlock(p);
+       ast_channel_unref(chan);
+
+       return 0;
+}
+
 int ast_unreal_hangup(struct ast_unreal_pvt *p, struct ast_channel *ast)
 {
        int hangup_chan = 0;