Applications no longer need to call ast_module_user_add and ast_module_user_remove...
[asterisk/asterisk.git] / apps / app_dial.c
index c3cc262..4438daa 100644 (file)
@@ -82,7 +82,7 @@ static char *descrip =
 "    ANSWEREDTIME - This is the amount of time for actual call.\n"
 "    DIALSTATUS   - This is the status of the call:\n"
 "                   CHANUNAVAIL | CONGESTION | NOANSWER | BUSY | ANSWER | CANCEL\n" 
-"                   DONTCALL | TORTURE\n"
+"                   DONTCALL | TORTURE | INVALIDARGS\n"
 "  For the Privacy and Screening Modes, the DIALSTATUS variable will be set to\n"
 "DONTCALL if the called party chooses to send the calling party to the 'Go Away'\n"
 "script. The DIALSTATUS variable will be set to TORTURE if the called party\n"
@@ -92,10 +92,15 @@ static char *descrip =
 "ends the call.\n"
 "  The optional URL will be sent to the called party if the channel supports it.\n"
 "  If the OUTBOUND_GROUP variable is set, all peer channels created by this\n"
-"application will be put into that group (as in Set(GROUP()=...).\n\n"
+"application will be put into that group (as in Set(GROUP()=...).\n"
+"  If the OUTBOUND_GROUP_ONCE variable is set, all peer channels created by this\n"
+"application will be put into that group (as in Set(GROUP()=...). Unlike OUTBOUND_GROUP,\n"
+"however, the variable will be unset after use.\n\n"
 "  Options:\n"
 "    A(x) - Play an announcement to the called party, using 'x' as the file.\n"
 "    C    - Reset the CDR for this call.\n"
+"    c    - If DIAL cancels this call, always set the flag to tell the channel\n"
+"           driver that the call is answered elsewhere.\n"
 "    d    - Allow the calling user to dial a 1 digit extension while waiting for\n"
 "           a call to be answered. Exit to that extension if it exists in the\n"
 "           current context, or the context defined in the EXITCONTEXT variable,\n"
@@ -120,7 +125,10 @@ static char *descrip =
 "    H    - Allow the calling party to hang up by hitting the '*' DTMF digit.\n"
 "    i    - Asterisk will ignore any forwarding requests it may receive on this\n"
 "           dial attempt.\n"
-"    j    - Jump to priority n+101 if all of the requested channels were busy.\n"
+"    k    - Allow the called party to enable parking of the call by sending\n"
+"           the DTMF sequence defined for call parking in features.conf.\n"
+"    K    - Allow the calling party to enable parking of the call by sending\n"
+"           the DTMF sequence defined for call parking in features.conf.\n"
 "    L(x[:y][:z]) - Limit the call to 'x' ms. Play a warning when 'y' ms are\n"
 "           left. Repeat the warning every 'z' ms. The following special\n"
 "           variables can be used with this option:\n"
@@ -142,9 +150,7 @@ static char *descrip =
 "           finished executing.\n"
 "           * ABORT        Hangup both legs of the call.\n"
 "           * CONGESTION   Behave as if line congestion was encountered.\n"
-"           * BUSY         Behave as if a busy signal was encountered. This will also\n"
-"                          have the application jump to priority n+101 if the\n"
-"                          'j' option is set.\n"
+"           * BUSY         Behave as if a busy signal was encountered.\n"
 "           * CONTINUE     Hangup the called party and allow the calling party\n"
 "                          to continue dialplan execution at the next priority.\n"
 "           * GOTO:<context>^<exten>^<priority> - Transfer the call to the\n"
@@ -184,14 +190,25 @@ static char *descrip =
 "           DTMF sequence defined in features.conf.\n"
 "    T    - Allow the calling party to transfer the called party by sending the\n"
 "           DTMF sequence defined in features.conf.\n"
+"    U(x[^arg]) - Execute via Gosub the routine 'x' for the *called* channel before connecting\n"
+"           to the calling channel. Arguments can be specified to the Gosub\n"
+"           using '^' as a delimeter. The Gosub routine can set the variable\n"
+"           GOSUB_RESULT to specify the following actions after the Gosub returns.\n" 
+"           * ABORT        Hangup both legs of the call.\n"
+"           * CONGESTION   Behave as if line congestion was encountered.\n"
+"           * BUSY         Behave as if a busy signal was encountered.\n"
+"           * CONTINUE     Hangup the called party and allow the calling party\n"
+"                          to continue dialplan execution at the next priority.\n"
+"           * GOTO:<context>^<exten>^<priority> - Transfer the call to the\n"
+"                          specified priority. Optionally, an extension, or\n"
+"                          extension and priority can be specified.\n"
+"           You cannot use any additional action post answer options in conjunction\n"
+"           with this option. Also, pbx services are not run on the peer (called) channel,\n"
+"           so you will not be able to set timeouts via the TIMEOUT() function in this routine.\n"
 "    w    - Allow the called party to enable recording of the call by sending\n"
 "           the DTMF sequence defined for one-touch recording in features.conf.\n"
 "    W    - Allow the calling party to enable recording of the call by sending\n"
-"           the DTMF sequence defined for one-touch recording in features.conf.\n"
-"    k    - Allow the called party to enable parking of the call by sending\n"
-"           the DTMF sequence defined for call parking in features.conf.\n"
-"    K    - Allow the calling party to enable parking of the call by sending\n"
-"           the DTMF sequence defined for call parking in features.conf.\n";
+"           the DTMF sequence defined for one-touch recording in features.conf.\n";
 
 /* RetryDial App by Anthony Minessale II <anthmct@yahoo.com> Jan/2005 */
 static char *rapp = "RetryDial";
@@ -218,7 +235,6 @@ enum {
        OPT_GO_ON =             (1 << 5),
        OPT_CALLEE_HANGUP =     (1 << 6),
        OPT_CALLER_HANGUP =     (1 << 7),
-       OPT_PRIORITY_JUMP =     (1 << 8),
        OPT_DURATION_LIMIT =    (1 << 9),
        OPT_MUSICBACK =         (1 << 10),
        OPT_CALLEE_MACRO =      (1 << 11),
@@ -238,7 +254,9 @@ enum {
        OPT_CALLEE_PARK =       (1 << 25),
        OPT_CALLER_PARK =       (1 << 26),
        OPT_IGNORE_FORWARDING = (1 << 27),
-} dial_exec_option_flags;
+       OPT_CALLEE_GOSUB =      (1 << 28),
+       OPT_CANCEL_ELSEWHERE =  (1 << 29),
+};
 
 #define DIAL_STILLGOING                        (1 << 30)
 #define DIAL_NOFORWARDHTML             (1 << 31)
