PreDial - Ability to run dialplan on callee and caller channels before Dial.
authorRichard Mudgett <rmudgett@digium.com>
Sat, 28 Apr 2012 00:31:47 +0000 (00:31 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Sat, 28 Apr 2012 00:31:47 +0000 (00:31 +0000)
Thanks to Mark Murawski for the initial patch and feature definition.

(closes issue ASTERISK-19548)
Reported by: Mark Murawski

Review: https://reviewboard.asterisk.org/r/1878/
Review: https://reviewboard.asterisk.org/r/1229/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@364436 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
apps/app_dial.c

diff --git a/CHANGES b/CHANGES
index 1887148..42e2bac 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -141,6 +141,8 @@ Applications
    be supplied with arguments indicating where the callee should go after the caller
    is hung up, or without options specified, the priority after the Queue/Bridge
    will be used.
+ * Added 'b' and 'B' options to Dial that execute a Gosub on callee and caller
+   channels respectively before the callee channels are called.
 
 Parking
 ------------
index 875e943..72a38ce 100644 (file)
@@ -108,6 +108,27 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                        channel before doing anything on the called channel. You will rarely need to use
                                        this option, the default behavior is adequate in most cases.</para>
                                </option>
+                               <option name="b" argsep="^">
+                                       <para>Before initiating the actual call, Gosub to the specified
+                                       location using the newly created channel(s).  The Gosub will be
+                                       executed for each destination channel.</para>
+                                       <argument name="context" required="false" />
+                                       <argument name="exten" required="false" />
+                                       <argument name="priority" required="true" hasparams="optional" argsep="^">
+                                               <argument name="arg1" multiple="true" required="true" />
+                                               <argument name="argN" />
+                                       </argument>
+                               </option>
+                               <option name="B" argsep="^">
+                                       <para>Before initiating the actual call, Gosub to the specified
+                                       location using the current channel.</para>
+                                       <argument name="context" required="false" />
+                                       <argument name="exten" required="false" />
+                                       <argument name="priority" required="true" hasparams="optional" argsep="^">
+                                               <argument name="arg1" multiple="true" required="true" />
+                                               <argument name="argN" />
+                                       </argument>
+                               </option>
                                <option name="C">
                                        <para>Reset the call detail record (CDR) for this call.</para>
                                </option>
@@ -587,6 +608,8 @@ enum {
 #define OPT_FORCE_CID_TAG    (1LLU << 38)
 #define OPT_FORCE_CID_PRES   (1LLU << 39)
 #define OPT_CALLER_ANSWER    (1LLU << 40)
+#define OPT_PREDIAL_CALLEE   (1LLU << 41)
+#define OPT_PREDIAL_CALLER   (1LLU << 42)
 
 enum {
        OPT_ARG_ANNOUNCE = 0,
@@ -606,6 +629,8 @@ enum {
        OPT_ARG_FORCECLID,
        OPT_ARG_FORCE_CID_TAG,
        OPT_ARG_FORCE_CID_PRES,
+       OPT_ARG_PREDIAL_CALLEE,
+       OPT_ARG_PREDIAL_CALLER,
        /* note: this entry _MUST_ be the last one in the enum */
        OPT_ARG_ARRAY_SIZE,
 };
@@ -613,6 +638,8 @@ enum {
 AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
        AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE),
        AST_APP_OPTION('a', OPT_CALLER_ANSWER),
+       AST_APP_OPTION_ARG('b', OPT_PREDIAL_CALLEE, OPT_ARG_PREDIAL_CALLEE),
+       AST_APP_OPTION_ARG('B', OPT_PREDIAL_CALLER, OPT_ARG_PREDIAL_CALLER),
        AST_APP_OPTION('C', OPT_RESETCDR),
        AST_APP_OPTION('c', OPT_CANCEL_ELSEWHERE),
        AST_APP_OPTION('d', OPT_DTMF_EXIT),
@@ -640,9 +667,9 @@ AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
        AST_APP_OPTION_ARG('r', OPT_RINGBACK, OPT_ARG_RINGBACK),
        AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP),
        AST_APP_OPTION_ARG('s', OPT_FORCE_CID_TAG, OPT_ARG_FORCE_CID_TAG),
-       AST_APP_OPTION_ARG('u', OPT_FORCE_CID_PRES, OPT_ARG_FORCE_CID_PRES),
        AST_APP_OPTION('t', OPT_CALLEE_TRANSFER),
        AST_APP_OPTION('T', OPT_CALLER_TRANSFER),
+       AST_APP_OPTION_ARG('u', OPT_FORCE_CID_PRES, OPT_ARG_FORCE_CID_PRES),
        AST_APP_OPTION_ARG('U', OPT_CALLEE_GOSUB, OPT_ARG_CALLEE_GOSUB),
        AST_APP_OPTION('w', OPT_CALLEE_MONITOR),
        AST_APP_OPTION('W', OPT_CALLER_MONITOR),
@@ -662,16 +689,26 @@ END_OPTIONS );
  * The list of active channels
  */
 struct chanlist {
-       struct chanlist *next;
+       AST_LIST_ENTRY(chanlist) node;
        struct ast_channel *chan;
+       /*! Channel interface dialing string (is tech/number).  (Stored in stuff[]) */
+       const char *interface;
+       /*! Channel technology name.  (Stored in stuff[]) */
+       const char *tech;
+       /*! Channel device addressing.  (Stored in stuff[]) */
+       const char *number;
        uint64_t flags;
        /*! Saved connected party info from an AST_CONTROL_CONNECTED_LINE. */
        struct ast_party_connected_line connected;
        /*! TRUE if an AST_CONTROL_CONNECTED_LINE update was saved to the connected element. */
        unsigned int pending_connected_update:1;
        struct ast_aoc_decoded *aoc_s_rate_list;
+       /*! The interface, tech, and number strings are stuffed here. */
+       char stuff[0];
 };
 
+AST_LIST_HEAD_NOLOCK(dial_head, chanlist);
+
 static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str *featurecode);
 
 static void chanlist_free(struct chanlist *outgoing)
