Clean-up on isle five for __ast_request_and_dial() and ast_call_forward().
[asterisk/asterisk.git] / main / channel.c
index 54a102d..017fba9 100644 (file)
@@ -5389,11 +5389,47 @@ static void handle_cause(int cause, int *outstate)
        }
 }
 
+/*!
+ * \internal
+ * \brief Helper function to inherit info from parent channel.
+ *
+ * \param new_chan Channel inheriting information.
+ * \param parent Channel new_chan inherits information.
+ * \param orig Channel being replaced by the call forward channel.
+ *
+ * \return Nothing
+ */
+static void call_forward_inherit(struct ast_channel *new_chan, struct ast_channel *parent, struct ast_channel *orig)
+{
+       if (!ast_test_flag(parent, AST_FLAG_ZOMBIE) && !ast_check_hangup(parent)) {
+               struct ast_party_redirecting redirecting;
+
+               /*
+                * The parent is not a ZOMBIE or hungup so update it with the
+                * original channel's redirecting information.
+                */
+               ast_party_redirecting_init(&redirecting);
+               ast_channel_lock(orig);
+               ast_party_redirecting_copy(&redirecting, &orig->redirecting);
+               ast_channel_unlock(orig);
+               if (ast_channel_redirecting_macro(orig, parent, &redirecting, 1, 0)) {
+                       ast_channel_update_redirecting(parent, &redirecting, NULL);
+               }
+               ast_party_redirecting_free(&redirecting);
+       }
+
+       /* Safely inherit variables and datastores from the parent channel. */
+       ast_channel_lock_both(parent, new_chan);
+       ast_channel_inherit_variables(parent, new_chan);
+       ast_channel_datastore_inherit(parent, new_chan);
+       ast_channel_unlock(new_chan);
+       ast_channel_unlock(parent);
+}
+
 struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_channel *orig, int *timeout, struct ast_format_cap *cap, struct outgoing_helper *oh, int *outstate)
 {
        char tmpchan[256];
-       struct ast_channel *new = NULL;
-       struct ast_party_redirecting *apr = &orig->redirecting;
+       struct ast_channel *new_chan = NULL;
        char *data, *type;
        int cause = 0;
        int res;
@@ -5412,64 +5448,52 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan
                data = tmpchan;
                type = "Local";
        }
-       if (!(new = ast_request(type, cap, orig, data, &cause))) {
+       if (!(new_chan = ast_request(type, cap, orig, data, &cause))) {
                ast_log(LOG_NOTICE, "Unable to create channel for call forward to '%s/%s' (cause = %d)\n", type, data, cause);
                handle_cause(cause, outstate);
                ast_hangup(orig);
                return NULL;
        }
 
-       ast_channel_set_redirecting(new, apr, NULL);
-
        /* Copy/inherit important information into new channel */
        if (oh) {
                if (oh->vars) {
-                       ast_set_variables(new, oh->vars);
-               }
-               if (!ast_strlen_zero(oh->cid_num) && !ast_strlen_zero(oh->cid_name)) {
-                       ast_set_callerid(new, oh->cid_num, oh->cid_name, oh->cid_num);
+                       ast_set_variables(new_chan, oh->vars);
                }
                if (oh->parent_channel) {
-                       ast_channel_update_redirecting(oh->parent_channel, apr, NULL);
-                       ast_channel_inherit_variables(oh->parent_channel, new);
-                       ast_channel_datastore_inherit(oh->parent_channel, new);
+                       call_forward_inherit(new_chan, oh->parent_channel, orig);
                }
                if (oh->account) {
-                       ast_channel_lock(new);
-                       ast_cdr_setaccount(new, oh->account);
-                       ast_channel_unlock(new);
+                       ast_channel_lock(new_chan);
+                       ast_cdr_setaccount(new_chan, oh->account);
+                       ast_channel_unlock(new_chan);
                }
        } else if (caller) { /* no outgoing helper so use caller if avaliable */
-               ast_channel_update_redirecting(caller, apr, NULL);
-               ast_channel_inherit_variables(caller, new);
-               ast_channel_datastore_inherit(caller, new);
+               call_forward_inherit(new_chan, caller, orig);
        }
 
-       ast_channel_lock(orig);
-       while (ast_channel_trylock(new)) {
-               CHANNEL_DEADLOCK_AVOIDANCE(orig);
-       }
-       ast_copy_flags(new->cdr, orig->cdr, AST_CDR_FLAG_ORIGINATED);
-       ast_string_field_set(new, accountcode, orig->accountcode);
-       ast_party_caller_copy(&new->caller, &orig->caller);
-       ast_party_connected_line_copy(&new->connected, &orig->connected);
-       ast_channel_unlock(new);
+       ast_channel_lock_both(orig, new_chan);
+       ast_copy_flags(new_chan->cdr, orig->cdr, AST_CDR_FLAG_ORIGINATED);
+       ast_string_field_set(new_chan, accountcode, orig->accountcode);
+       ast_party_connected_line_copy(&new_chan->connected, &orig->connected);
+       ast_party_redirecting_copy(&new_chan->redirecting, &orig->redirecting);
+       ast_channel_unlock(new_chan);
        ast_channel_unlock(orig);
 
        /* call new channel */
-       res = ast_call(new, data, 0);
+       res = ast_call(new_chan, data, 0);
        if (timeout) {
                *timeout = res;
        }
        if (res) {
                ast_log(LOG_NOTICE, "Unable to call forward to channel %s/%s\n", type, (char *)data);
                ast_hangup(orig);
-               ast_hangup(new);
+               ast_hangup(new_chan);
                return NULL;
        }
        ast_hangup(orig);
 
-       return new;
+       return new_chan;
 }
 
 struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
@@ -5494,14 +5518,24 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
        }
 
        if (oh) {
-               if (oh->vars)   
+               if (oh->vars) {
                        ast_set_variables(chan, oh->vars);
-               /* XXX why is this necessary, for the parent_channel perhaps ? */
-               if (!ast_strlen_zero(oh->cid_num) && !ast_strlen_zero(oh->cid_name))
-                       ast_set_callerid(chan, oh->cid_num, oh->cid_name, oh->cid_num);
+               }
+               if (!ast_strlen_zero(oh->cid_num) && !ast_strlen_zero(oh->cid_name)) {
+                       /*
+                        * Use the oh values instead of the function parameters for the
+                        * outgoing CallerID.
+                        */
+                       cid_num = oh->cid_num;
+                       cid_name = oh->cid_name;
+               }
                if (oh->parent_channel) {
+                       /* Safely inherit variables and datastores from the parent channel. */
+                       ast_channel_lock_both(oh->parent_channel, chan);
                        ast_channel_inherit_variables(oh->parent_channel, chan);
                        ast_channel_datastore_inherit(oh->parent_channel, chan);
+                       ast_channel_unlock(oh->parent_channel);
+                       ast_channel_unlock(chan);
                }
                if (oh->account) {
                        ast_channel_lock(chan);
@@ -5510,7 +5544,6 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c
                }
        }
 
-       ast_set_callerid(chan, cid_num, cid_name, cid_num);
        ast_set_flag(chan->cdr, AST_CDR_FLAG_ORIGINATED);
        ast_party_connected_line_set_init(&connected, &chan->connected);
        if (cid_num) {