@@ -250,16 +268,18 @@ enum {
        OPT_ARG_DURATION_LIMIT,
        OPT_ARG_MUSICBACK,
        OPT_ARG_CALLEE_MACRO,
+       OPT_ARG_CALLEE_GOSUB,
        OPT_ARG_PRIVACY,
        OPT_ARG_DURATION_STOP,
        OPT_ARG_OPERMODE,
        /* note: this entry _MUST_ be the last one in the enum */
        OPT_ARG_ARRAY_SIZE,
-} dial_exec_option_args;
+};
 
 AST_APP_OPTIONS(dial_exec_options, {
        AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE),
        AST_APP_OPTION('C', OPT_RESETCDR),
+       AST_APP_OPTION('c', OPT_CANCEL_ELSEWHERE),
        AST_APP_OPTION('d', OPT_DTMF_EXIT),
        AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF),
        AST_APP_OPTION('f', OPT_FORCECLID),
@@ -268,7 +288,8 @@ AST_APP_OPTIONS(dial_exec_options, {
        AST_APP_OPTION('h', OPT_CALLEE_HANGUP),
        AST_APP_OPTION('H', OPT_CALLER_HANGUP),
        AST_APP_OPTION('i', OPT_IGNORE_FORWARDING),
-       AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
+       AST_APP_OPTION('k', OPT_CALLEE_PARK),
+       AST_APP_OPTION('K', OPT_CALLER_PARK),
        AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
        AST_APP_OPTION_ARG('m', OPT_MUSICBACK, OPT_ARG_MUSICBACK),
        AST_APP_OPTION_ARG('M', OPT_CALLEE_MACRO, OPT_ARG_CALLEE_MACRO),
@@ -282,35 +303,36 @@ AST_APP_OPTIONS(dial_exec_options, {
        AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP),
        AST_APP_OPTION('t', OPT_CALLEE_TRANSFER),
        AST_APP_OPTION('T', OPT_CALLER_TRANSFER),
+       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),
-       AST_APP_OPTION('k', OPT_CALLEE_PARK),
-       AST_APP_OPTION('K', OPT_CALLER_PARK),
 });
 
-/* We define a custom "local user" structure because we
-   use it not only for keeping track of what is in use but
-   also for keeping track of who we're dialing. */
-
-struct dial_localuser {
+/*
+ * The list of active channels
+ */
+struct chanlist {
+       struct chanlist *next;
        struct ast_channel *chan;
        unsigned int flags;
        int forwards;
-       struct dial_localuser *next;
 };
 
 
-static void hanguptree(struct dial_localuser *outgoing, struct ast_channel *exception)
+static void hanguptree(struct chanlist *outgoing, struct ast_channel *exception, int answered_elsewhere)
 {
        /* Hang up a tree of stuff */
-       struct dial_localuser *oo;
+       struct chanlist *oo;
        while (outgoing) {
                /* Hangup any existing lines we have open */
-               if (outgoing->chan && (outgoing->chan != exception))
+               if (outgoing->chan && (outgoing->chan != exception)) {
+                       if (answered_elsewhere)
+                               ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
                        ast_hangup(outgoing->chan);
+               }
                oo = outgoing;
                outgoing=outgoing->next;
-               free(oo);
+               ast_free(oo);
        }
 }
 
@@ -419,11 +441,17 @@ static void senddialendevent(const struct ast_channel *src, const char *dialstat
                                        src->name, dialstatus);
 }      
 
-/* helper function for wait_for_answer() */
-static void do_forward(struct dial_localuser *o,
+/*!
+ * helper function for wait_for_answer()
+ *
+ * XXX this code is highly suspicious, as it essentially overwrites
+ * the outgoing channel without properly deleting it.
+ */
+static void do_forward(struct chanlist *o,
        struct cause_args *num, struct ast_flags *peerflags, int single)
 {
        char tmpchan[256];
+       struct ast_channel *original = o->chan;
        struct ast_channel *c = o->chan; /* the winner */
        struct ast_channel *in = num->chan; /* the input channel */
        char *stuff;
@@ -454,10 +482,12 @@ static void do_forward(struct dial_localuser *o,
                } else {
                        /* Setup parameters */
                        c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause);
-                       if (!c)
-                               ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
-                       else
+                       if (c) {
+                               if (single)
+                                       ast_channel_make_compatible(o->chan, in);
                                ast_channel_inherit_variables(in, o->chan);
+                       } else
+                               ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
                }
        } else {
                if (option_verbose > 2)
@@ -494,20 +524,20 @@ static void do_forward(struct dial_localuser *o,
                if (ast_call(c, tmpchan, 0)) {
                        ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
                        ast_clear_flag(o, DIAL_STILLGOING);     
-                       ast_hangup(c);
+                       ast_hangup(original);
                        c = o->chan = NULL;
                        num->nochan++;
                } else {
                        senddialevent(in, c);
                        /* After calling, set callerid to extension */
                        if (!ast_test_flag(peerflags, OPT_ORIGINAL_CLID)) {
-                               char cidname[AST_MAX_EXTENSION];
+                               char cidname[AST_MAX_EXTENSION] = "";
                                ast_set_callerid(c, S_OR(in->macroexten, in->exten), get_cid_name(cidname, sizeof(cidname), in), NULL);
                        }
+                       /* Hangup the original channel now, in case we needed it */
+                       ast_hangup(original);
                }
        }
-       /* Hangup the original channel now, in case we needed it */
-       ast_hangup(c);
 }
 
 /* argument used for some functions. */
@@ -520,9 +550,9 @@ struct privacy_args {
 };
 
 static struct ast_channel *wait_for_answer(struct ast_channel *in,
-       struct dial_localuser *outgoing, int *to, struct ast_flags *peerflags,
+       struct chanlist *outgoing, int *to, struct ast_flags *peerflags,
        struct privacy_args *pa,
-       const struct cause_args *num_in, int priority_jump, int *result)
+       const struct cause_args *num_in, int *result)
 {
        struct cause_args num = *num_in;
        int prestart = num.busy + num.congestion + num.nochan;
@@ -540,7 +570,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
        
        
        while (*to && !peer) {
-               struct dial_localuser *o;
+               struct chanlist *o;
                int pos = 0;    /* how many channels do we handle */
                int numlines = prestart;
                struct ast_channel *winner;
@@ -563,8 +593,6 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                        strcpy(pa->status, "CONGESTION");
                                else if (num.nochan)
                                        strcpy(pa->status, "CHANUNAVAIL");
-                               if (ast_opt_priority_jumping || priority_jump)
-                                       ast_goto_if_exists(in, in->context, in->exten, in->priority + 101);
                        } else {
                                if (option_verbose > 2)
                                        ast_verbose(VERBOSE_PREFIX_3 "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
@@ -708,8 +736,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                        }
                                        break;
                                default:
-                                       if (option_debug)
-                                               ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
+                                       ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
                                }
                        } else if (single) {
                                /* XXX are we sure the logic is correct ? or we should just switch on f->frametype ? */
@@ -741,6 +768,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                /* Got hung up */
                                *to = -1;
                                strcpy(pa->status, "CANCEL");
+                               ast_cdr_noanswer(in->cdr);
                                if (f)
                                        ast_frfree(f);
                                return NULL;
@@ -754,6 +782,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                                if (option_verbose > 2)
                                                        ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
                                                *to=0;
+                                               ast_cdr_noanswer(in->cdr);
                                                *result = f->subclass;
                                                strcpy(pa->status, "CANCEL");
                                                ast_frfree(f);
@@ -767,6 +796,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                                ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
                                        *to=0;
                                        strcpy(pa->status, "CANCEL");
+                                       ast_cdr_noanswer(in->cdr);
                                        ast_frfree(f);
                                        return NULL;
                                }