@@ -681,11 +718,12 @@ static void chanlist_free(struct chanlist *outgoing)
        ast_free(outgoing);
 }
 
-static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception, int answered_elsewhere)
+static void hanguptree(struct dial_head *out_chans, struct ast_channel *exception, int answered_elsewhere)
 {
        /* Hang up a tree of stuff */
-       struct chanlist *oo;
-       while (outgoing) {
+       struct chanlist *outgoing;
+
+       while ((outgoing = AST_LIST_REMOVE_HEAD(out_chans, node))) {
                /* Hangup any existing lines we have open */
                if (outgoing->chan && (outgoing->chan != exception)) {
                        if (answered_elsewhere) {
@@ -696,9 +734,7 @@ static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception,
                        }
                        ast_hangup(outgoing->chan);
                }
-               oo = outgoing;
-               outgoing = outgoing->next;
-               chanlist_free(oo);
+               chanlist_free(outgoing);
        }
 }
 
@@ -1004,7 +1040,7 @@ struct privacy_args {
 };
 
 static struct ast_channel *wait_for_answer(struct ast_channel *in,
-       struct chanlist *outgoing, int *to, struct ast_flags64 *peerflags,
+       struct dial_head *out_chans, int *to, struct ast_flags64 *peerflags,
        char *opt_args[],
        struct privacy_args *pa,
        const struct cause_args *num_in, int *result, char *dtmf_progress,
@@ -1015,13 +1051,14 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
        int prestart = num.busy + num.congestion + num.nochan;
        int orig = *to;
        struct ast_channel *peer = NULL;
-       /* single is set if only one destination is enabled */
-       int single = outgoing && !outgoing->next;
-       int caller_entertained = outgoing
-               && ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
 #ifdef HAVE_EPOLL
        struct chanlist *epollo;
 #endif
+       struct chanlist *outgoing = AST_LIST_FIRST(out_chans);
+       /* single is set if only one destination is enabled */
+       int single = outgoing && !AST_LIST_NEXT(outgoing, node);
+       int caller_entertained = outgoing
+               && ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
        struct ast_party_connected_line connected_caller;
        struct ast_str *featurecode = ast_str_alloca(FEATURE_MAX_LEN + 1);
        int cc_recall_core_id;
@@ -1064,8 +1101,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
        is_cc_recall = ast_cc_is_recall(in, &cc_recall_core_id, NULL);
 
 #ifdef HAVE_EPOLL
-       for (epollo = outgoing; epollo; epollo = epollo->next)
+       AST_LIST_TRAVERSE(out_chans, epollo, node) {
                ast_poll_channel_add(in, epollo->chan);
+       }
 #endif
 
        while (*to && !peer) {
@@ -1076,7 +1114,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                struct ast_channel *watchers[AST_MAX_WATCHERS];
 
                watchers[pos++] = in;
-               for (o = outgoing; o; o = o->next) {
+               AST_LIST_TRAVERSE(out_chans, o, node) {
                        /* Keep track of important channels */
                        if (ast_test_flag64(o, DIAL_STILLGOING) && o->chan)
                                watchers[pos++] = o->chan;
@@ -1101,7 +1139,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                        return NULL;
                }
                winner = ast_waitfor_n(watchers, pos, to);
-               for (o = outgoing; o; o = o->next) {
+               AST_LIST_TRAVERSE(out_chans, o, node) {
                        struct ast_frame *f;
                        struct ast_channel *c = o->chan;
 
@@ -1486,7 +1524,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                        }
 
                        /* Send the frame from the in channel to all outgoing channels. */
-                       for (o = outgoing; o; o = o->next) {
+                       AST_LIST_TRAVERSE(out_chans, o, node) {
                                if (!o->chan || !ast_test_flag64(o, DIAL_STILLGOING)) {
                                        /* This outgoing channel has died so don't send the frame to it. */
                                        continue;
@@ -1575,7 +1613,7 @@ skip_frame:;
        }
 
 #ifdef HAVE_EPOLL
-       for (epollo = outgoing; epollo; epollo = epollo->next) {
+       AST_LIST_TRAVERSE(out_chans, epollo, node) {
                if (epollo->chan)
                        ast_poll_channel_del(in, epollo->chan);
        }
@@ -1918,12 +1956,13 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 {
        int res = -1; /* default: error */
        char *rest, *cur; /* scan the list of destinations */
-       struct chanlist *outgoing = NULL; /* list of destinations */
+       struct dial_head out_chans = AST_LIST_HEAD_NOLOCK_INIT_VALUE; /* list of destinations */
+       struct chanlist *outgoing;
+       struct chanlist *tmp;
        struct ast_channel *peer;
        int to; /* timeout */
        struct cause_args num = { chan, 0, 0, 0 };
        int cause;
-       char numsubst[256];
 
        struct ast_bridge_config config = { { 0, } };
        struct timeval calldurationlimit = { 0, };
@@ -1983,7 +2022,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
        pbx_builtin_setvar_helper(chan, "DIALEDTIME", "");
 
        if (ast_strlen_zero(data)) {
-               ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
+               ast_log(LOG_WARNING, "Dial requires an argument (technology/resource)\n");
                pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
                return -1;
        }
@@ -1999,7 +2038,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
        }
 
        if (ast_strlen_zero(args.peers)) {
-               ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
+               ast_log(LOG_WARNING, "Dial requires an argument (technology/resource)\n");
                pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
                goto done;
        }
@@ -2154,34 +2193,60 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 
        ast_channel_lock(chan);
        if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP_ONCE"))) {
-               outbound_group = ast_strdupa(outbound_group);   
+               outbound_group = ast_strdupa(outbound_group);
                pbx_builtin_setvar_helper(chan, "OUTBOUND_GROUP_ONCE", NULL);
        } else if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP"))) {
                outbound_group = ast_strdupa(outbound_group);
        }
-       ast_channel_unlock(chan);       
+       ast_channel_unlock(chan);
        ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_IGNORE_CONNECTEDLINE |
                         OPT_CANCEL_TIMEOUT | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB | OPT_FORCECLID);
 
+       /* PREDIAL: Run gosub on the caller's channel */
+       if (ast_test_flag64(&opts, OPT_PREDIAL_CALLER)
+               && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER])) {
+               ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLER]);
+               ast_app_exec_sub(NULL, chan, opt_args[OPT_ARG_PREDIAL_CALLER]);
+       }
+
        /* loop through the list of dial destinations */
        rest = args.peers;
        while ((cur = strsep(&rest, "&")) ) {
-               struct chanlist *tmp;
                struct ast_channel *tc; /* channel for this destination */
-               /* Get a technology/[device:]number pair */
+               /* Get a technology/resource pair */
                char *number = cur;
-               char *interface = ast_strdupa(number);
                char *tech = strsep(&number, "/");
+               size_t tech_len;
+               size_t number_len;
                /* find if we already dialed this interface */
                struct ast_dialed_interface *di;
                AST_LIST_HEAD(,ast_dialed_interface) *dialed_interfaces;
+
                num_dialed++;
                if (ast_strlen_zero(number)) {
-                       ast_log(LOG_WARNING, "Dial argument takes format (technology/[device:]number1)\n");
+                       ast_log(LOG_WARNING, "Dial argument takes format (technology/resource)\n");
                        goto out;
                }
-               if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+
+               tech_len = strlen(tech) + 1;
+               number_len = strlen(number) + 1;
+               tmp = ast_calloc(1, sizeof(*tmp) + (2 * tech_len) + number_len);
+               if (!tmp) {
                        goto out;
+               }
+
+               /* Save tech, number, and interface. */
+               cur = tmp->stuff;
+               strcpy(cur, tech);
+               tmp->tech = cur;
+               cur += tech_len;
+               strcpy(cur, tech);
+               cur[tech_len - 1] = '/';
+               tmp->interface = cur;
+               cur += tech_len;
+               strcpy(cur, number);
+               tmp->number = cur;
+
                if (opts.flags) {
                        ast_copy_flags64(tmp, &opts,
                                OPT_CANCEL_ELSEWHERE |
@@ -2193,7 +2258,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
                                OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID);
                        ast_set2_flag64(tmp, args.url, DIAL_NOFORWARDHTML);
                }
-               ast_copy_string(numsubst, number, sizeof(numsubst));
+
                /* Request the peer */
 
                ast_channel_lock(chan);
@@ -2215,7 +2280,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
                                chanlist_free(tmp);
                                goto out;
                        }
-
                        datastore->inheritance = DATASTORE_INHERIT_FOREVER;
 
                        if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
@@ -2234,14 +2298,13 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 
                AST_LIST_LOCK(dialed_interfaces);
                AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
-                       if (!strcasecmp(di->interface, interface)) {
+                       if (!strcasecmp(di->interface, tmp->interface)) {
                                ast_log(LOG_WARNING, "Skipping dialing interface '%s' again since it has already been dialed\n",
                                        di->interface);
                                break;
                        }
                }
                AST_LIST_UNLOCK(dialed_interfaces);
-
                if (di) {
                        fulldial++;
                        chanlist_free(tmp);
@@ -2252,45 +2315,47 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
                 * which "real" interfaces have been dialed.  The Local channel will
                 * inherit this list so that if it ends up dialing a real interface,
                 * it won't call one that has already been called. */
-               if (strcasecmp(tech, "Local")) {
-                       if (!(di = ast_calloc(1, sizeof(*di) + strlen(interface)))) {
-                               AST_LIST_UNLOCK(dialed_interfaces);
+               if (strcasecmp(tmp->tech, "Local")) {
+                       if (!(di = ast_calloc(1, sizeof(*di) + strlen(tmp->interface)))) {
                                chanlist_free(tmp);
                                goto out;
                        }
-                       strcpy(di->interface, interface);
+                       strcpy(di->interface, tmp->interface);
 
                        AST_LIST_LOCK(dialed_interfaces);
                        AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
                        AST_LIST_UNLOCK(dialed_interfaces);
                }
 
-               tc = ast_request(tech, ast_channel_nativeformats(chan), chan, numsubst, &cause);
+               tc = ast_request(tmp->tech, ast_channel_nativeformats(chan), chan, tmp->number, &cause);
                if (!tc) {
                        /* If we can't, just go on to the next call */
                        ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n",
-                               tech, cause, ast_cause2str(cause));
+                               tmp->tech, cause, ast_cause2str(cause));
                        handle_cause(cause, &num);
-                       if (!rest) /* we are on the last destination */
+                       if (!rest) {
+                               /* we are on the last destination */
                                ast_channel_hangupcause_set(chan, cause);
-                       chanlist_free(tmp);
+                       }
                        if (!ignore_cc && (cause == AST_CAUSE_BUSY || cause == AST_CAUSE_CONGESTION)) {
-                               if (!ast_cc_callback(chan, tech, numsubst, ast_cc_busy_interface)) {
-                                       ast_cc_extension_monitor_add_dialstring(chan, interface, "");
+                               if (!ast_cc_callback(chan, tmp->tech, tmp->number, ast_cc_busy_interface)) {
+                                       ast_cc_extension_monitor_add_dialstring(chan, tmp->interface, "");
                                }
                        }
+                       chanlist_free(tmp);
                        continue;
                }
                ast_channel_get_device_name(tc, device_name, sizeof(device_name));
                if (!ignore_cc) {
-                       ast_cc_extension_monitor_add_dialstring(chan, interface, device_name);
+                       ast_cc_extension_monitor_add_dialstring(chan, tmp->interface, device_name);
                }
-               pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst);
+               pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", tmp->number);
 
                ast_channel_lock_both(tc, chan);
 
                /* Setup outgoing SDP to match incoming one */
-               if (!outgoing && !rest && CAN_EARLY_BRIDGE(peerflags, chan, tc)) {
+               if (!AST_LIST_FIRST(&out_chans) && !rest && CAN_EARLY_BRIDGE(peerflags, chan, tc)) {
+                       /* We are on the only destination. */
                        ast_rtp_instance_early_bridge_make_compatible(tc, chan);
                }
                
@@ -2389,44 +2454,74 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
 
                ast_channel_unlock(tc);
                ast_channel_unlock(chan);
-               res = ast_call(tc, numsubst, 0); /* Place the call, but don't wait on the answer */
+
+               /* Put channel in the list of outgoing thingies. */
+               tmp->chan = tc;
+               AST_LIST_INSERT_TAIL(&out_chans, tmp, node);
+       }
+
+       /*
+        * PREDIAL: Run gosub on all of the callee channels
+        *
+        * We run the callee predial before ast_call() in case the user
+        * wishes to do something on the newly created channels before
+        * the channel does anything important.
+        *
+        * Inside the target gosub we will be able to do something with
+        * the newly created channel name ie: now the calling channel
+        * can know what channel will be used to call the destination
+        * ex: now we will know that SIP/abc-123 is calling SIP/def-124
+        */
+       if (ast_test_flag64(&opts, OPT_PREDIAL_CALLEE)
+               && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE])
+               && !AST_LIST_EMPTY(&out_chans)) {
+               ast_autoservice_start(chan);
+               ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLEE]);
+               AST_LIST_TRAVERSE(&out_chans, tmp, node) {
+                       ast_app_exec_sub(NULL, tmp->chan, opt_args[OPT_ARG_PREDIAL_CALLEE]);
+               }
+               ast_autoservice_stop(chan);
+       }
+
+       /* Start all outgoing calls */
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&out_chans, tmp, node) {
+               res = ast_call(tmp->chan, tmp->number, 0); /* Place the call, but don't wait on the answer */
                ast_channel_lock(chan);
 
                /* Save the info in cdr's that we called them */
                if (ast_channel_cdr(chan))
-                       ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(tc));
+                       ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(tmp->chan));
 
                /* check the results of ast_call */
                if (res) {
                        /* Again, keep going even if there's an error */
                        ast_debug(1, "ast call on peer returned %d\n", res);
-                       ast_verb(3, "Couldn't call %s/%s\n", tech, numsubst);
-                       if (ast_channel_hangupcause(tc)) {
-                               ast_channel_hangupcause_set(chan, ast_channel_hangupcause(tc));
+                       ast_verb(3, "Couldn't call %s\n", tmp->interface);
+                       if (ast_channel_hangupcause(tmp->chan)) {
+                               ast_channel_hangupcause_set(chan, ast_channel_hangupcause(tmp->chan));
                        }
                        ast_channel_unlock(chan);
-                       ast_cc_call_failed(chan, tc, interface);
-                       ast_hangup(tc);
-                       tc = NULL;
+                       ast_cc_call_failed(chan, tmp->chan, tmp->interface);
+                       ast_hangup(tmp->chan);
+                       tmp->chan = NULL;
+                       AST_LIST_REMOVE_CURRENT(node);
                        chanlist_free(tmp);
                        continue;
-               } else {
-                       senddialevent(chan, tc, numsubst);
-                       ast_verb(3, "Called %s/%s\n", tech, numsubst);
-                       ast_channel_unlock(chan);
                }
-               /* Put them in the list of outgoing thingies...  We're ready now.
-                  XXX If we're forcibly removed, these outgoing calls won't get
-                  hung up XXX */
+
+               senddialevent(chan, tmp->chan, tmp->number);
+               ast_channel_unlock(chan);
+
+               ast_verb(3, "Called %s\n", tmp->interface);
                ast_set_flag64(tmp, DIAL_STILLGOING);
-               tmp->chan = tc;
-               tmp->next = outgoing;
-               outgoing = tmp;
+
                /* If this line is up, don't try anybody else */
-               if (ast_channel_state(outgoing->chan) == AST_STATE_UP)
+               if (ast_channel_state(tmp->chan) == AST_STATE_UP) {
                        break;
+               }
        }
