Applications no longer need to call ast_module_user_add and ast_module_user_remove...
[asterisk/asterisk.git] / apps / app_dial.c
index 7f731bc..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,10 +303,9 @@ 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),
 });
 
 /*
@@ -299,17 +319,20 @@ struct chanlist {
 };
 
 
-static void hanguptree(struct chanlist *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 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);
        }
 }
 
@@ -428,6 +451,7 @@ 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;
@@ -458,10 +482,12 @@ static void do_forward(struct chanlist *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)
@@ -498,18 +524,18 @@ static void do_forward(struct chanlist *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(c);
+                       ast_hangup(original);
                }
        }
 }
@@ -526,7 +552,7 @@ struct privacy_args {
 static struct ast_channel *wait_for_answer(struct ast_channel *in,
        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;
@@ -567,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);
@@ -712,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 ? */
@@ -745,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;
@@ -758,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);
@@ -771,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;
                                }
@@ -782,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) || 
@@ -798,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;
@@ -937,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++;
@@ -1109,15 +1142,9 @@ static int setup_privacy_args(struct privacy_args *pa,
                return 0;
        } 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);
-               }
                return 0; /* 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);
-               }
                return 0; /* is this right??? */
        } else if (pa->privdb_val == AST_PRIVACY_UNKNOWN ) {
                /* Get the user's intro, store it in priv-callerintros/$CID, 
@@ -1126,13 +1153,13 @@ static int setup_privacy_args(struct privacy_args *pa,
 
                /* 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));
+               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) {
+               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.
                        */
@@ -1145,6 +1172,7 @@ static int setup_privacy_args(struct privacy_args *pa,
                           "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 */
@@ -1164,18 +1192,17 @@ static int setup_privacy_args(struct privacy_args *pa,
        return 1;       /* success */
 }
 
-static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags)
+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 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;
@@ -1183,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;
@@ -1201,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;
        }
 
@@ -1231,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)
@@ -1259,9 +1290,17 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                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;
@@ -1279,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 |
@@ -1296,7 +1336,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                        handle_cause(cause, &num);
                        if (!rest)      /* we are on the last destination */
                                chan->hangupcause = cause;
-                       free(tmp);
+                       ast_free(tmp);
                        continue;
                }
                pbx_builtin_setvar_helper(tc, "DIALEDPEERNUMBER", numsubst);
@@ -1343,7 +1383,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                        }
                        if (!tc) {
                                handle_cause(cause, &num);
-                               free(tmp);
+                               ast_free(tmp);
                                continue;
                        }
                }
@@ -1381,8 +1421,14 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                        ast_app_group_set_channel(tc, outbound_group);
 
                /* Inherit context and extension */
-               ast_copy_string(tc->dialcontext, chan->context, sizeof(tc->dialcontext));
-               ast_copy_string(tc->exten, chan->exten, sizeof(tc->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));
 
                res = ast_call(tc, numsubst, 0);        /* Place the call, but don't wait on the answer */
 
@@ -1393,13 +1439,12 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                /* 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(tc);
                        tc = NULL;
-                       free(tmp);
+                       ast_free(tmp);
                        continue;
                } else {
                        senddialevent(chan, tc);
@@ -1437,7 +1482,14 @@ 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);
@@ -1446,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) {
@@ -1466,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)
@@ -1479,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) {
@@ -1516,8 +1567,10 @@ 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);
-                       res = 1;
+                       hanguptree(outgoing, NULL, ast_test_flag(&opts, OPT_CANCEL_ELSEWHERE ? 1 : 0));
+                       if (continue_exec)
+                               *continue_exec = 1;
+                       res = 0;
                        goto done;
                }
 
@@ -1536,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");
@@ -1554,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));
@@ -1587,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)
@@ -1675,29 +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;
-       int res = 0;
 
        memset(&peerflags, 0, sizeof(peerflags));
-       res = dial_exec_full(chan, data, &peerflags);
 
-       return (res >= 0 ? 0 : -1);
+       return dial_exec_full(chan, data, &peerflags, NULL);
 }
 
 static int retrydial_exec(struct ast_channel *chan, void *data)
@@ -1705,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)) {
@@ -1713,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);
@@ -1729,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;
                        }
@@ -1753,14 +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);
 
-               res = dial_exec_full(chan, dialdata, &peerflags);
-               if (res == 1) {
+               res = dial_exec_full(chan, dialdata, &peerflags, &continue_exec);
+               if (continue_exec)
                        break;
-               } else if (res == 0) {
+               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);
@@ -1798,27 +1926,36 @@ static int retrydial_exec(struct ast_channel *chan, void *data)
 
        if (ast_test_flag(chan, AST_FLAG_MOH))
                ast_moh_stop(chan);
-
  done:
-       ast_module_user_remove(u);
        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);