@@ -778,9 +808,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                        ast_log(LOG_WARNING, "Unable to send URL\n");
                        
 
-                       if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF)))  {
+                       if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF_BEGIN) || (f->frametype == AST_FRAME_DTMF_END)))  {
                                if (ast_write(outgoing->chan, f))
-                                       ast_log(LOG_WARNING, "Unable to forward voice\n");
+                                       ast_log(LOG_WARNING, "Unable to forward voice or dtmf\n");
                        }
                        if (single && (f->frametype == AST_FRAME_CONTROL) && 
                                ((f->subclass == AST_CONTROL_HOLD) || 
@@ -794,6 +824,10 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                }
                if (!*to && (option_verbose > 2))
                        ast_verbose(VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
+               if (!*to || ast_check_hangup(in)) {
+                       ast_cdr_noanswer(in->cdr);
+               }
+               
        }
 
        return peer;
@@ -933,8 +967,11 @@ static int do_privacy(struct ast_channel *chan, struct ast_channel *peer,
           time and make the caller believe the peer hasn't picked up yet */
 
        if (ast_test_flag(opts, OPT_MUSICBACK) && !ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
+               char *original_moh = ast_strdupa(chan->musicclass);
                ast_indicate(chan, -1);
+               ast_string_field_set(chan, musicclass, opt_args[OPT_ARG_MUSICBACK]);
                ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
+               ast_string_field_set(chan, musicclass, original_moh);
        } else if (ast_test_flag(opts, OPT_RINGBACK)) {
                ast_indicate(chan, AST_CONTROL_RINGING);
                pa->sentringing++;
@@ -1049,18 +1086,123 @@ static int do_privacy(struct ast_channel *chan, struct ast_channel *peer,
        }
 }
 
-static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags)
+/*! \brief returns 1 if successful, 0 or <0 if the caller should 'goto out' */
+static int setup_privacy_args(struct privacy_args *pa,
+       struct ast_flags *opts, char *opt_args[], struct ast_channel *chan)
+{
+       char callerid[60];
+       int res;
+       char *l;
+
+       if (!ast_strlen_zero(chan->cid.cid_num)) {
+               l = ast_strdupa(chan->cid.cid_num);
+               ast_shrink_phone_number(l);
+               if (ast_test_flag(opts, OPT_PRIVACY) ) {
+                       if (option_verbose > 2)
+                               ast_verbose(VERBOSE_PREFIX_3  "Privacy DB is '%s', clid is '%s'\n",
+                                            opt_args[OPT_ARG_PRIVACY], l);
+                       pa->privdb_val = ast_privacy_check(opt_args[OPT_ARG_PRIVACY], l);
+               } else {
+                       if (option_verbose > 2)
+                               ast_verbose(VERBOSE_PREFIX_3  "Privacy Screening, clid is '%s'\n", l);
+                       pa->privdb_val = AST_PRIVACY_UNKNOWN;
+               }
+       } else {
+               char *tnam, *tn2;
+
+               tnam = ast_strdupa(chan->name);
+               /* clean the channel name so slashes don't try to end up in disk file name */
+               for (tn2 = tnam; *tn2; tn2++) {
+                       if (*tn2=='/')  /* any other chars to be afraid of? */
+                               *tn2 = '=';
+               }
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3  "Privacy-- callerid is empty\n");
+
+               snprintf(callerid, sizeof(callerid), "NOCALLERID_%s%s", chan->exten, tnam);
+               l = callerid;
+               pa->privdb_val = AST_PRIVACY_UNKNOWN;
+       }
+       
+       ast_copy_string(pa->privcid,l,sizeof(pa->privcid));
+
+       if( strncmp(pa->privcid,"NOCALLERID",10) != 0 && ast_test_flag(opts, OPT_SCREEN_NOCLID) ) { /* if callerid is set, and ast_test_flag(&opts, OPT_SCREEN_NOCLID) is set also */  
+               if (option_verbose > 2)
+                       ast_verbose( VERBOSE_PREFIX_3  "CallerID set (%s); N option set; Screening should be off\n", pa->privcid);
+               pa->privdb_val = AST_PRIVACY_ALLOW;
+       } else if (ast_test_flag(opts, OPT_SCREEN_NOCLID) && strncmp(pa->privcid,"NOCALLERID",10) == 0 ) {
+               if (option_verbose > 2)
+                       ast_verbose( VERBOSE_PREFIX_3  "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa->privdb_val);
+       }
+       
+       if (pa->privdb_val == AST_PRIVACY_DENY ) {
+               ast_copy_string(pa->status, "NOANSWER", sizeof(pa->status));
+               if (option_verbose > 2)
+                       ast_verbose( VERBOSE_PREFIX_3  "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n");
+               return 0;
+       } else if (pa->privdb_val == AST_PRIVACY_KILL ) {
+               ast_copy_string(pa->status, "DONTCALL", sizeof(pa->status));
+               return 0; /* Is this right? */
+       } else if (pa->privdb_val == AST_PRIVACY_TORTURE ) {
+               ast_copy_string(pa->status, "TORTURE", sizeof(pa->status));
+               return 0; /* is this right??? */
+       } else if (pa->privdb_val == AST_PRIVACY_UNKNOWN ) {
+               /* Get the user's intro, store it in priv-callerintros/$CID, 
+                  unless it is already there-- this should be done before the 
+                  call is actually dialed  */
+
+               /* make sure the priv-callerintros dir actually exists */
+               snprintf(pa->privintro, sizeof(pa->privintro), "%s/sounds/priv-callerintros", ast_config_AST_DATA_DIR);
+               if ((res = ast_mkdir(pa->privintro, 0755))) {
+                       ast_log(LOG_WARNING, "privacy: can't create directory priv-callerintros: %s\n", strerror(res));
+                       return -1;
+               }
+
+               snprintf(pa->privintro, sizeof(pa->privintro), "priv-callerintros/%s", pa->privcid);
+               if (ast_fileexists(pa->privintro, NULL, NULL ) > 0 && strncmp(pa->privcid, "NOCALLERID", 10) != 0) {
+                       /* the DELUX version of this code would allow this caller the
+                          option to hear and retape their previously recorded intro.
+                       */
+               } else {
+                       int duration; /* for feedback from play_and_wait */
+                       /* the file doesn't exist yet. Let the caller submit his
+                          vocal intro for posterity */
+                       /* priv-recordintro script:
+
+                          "At the tone, please say your name:"
+
+                       */
+                       ast_answer(chan);
+                       res = ast_play_and_record(chan, "priv-recordintro", pa->privintro, 4, "gsm", &duration, 128, 2000, 0);  /* NOTE: I've reduced the total time to 4 sec */
+                                                                       /* don't think we'll need a lock removed, we took care of
+                                                                          conflicts by naming the pa.privintro file */
+                       if (res == -1) {
+                               /* Delete the file regardless since they hung up during recording */
+                               ast_filedelete(pa->privintro, NULL);
+                               if (ast_fileexists(pa->privintro,NULL,NULL ) > 0 )
+                                       ast_log(LOG_NOTICE,"privacy: ast_filedelete didn't do its job on %s\n", pa->privintro);
+                               else if (option_verbose > 2)
+                                       ast_verbose( VERBOSE_PREFIX_3 "Successfully deleted %s intro file\n", pa->privintro);
+                               return -1;
+                       }
+                       if (!ast_streamfile(chan, "vm-dialout", chan->language) )
+                               ast_waitstream(chan, "");
+               }
+       }
+       return 1;       /* success */
+}
+
+static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags, int *continue_exec)
 {
        int res = -1;   /* default: error */
-       struct ast_module_user *u;
        char *rest, *cur;       /* scan the list of destinations */
-       struct dial_localuser *outgoing = NULL; /* list of destinations */
+       struct chanlist *outgoing = NULL;       /* list of destinations */
        struct ast_channel *peer;
        int to; /* timeout */
        struct cause_args num = { chan, 0, 0, 0 };
        int cause;
-       char numsubst[AST_MAX_EXTENSION];
-       char cidname[AST_MAX_EXTENSION];
+       char numsubst[256];
+       char cidname[AST_MAX_EXTENSION] = "";
 
        struct ast_bridge_config config;
        unsigned int calldurationlimit = 0;
@@ -1068,6 +1210,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
        struct privacy_args pa = {
                .sentringing = 0,
                .privdb_val = 0,
+               .status = "INVALIDARGS",
        };
        int sentringing = 0, moh = 0;
        const char *outbound_group = NULL;
@@ -1086,23 +1229,25 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
 
        if (ast_strlen_zero(data)) {
                ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
+               pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
                return -1;
        }
 
-       u = ast_module_user_add(chan);  /* XXX is this the right place ? */
-
        parse = ast_strdupa(data);
-
+       
        AST_STANDARD_APP_ARGS(args, parse);
 
        memset(&config,0,sizeof(struct ast_bridge_config));
 
        if (!ast_strlen_zero(args.options) &&
-                       ast_app_parse_options(dial_exec_options, &opts, opt_args, args.options))
+                       ast_app_parse_options(dial_exec_options, &opts, opt_args, args.options)) {
+               pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
                goto done;
+       }
 
        if (ast_strlen_zero(args.peers)) {
                ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
+               pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
                goto done;
        }
 