-       
+       AST_LIST_TRAVERSE_SAFE_END;
+
        if (ast_strlen_zero(args.timeout)) {
                to = -1;
        } else {
@@ -2439,6 +2534,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
                }
        }
 
+       outgoing = AST_LIST_FIRST(&out_chans);
        if (!outgoing) {
                strcpy(pa.status, "CHANUNAVAIL");
                if (fulldial == num_dialed) {
@@ -2474,7 +2570,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
                }
        }
 
-       peer = wait_for_answer(chan, outgoing, &to, peerflags, opt_args, &pa, &num, &result,
+       peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result,
                dtmf_progress, ignore_cc, &forced_clid, &stored_clid);
 
        /* The ast_channel_datastore_remove() function could fail here if the
@@ -2508,8 +2604,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
                /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
                   we will always return with -1 so that it is hung up properly after the
                   conversation.  */
-               hanguptree(outgoing, peer, 1);
-               outgoing = NULL;
+               hanguptree(&out_chans, peer, 1);
                /* If appropriate, log that we have a destination channel and set the answer time */
                if (ast_channel_cdr(chan)) {
                        ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(peer));
@@ -2519,11 +2614,14 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
                        pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", ast_channel_name(peer));
                
                ast_channel_lock(peer);
-               number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER"); 
-               if (!number)
-                       number = numsubst;
-               pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
+               number = pbx_builtin_getvar_helper(peer, "DIALEDPEERNUMBER");
+               if (ast_strlen_zero(number)) {
+                       number = NULL;
+               } else {
+                       number = ast_strdupa(number);
+               }
                ast_channel_unlock(peer);
+               pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
 
                if (!ast_strlen_zero(args.url) && ast_channel_supports_html(peer) ) {
                        ast_debug(1, "app_dial: sendurl=%s.\n", args.url);
@@ -2618,7 +2716,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
                        ast_channel_exten_set(peer, ast_channel_exten(chan));
                        ast_channel_priority_set(peer, ast_channel_priority(chan) + 2);
                        ast_pbx_start(peer);
-                       hanguptree(outgoing, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0);
+                       hanguptree(&out_chans, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0);
                        if (continue_exec)
                                *continue_exec = 1;
                        res = 0;
@@ -2903,7 +3001,7 @@ out:
        }
 
        ast_channel_early_bridge(chan, NULL);
-       hanguptree(outgoing, NULL, 0); /* In this case, there's no answer anywhere */
+       hanguptree(&out_chans, NULL, 0); /* In this case, there's no answer anywhere */
        pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
        senddialendevent(chan, pa.status);
        ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);