Applications no longer need to call ast_module_user_add and ast_module_user_remove...
[asterisk/asterisk.git] / apps / app_dial.c
index e83f80a..4438daa 100644 (file)
@@ -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);
        }
 }
 
@@ -459,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)
@@ -506,7 +531,7 @@ static void do_forward(struct chanlist *o,
                        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 */
@@ -527,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;
@@ -568,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);
@@ -713,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 ? */
@@ -746,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;
@@ -759,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);
@@ -772,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;
                                }
@@ -799,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;
@@ -1113,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, 
@@ -1130,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.
                        */
@@ -1172,15 +1195,14 @@ static int setup_privacy_args(struct privacy_args *pa,
 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;
@@ -1211,8 +1233,6 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                return -1;
        }
 
-       u = ast_module_user_add(chan);  /* XXX is this the right place ? */
-
        parse = ast_strdupa(data);
        
        AST_STANDARD_APP_ARGS(args, parse);
@@ -1272,10 +1292,15 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
 
        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;
@@ -1293,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 |
@@ -1310,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);
@@ -1357,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;
                        }
                }
@@ -1413,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);
@@ -1473,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) {
@@ -1493,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)
@@ -1506,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) {
@@ -1543,7 +1567,7 @@ 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;
@@ -1565,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");
@@ -1583,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));
@@ -1616,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)
@@ -1704,17 +1805,18 @@ 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;
 }
 
@@ -1732,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)) {
@@ -1740,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);
@@ -1756,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;
                        }
@@ -1828,25 +1927,35 @@ 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);