@@ -1116,6 +1261,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                calldurationlimit = atoi(opt_args[OPT_ARG_DURATION_STOP]);
                if (!calldurationlimit) {
                        ast_log(LOG_WARNING, "Dial does not accept S(%s), hanging up.\n", opt_args[OPT_ARG_DURATION_STOP]);
+                       pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
                        goto done;
                }
                if (option_verbose > 2)
@@ -1136,128 +1282,31 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                ast_cdr_reset(chan->cdr, NULL);
        if (ast_test_flag(&opts, OPT_PRIVACY) && ast_strlen_zero(opt_args[OPT_ARG_PRIVACY]))
                opt_args[OPT_ARG_PRIVACY] = ast_strdupa(chan->exten);
-       if (ast_test_flag(&opts, OPT_PRIVACY) || ast_test_flag(&opts, OPT_SCREENING)) {
-               char callerid[60];
-               char *l = chan->cid.cid_num;    /* XXX watch out, we are overwriting it */
-               if (!ast_strlen_zero(l)) {
-                       ast_shrink_phone_number(l);
-                       if( ast_test_flag(&opts, OPT_PRIVACY) ) {
-                               if (option_verbose > 2)
-                                       ast_verbose(VERBOSE_PREFIX_3  "Privacy DB is '%s', clid is '%s'\n",
-                                                    opt_args[OPT_ARG_PRIVACY], l);
-                               pa.privdb_val = ast_privacy_check(opt_args[OPT_ARG_PRIVACY], l);
-                       }
-                       else {
-                               if (option_verbose > 2)
-                                       ast_verbose(VERBOSE_PREFIX_3  "Privacy Screening, clid is '%s'\n", l);
-                               pa.privdb_val = AST_PRIVACY_UNKNOWN;
-                       }
-               } else {
-                       char *tnam, *tn2;
-
-                       tnam = ast_strdupa(chan->name);
-                       /* clean the channel name so slashes don't try to end up in disk file name */
-                       for(tn2 = tnam; *tn2; tn2++) {
-                               if( *tn2=='/')
-                                       *tn2 = '=';  /* any other chars to be afraid of? */
-                       }
-                       if (option_verbose > 2)
-                               ast_verbose(VERBOSE_PREFIX_3  "Privacy-- callerid is empty\n");
 
-                       snprintf(callerid, sizeof(callerid), "NOCALLERID_%s%s", chan->exten, tnam);
-                       l = callerid;
-                       pa.privdb_val = AST_PRIVACY_UNKNOWN;
-               }
-               
-               ast_copy_string(pa.privcid,l,sizeof(pa.privcid));
-
-               if( strncmp(pa.privcid,"NOCALLERID",10) != 0 && ast_test_flag(&opts, OPT_SCREEN_NOCLID) ) { /* if callerid is set, and ast_test_flag(&opts, OPT_SCREEN_NOCLID) is set also */  
-                       if (option_verbose > 2)
-                               ast_verbose( VERBOSE_PREFIX_3  "CallerID set (%s); N option set; Screening should be off\n", pa.privcid);
-                       pa.privdb_val = AST_PRIVACY_ALLOW;
-               }
-               else if(ast_test_flag(&opts, OPT_SCREEN_NOCLID) && strncmp(pa.privcid,"NOCALLERID",10) == 0 ) {
-                       if (option_verbose > 2)
-                               ast_verbose( VERBOSE_PREFIX_3  "CallerID blank; N option set; Screening should happen; dbval is %d\n", pa.privdb_val);
-               }
-               
-               if(pa.privdb_val == AST_PRIVACY_DENY ) {
-                       ast_copy_string(pa.status, "NOANSWER", sizeof(pa.status));
-                       if (option_verbose > 2)
-                               ast_verbose( VERBOSE_PREFIX_3  "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n");
-                       res=0;
+       if (ast_test_flag(&opts, OPT_PRIVACY) || ast_test_flag(&opts, OPT_SCREENING)) {
+               res = setup_privacy_args(&pa, &opts, opt_args, chan);
+               if (res <= 0)
                        goto out;
-               }
-               else if(pa.privdb_val == AST_PRIVACY_KILL ) {
-                       ast_copy_string(pa.status, "DONTCALL", sizeof(pa.status));
-                       if (ast_opt_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) {
-                               ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 201);
-                       }
-                       res = 0;
-                       goto out; /* Is this right? */
-               }
-               else if(pa.privdb_val == AST_PRIVACY_TORTURE ) {
-                       ast_copy_string(pa.status, "TORTURE", sizeof(pa.status));
-                       if (ast_opt_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) {
-                               ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 301);
-                       }
-                       res = 0;
-                       goto out; /* is this right??? */
-               }
-               else if(pa.privdb_val == AST_PRIVACY_UNKNOWN ) {
-                       /* Get the user's intro, store it in priv-callerintros/$CID, 
-                          unless it is already there-- this should be done before the 
-                          call is actually dialed  */
-
-                       /* make sure the priv-callerintros dir actually exists */
-                       snprintf(pa.privintro, sizeof(pa.privintro), "%s/sounds/priv-callerintros", ast_config_AST_DATA_DIR);
-                       if (mkdir(pa.privintro, 0755) && errno != EEXIST) {
-                               ast_log(LOG_WARNING, "privacy: can't create directory priv-callerintros: %s\n", strerror(errno));
-                               res = -1;
-                               goto out;
-                       }
-
-                       snprintf(pa.privintro,sizeof(pa.privintro), "priv-callerintros/%s", pa.privcid);
-                       if( ast_fileexists(pa.privintro,NULL,NULL ) > 0 && strncmp(pa.privcid,"NOCALLERID",10) != 0) {
-                               /* the DELUX version of this code would allow this caller the
-                                  option to hear and retape their previously recorded intro.
-                               */
-                       }
-                       else {
-                               int duration; /* for feedback from play_and_wait */
-                               /* the file doesn't exist yet. Let the caller submit his
-                                  vocal intro for posterity */
-                               /* priv-recordintro script:
-
-                                  "At the tone, please say your name:"
-
-                               */
-                               res = ast_play_and_record(chan, "priv-recordintro", pa.privintro, 4, "gsm", &duration, 128, 2000, 0);  /* NOTE: I've reduced the total time to 4 sec */
-                                                                               /* don't think we'll need a lock removed, we took care of
-                                                                                  conflicts by naming the pa.privintro file */
-                               if (res == -1) {
-                                       /* Delete the file regardless since they hung up during recording */
-                                        ast_filedelete(pa.privintro, NULL);
-                                        if( ast_fileexists(pa.privintro,NULL,NULL ) > 0 )
-                                                ast_log(LOG_NOTICE,"privacy: ast_filedelete didn't do its job on %s\n", pa.privintro);
-                                        else if (option_verbose > 2)
-                                                ast_verbose( VERBOSE_PREFIX_3 "Successfully deleted %s intro file\n", pa.privintro);
-                                       goto out;
-                               }
-                                if( !ast_streamfile(chan, "vm-dialout", chan->language) )
-                                        ast_waitstream(chan, "");
-                       }
-               }
+               res = -1;       /* reset default */
        }
 
+       if (continue_exec)
+               *continue_exec = 0;
+       
        /* If a channel group has been specified, get it for use when we create peer channels */
-       outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP");
-
+       if ((outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP_ONCE"))) {
+               outbound_group = ast_strdupa(outbound_group);
+               pbx_builtin_setvar_helper(chan, "OUTBOUND_GROUP_ONCE", NULL);
+       } else {
+               outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP");
+       }
+           
        ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING);
        /* loop through the list of dial destinations */
        rest = args.peers;
        while ((cur = strsep(&rest, "&")) ) {
-               struct dial_localuser *tmp;
+               struct chanlist *tmp;
+               struct ast_channel *tc; /* channel for this destination */
                /* Get a technology/[device:]number pair */
                char *number = cur;
                char *tech = strsep(&number, "/");
@@ -1269,6 +1318,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                        goto out;
                if (opts.flags) {
                        ast_copy_flags(tmp, &opts,
+                                      OPT_CANCEL_ELSEWHERE |
                                       OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
                                       OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
                                       OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
@@ -1278,128 +1328,136 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                }
                ast_copy_string(numsubst, number, sizeof(numsubst));
                /* Request the peer */
-               tmp->chan = ast_request(tech, chan->nativeformats, numsubst, &cause);
-               if (!tmp->chan) {
+               tc = ast_request(tech, chan->nativeformats, numsubst, &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));
+                       ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n",
+                                   tech, cause, ast_cause2str(cause));
                        handle_cause(cause, &num);
                        if (!rest)      /* we are on the last destination */
                                chan->hangupcause = cause;
+                       ast_free(tmp);
                        continue;
                }
-               pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", numsubst);
-               if (!ast_strlen_zero(tmp->chan->call_forward)) {
+               pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst);
+               if (!ast_strlen_zero(tc->call_forward)) {
                        char tmpchan[256];
                        char *stuff;
                        char *tech;
-                       ast_copy_string(tmpchan, tmp->chan->call_forward, sizeof(tmpchan));
+                       ast_copy_string(tmpchan, tc->call_forward, sizeof(tmpchan));
                        if ((stuff = strchr(tmpchan, '/'))) {
                                *stuff++ = '\0';
                                tech = tmpchan;
                        } else {
-                               snprintf(tmpchan, sizeof(tmpchan), "%s@%s", tmp->chan->call_forward, tmp->chan->context);
+                               snprintf(tmpchan, sizeof(tmpchan), "%s@%s", tc->call_forward, tc->context);
                                stuff = tmpchan;
                                tech = "Local";
                        }
                        tmp->forwards++;
                        if (tmp->forwards < AST_MAX_FORWARDS) {
                                if (option_verbose > 2)
-                                       ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", chan->name, tech, stuff, tmp->chan->name);
-                               ast_hangup(tmp->chan);
-                               /* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */
+                                       ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n",
+                                       chan->name, tech, stuff, tc->name);
+                               ast_hangup(tc);
+                               /* If we have been told to ignore forwards, just set this channel to null
+                                * and continue processing extensions normally */
                                if (ast_test_flag(&opts, OPT_IGNORE_FORWARDING)) {
-                                       tmp->chan = NULL;
+                                       tc = NULL;
                                        cause = AST_CAUSE_BUSY;
                                        if (option_verbose > 2)
-                                               ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s/%s' prevented.\n", chan->name, tech, stuff);
+                                               ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s/%s' prevented.\n",
+                                                       chan->name, tech, stuff);
                                } else {
-                                       tmp->chan = ast_request(tech, chan->nativeformats, stuff, &cause);
+                                       tc = ast_request(tech, chan->nativeformats, stuff, &cause);
                                }
-                               if (!tmp->chan)
+                               if (!tc)
                                        ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
                                else
-                                       ast_channel_inherit_variables(chan, tmp->chan);
+                                       ast_channel_inherit_variables(chan, tc);
                        } else {
                                if (option_verbose > 2)
-                                       ast_verbose(VERBOSE_PREFIX_3 "Too many forwards from %s\n", tmp->chan->name);
-                               ast_hangup(tmp->chan);
-                               tmp->chan = NULL;
+                                       ast_verbose(VERBOSE_PREFIX_3 "Too many forwards from %s\n", tc->name);
+                               ast_hangup(tc);
+                               tc = NULL;
                                cause = AST_CAUSE_CONGESTION;
                        }
-                       if (!tmp->chan) {
+                       if (!tc) {
                                handle_cause(cause, &num);
+                               ast_free(tmp);
                                continue;
                        }
                }
 
                /* Setup outgoing SDP to match incoming one */
-               ast_rtp_make_compatible(tmp->chan, chan, !outgoing && !rest);
+               ast_rtp_make_compatible(tc, chan, !outgoing && !rest);
                
                /* Inherit specially named variables from parent channel */
-               ast_channel_inherit_variables(chan, tmp->chan);
+               ast_channel_inherit_variables(chan, tc);
 
-               tmp->chan->appl = "AppDial";
-               tmp->chan->data = "(Outgoing Line)";
-               tmp->chan->whentohangup = 0;
+               tc->appl = "AppDial";
+               tc->data = "(Outgoing Line)";
+               tc->whentohangup = 0;
 
-               S_REPLACE(tmp->chan->cid.cid_num, ast_strdup(chan->cid.cid_num));
-               S_REPLACE(tmp->chan->cid.cid_name, ast_strdup(chan->cid.cid_name));
-               S_REPLACE(tmp->chan->cid.cid_ani, ast_strdup(chan->cid.cid_ani));
-               S_REPLACE(tmp->chan->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis));
+               S_REPLACE(tc->cid.cid_num, ast_strdup(chan->cid.cid_num));
+               S_REPLACE(tc->cid.cid_name, ast_strdup(chan->cid.cid_name));
+               S_REPLACE(tc->cid.cid_ani, ast_strdup(chan->cid.cid_ani));
+               S_REPLACE(tc->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis));
                
                /* Copy language from incoming to outgoing */
-               ast_string_field_set(tmp->chan, language, chan->language);
-               ast_string_field_set(tmp->chan, accountcode, chan->accountcode);
-               tmp->chan->cdrflags = chan->cdrflags;
-               if (ast_strlen_zero(tmp->chan->musicclass))
-                       ast_string_field_set(tmp->chan, musicclass, chan->musicclass);
-               /* Pass callingpres setting */
-               tmp->chan->cid.cid_pres = chan->cid.cid_pres;
-               /* Pass type of number */
-               tmp->chan->cid.cid_ton = chan->cid.cid_ton;
-               /* Pass type of tns */
-               tmp->chan->cid.cid_tns = chan->cid.cid_tns;
-               /* Presense of ADSI CPE on outgoing channel follows ours */
-               tmp->chan->adsicpe = chan->adsicpe;
-               /* Pass the transfer capability */
-               tmp->chan->transfercapability = chan->transfercapability;
+               ast_string_field_set(tc, language, chan->language);
+               ast_string_field_set(tc, accountcode, chan->accountcode);
+               tc->cdrflags = chan->cdrflags;
+               if (ast_strlen_zero(tc->musicclass))
+                       ast_string_field_set(tc, musicclass, chan->musicclass);
+               /* Pass callingpres, type of number, tns, ADSI CPE, transfer capability */
+               tc->cid.cid_pres = chan->cid.cid_pres;
+               tc->cid.cid_ton = chan->cid.cid_ton;
+               tc->cid.cid_tns = chan->cid.cid_tns;
+               tc->adsicpe = chan->adsicpe;
+               tc->transfercapability = chan->transfercapability;
 
                /* If we have an outbound group, set this peer channel to it */
                if (outbound_group)
-                       ast_app_group_set_channel(tmp->chan, outbound_group);
+                       ast_app_group_set_channel(tc, outbound_group);
 
                /* Inherit context and extension */
-               ast_copy_string(tmp->chan->dialcontext, chan->context, sizeof(tmp->chan->dialcontext));
-               ast_copy_string(tmp->chan->exten, chan->exten, sizeof(tmp->chan->exten));
+               if (!ast_strlen_zero(chan->macrocontext))
+                       ast_copy_string(tc->dialcontext, chan->macrocontext, sizeof(tc->dialcontext));
+               else
+                       ast_copy_string(tc->dialcontext, chan->context, sizeof(tc->dialcontext));
+               if (!ast_strlen_zero(chan->macroexten))
+                       ast_copy_string(tc->exten, chan->macroexten, sizeof(tc->exten));
+               else
+                       ast_copy_string(tc->exten, chan->exten, sizeof(tc->exten));
 
-               /* Place the call, but don't wait on the answer */
-               res = ast_call(tmp->chan, numsubst, 0);
+               res = ast_call(tc, numsubst, 0);        /* Place the call, but don't wait on the answer */
 
                /* Save the info in cdr's that we called them */
                if (chan->cdr)
-                       ast_cdr_setdestchan(chan->cdr, tmp->chan->name);
+                       ast_cdr_setdestchan(chan->cdr, tc->name);
 
                /* check the results of ast_call */
                if (res) {
                        /* Again, keep going even if there's an error */
-                       if (option_debug)
-                               ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
+                       ast_debug(1, "ast call on peer returned %d\n", res);
                        if (option_verbose > 2)
                                ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
-                       ast_hangup(tmp->chan);
-                       tmp->chan = NULL;
+                       ast_hangup(tc);
+                       tc = NULL;
+                       ast_free(tmp);
                        continue;
                } else {
-                       senddialevent(chan, tmp->chan);
+                       senddialevent(chan, tc);
                        if (option_verbose > 2)
                                ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
                        if (!ast_test_flag(peerflags, OPT_ORIGINAL_CLID))
-                               ast_set_callerid(tmp->chan, S_OR(chan->macroexten, chan->exten), get_cid_name(cidname, sizeof(cidname), chan), NULL);
+                               ast_set_callerid(tc, S_OR(chan->macroexten, chan->exten), get_cid_name(cidname, sizeof(cidname), chan), NULL);
                }
                /* 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 */
                ast_set_flag(tmp, DIAL_STILLGOING);     
+               tmp->chan = tc;
                tmp->next = outgoing;
                outgoing = tmp;
                /* If this line is up, don't try anybody else */
@@ -1424,7 +1482,15 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                strcpy(pa.status, "NOANSWER");
                if (ast_test_flag(outgoing, OPT_MUSICBACK)) {
                        moh = 1;
-                       ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
+                       if (!ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
+                               char *original_moh = ast_strdupa(chan->musicclass);
+                               ast_string_field_set(chan, musicclass, opt_args[OPT_ARG_MUSICBACK]);
+                               ast_moh_start(chan, opt_args[OPT_ARG_MUSICBACK], NULL);
+                               ast_string_field_set(chan, musicclass, original_moh);
+                       } else {
+                               ast_moh_start(chan, NULL, NULL);
+                       }
+                       ast_indicate(chan, AST_CONTROL_PROGRESS);
                } else if (ast_test_flag(outgoing, OPT_RINGBACK)) {
                        ast_indicate(chan, AST_CONTROL_RINGING);
                        sentringing++;
@@ -1432,7 +1498,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
        }
 
        time(&start_time);
-       peer = wait_for_answer(chan, outgoing, &to, peerflags, &pa, &num, ast_test_flag(&opts, OPT_PRIORITY_JUMP), &result);
+       peer = wait_for_answer(chan, outgoing, &to, peerflags, &pa, &num, &result);
        
        if (!peer) {
                if (result) {
@@ -1452,7 +1518,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                /* 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);
+               hanguptree(outgoing, peer, 1);
                outgoing = NULL;
                /* If appropriate, log that we have a destination channel */
                if (chan->cdr)
@@ -1465,8 +1531,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                        number = numsubst;
                pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", number);
                if (!ast_strlen_zero(args.url) && ast_channel_supports_html(peer) ) {
-                       if (option_debug)
-                               ast_log(LOG_DEBUG, "app_dial: sendurl=%s.\n", args.url);
+                       ast_debug(1, "app_dial: sendurl=%s.\n", args.url);
                        ast_channel_sendurl( peer, args.url );
                }
                if ( (ast_test_flag(&opts, OPT_PRIVACY) || ast_test_flag(&opts, OPT_SCREENING)) && pa.privdb_val == AST_PRIVACY_UNKNOWN) {
@@ -1502,7 +1567,9 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                        ast_parseable_goto(peer, opt_args[OPT_ARG_GOTO]);
                        peer->priority++;
                        ast_pbx_start(peer);
-                       hanguptree(outgoing, NULL);
+                       hanguptree(outgoing, NULL, ast_test_flag(&opts, OPT_CANCEL_ELSEWHERE ? 1 : 0));
+                       if (continue_exec)
+                               *continue_exec = 1;
                        res = 0;
                        goto done;
                }
@@ -1522,8 +1589,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                        if (theapp && !res) {   /* XXX why check res here ? */
                                replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_MACRO]);
                                res = pbx_exec(peer, theapp, opt_args[OPT_ARG_CALLEE_MACRO]);
-                               if (option_debug)
-                                       ast_log(LOG_DEBUG, "Macro exited with status %d\n", res);
+                               ast_debug(1, "Macro exited with status %d\n", res);
                                res = 0;
                        } else {
                                ast_log(LOG_ERROR, "Could not find application Macro\n");
@@ -1540,12 +1606,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
 
                                if (!strcasecmp(macro_result, "BUSY")) {
                                        ast_copy_string(pa.status, macro_result, sizeof(pa.status));
-                                       if (ast_opt_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) {
-                                               if (!ast_goto_if_exists(chan, NULL, NULL, chan->priority + 101)) {
-                                                       ast_set_flag(peerflags, OPT_GO_ON);
-                                               }
-                                       } else
-                                               ast_set_flag(peerflags, OPT_GO_ON);
+                                       ast_set_flag(peerflags, OPT_GO_ON);
                                        res = -1;
                                } else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) {
                                        ast_copy_string(pa.status, macro_result, sizeof(pa.status));
@@ -1573,9 +1634,92 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                        }
                }
 
+               if (ast_test_flag(&opts, OPT_CALLEE_GOSUB) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GOSUB])) {
+                       struct ast_app *theapp;
+                       const char *gosub_result;
+                       char *gosub_args, *gosub_argstart;
+
+                       res = ast_autoservice_start(chan);
+                       if (res) {
+                               ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
+                               res = -1;
+                       }
+
+                       theapp = pbx_findapp("Gosub");
+
+                       if (theapp && !res) {   /* XXX why check res here ? */
+                               replace_macro_delimiter(opt_args[OPT_ARG_CALLEE_GOSUB]);
+
+                               /* Set where we came from */
+                               ast_copy_string(peer->context, "app_dial_gosub_virtual_context", sizeof(peer->context));
+                               ast_copy_string(peer->exten, "s", sizeof(peer->exten));
+                               peer->priority = 0;
+
+                               gosub_argstart = strchr(opt_args[OPT_ARG_CALLEE_GOSUB], '|');
+                               if (gosub_argstart) {
+                                       *gosub_argstart = 0;
+                                       asprintf(&gosub_args, "%s|s|1(%s)", opt_args[OPT_ARG_CALLEE_GOSUB], gosub_argstart + 1);
+                                       *gosub_argstart = '|';
+                               } else {
+                                       asprintf(&gosub_args, "%s|s|1", opt_args[OPT_ARG_CALLEE_GOSUB]);
+                               }
+
+                               if (gosub_args) {
+                                       res = pbx_exec(peer, theapp, gosub_args);
+                                       ast_pbx_run(peer);
+                                       free(gosub_args);
+                                       if (option_debug)
+                                               ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
+                               } else
+                                       ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
+                               
+                               res = 0;
+                       } else {
+                               ast_log(LOG_ERROR, "Could not find application Gosub\n");
+                               res = -1;
+                       }
+
+                       if (ast_autoservice_stop(chan) < 0) {
+                               ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
+                               res = -1;
+                       }
+
+                       if (!res && (gosub_result = pbx_builtin_getvar_helper(peer, "GOSUB_RESULT"))) {
+                               char *gosub_transfer_dest;
+
+                               if (!strcasecmp(gosub_result, "BUSY")) {
+                                       ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
+                                       ast_set_flag(peerflags, OPT_GO_ON);
+                                       res = -1;
+                               } else if (!strcasecmp(gosub_result, "CONGESTION") || !strcasecmp(gosub_result, "CHANUNAVAIL")) {
+                                       ast_copy_string(pa.status, gosub_result, sizeof(pa.status));
+                                       ast_set_flag(peerflags, OPT_GO_ON);     
+                                       res = -1;
+                               } else if (!strcasecmp(gosub_result, "CONTINUE")) {
+                                       /* hangup peer and keep chan alive assuming the macro has changed 
+                                          the context / exten / priority or perhaps 
+                                          the next priority in the current exten is desired.
+                                       */
+                                       ast_set_flag(peerflags, OPT_GO_ON);     
+                                       res = -1;
+                               } else if (!strcasecmp(gosub_result, "ABORT")) {
+                                       /* Hangup both ends unless the caller has the g flag */
+                                       res = -1;
+                               } else if (!strncasecmp(gosub_result, "GOTO:", 5) && (gosub_transfer_dest = ast_strdupa(gosub_result + 5))) {
+                                       res = -1;
+                                       /* perform a transfer to a new extension */
+                                       if (strchr(gosub_transfer_dest, '^')) { /* context^exten^priority*/
+                                               replace_macro_delimiter(gosub_transfer_dest);
+                                               if (!ast_parseable_goto(chan, gosub_transfer_dest))
+                                                       ast_set_flag(peerflags, OPT_GO_ON);
+                                       }
+                               }
+                       }
+               }
+
                if (!res) {
                        if (calldurationlimit > 0) {
-                               chan->whentohangup = time(NULL) + calldurationlimit;
+                               peer->whentohangup = time(NULL) + calldurationlimit;
                        }
                        if (!ast_strlen_zero(dtmfcalled)) { 
                                if (option_verbose > 2)
@@ -1661,25 +1805,28 @@ out:
                ast_indicate(chan, -1);
        }
        ast_channel_early_bridge(chan, NULL);
-       hanguptree(outgoing, NULL);
+       hanguptree(outgoing, NULL, 0);  /* In this case, there's no answer anywhere */
        pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
        senddialendevent(chan, pa.status);
-       if (option_debug)
-               ast_log(LOG_DEBUG, "Exiting with DIALSTATUS=%s.\n", pa.status);
+       ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);
        
-       if ((ast_test_flag(peerflags, OPT_GO_ON)) && (!chan->_softhangup) && (res != AST_PBX_KEEPALIVE))
+       if ((ast_test_flag(peerflags, OPT_GO_ON)) && (!chan->_softhangup) && (res != AST_PBX_KEEPALIVE)) {
+               if (calldurationlimit)
+                       chan->whentohangup = 0;
                res = 0;
+       }
 
 done:
-       ast_module_user_remove(u);      /* XXX probably not the right place for this. */
        return res;
 }
 
 static int dial_exec(struct ast_channel *chan, void *data)
 {
        struct ast_flags peerflags;
+
        memset(&peerflags, 0, sizeof(peerflags));
-       return dial_exec_full(chan, data, &peerflags);
+
+       return dial_exec_full(chan, data, &peerflags, NULL);
 }
 
 static int retrydial_exec(struct ast_channel *chan, void *data)
@@ -1687,7 +1834,6 @@ static int retrydial_exec(struct ast_channel *chan, void *data)
        char *announce = NULL, *dialdata = NULL;
        const char *context = NULL;
        int sleep = 0, loops = 0, res = -1;
-       struct ast_module_user *u;
        struct ast_flags peerflags;
        
        if (ast_strlen_zero(data)) {
@@ -1695,15 +1841,13 @@ static int retrydial_exec(struct ast_channel *chan, void *data)
                return -1;
        }       
 
-       u = ast_module_user_add(chan);
-
        announce = ast_strdupa(data);
 
        memset(&peerflags, 0, sizeof(peerflags));
 
        if ((dialdata = strchr(announce, '|'))) {
                *dialdata++ = '\0';
-               if ((sleep = atoi(dialdata))) {
+               if (sscanf(dialdata, "%d", &sleep) == 1) {
                        sleep *= 1000;
                } else {
                        ast_log(LOG_ERROR, "%s requires the numerical argument <sleep>\n",rapp);
@@ -1711,7 +1855,7 @@ static int retrydial_exec(struct ast_channel *chan, void *data)
                }
                if ((dialdata = strchr(dialdata, '|'))) {
                        *dialdata++ = '\0';
-                       if (!(loops = atoi(dialdata))) {
+                       if (sscanf(dialdata, "%d", &loops) != 1) {
                                ast_log(LOG_ERROR, "%s requires the numerical argument <loops>\n",rapp);
                                goto done;
                        }
@@ -1735,11 +1879,16 @@ static int retrydial_exec(struct ast_channel *chan, void *data)
 
        res = 0;
        while (loops) {
+               int continue_exec;
+
                chan->data = "Retrying";
                if (ast_test_flag(chan, AST_FLAG_MOH))
                        ast_moh_stop(chan);
 
-               if ((res = dial_exec_full(chan, dialdata, &peerflags)) == 0) {
+               res = dial_exec_full(chan, dialdata, &peerflags, &continue_exec);
+               if (continue_exec)
+                       break;
+               if (res == 0) {
                        if (ast_test_flag(&peerflags, OPT_DTMF_EXIT)) {
                                if (!(res = ast_streamfile(chan, announce, chan->language)))
                                        res = ast_waitstream(chan, AST_DIGIT_ANY);
@@ -1772,29 +1921,41 @@ static int retrydial_exec(struct ast_channel *chan, void *data)
        }
        if (loops == 0)
                res = 0;
-       
+       else if (res == 1)
+               res = 0;
+
        if (ast_test_flag(chan, AST_FLAG_MOH))
                ast_moh_stop(chan);
-done:
-       ast_module_user_remove(u);
+ done:
        return res;
 }
 
 static int unload_module(void)
 {
        int res;
+       struct ast_context *con;
 
        res = ast_unregister_application(app);
        res |= ast_unregister_application(rapp);
 
-       ast_module_user_hangup_all();
-       
+       if ((con = ast_context_find("app_dial_gosub_virtual_context")))
+               ast_context_remove_extension2(con, "s", 1, NULL);
+
        return res;
 }
 
 static int load_module(void)
 {
        int res;
+       struct ast_context *con;
+
+       con = ast_context_find("app_dial_gosub_virtual_context");
+       if (!con)
+               con = ast_context_create(NULL, "app_dial_gosub_virtual_context", "app_dial");
+       if (!con)
+               ast_log(LOG_ERROR, "Dial virtual context 'app_dial_gosub_virtual_context' does not exist and unable to create\n");
+       else
+               ast_add_extension2(con, 1, "s", 1, NULL, NULL, "KeepAlive", ast_strdup(""), ast_free, "app_dial");
 
        res = ast_register_application(app, dial_exec, synopsis, descrip);
        res |= ast_register_application(rapp, retrydial_exec, rsynopsis, rdescrip);