Via bug9228, no way to create macros via AEL, and some of the apps allow you to call...
authorSteve Murphy <murf@digium.com>
Tue, 19 Jun 2007 23:36:34 +0000 (23:36 +0000)
committerSteve Murphy <murf@digium.com>
Tue, 19 Jun 2007 23:36:34 +0000 (23:36 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@70161 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_dial.c
apps/app_queue.c
apps/app_rpt.c

index a4cc3cb..c8b0007 100644 (file)
@@ -123,6 +123,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"
+"    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"
@@ -184,14 +188,27 @@ 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. This will also\n"
+"                          have the application jump to priority n+101 if the\n"
+"                          'j' option is set.\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";
@@ -237,6 +254,7 @@ enum {
        OPT_CALLEE_PARK =       (1 << 25),
        OPT_CALLER_PARK =       (1 << 26),
        OPT_IGNORE_FORWARDING = (1 << 27),
+       OPT_CALLEE_GOSUB =      (1 << 28),
 };
 
 #define DIAL_STILLGOING                        (1 << 30)
@@ -249,6 +267,7 @@ 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,
@@ -267,6 +286,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('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),
@@ -280,10 +301,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),
 });
 
 /*
@@ -1611,6 +1631,67 @@ 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;
+
+                       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]);
+                               res = pbx_exec(peer, theapp, opt_args[OPT_ARG_CALLEE_GOSUB]);
+                               if (option_debug)
+                                       ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
+                               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;
index 06e9a91..0f425a2 100644 (file)
@@ -130,7 +130,7 @@ static char *app = "Queue";
 static char *synopsis = "Queue a call for a call queue";
 
 static char *descrip =
-"  Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI][|macro]):\n"
+"  Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI][|macro][|gosub]):\n"
 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
 "This application will return to the dialplan if the queue does not exist, or\n"
 "any of the join options cause the caller to not enter the queue.\n"
@@ -156,6 +156,8 @@ static char *descrip =
 "calling party's channel once they are connected to a queue member.\n"
 "  The optional macro parameter will run a macro on the \n"
 "calling party's channel once they are connected to a queue member.\n"
+"  The optional gosub parameter will run a gosub on the \n"
+"calling party's channel once they are connected to a queue member.\n"
 "  The timeout will cause the queue to fail out after a specified number of\n"
 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
 "  This application sets the following channel variable upon completion:\n"
@@ -372,6 +374,7 @@ struct call_queue {
        char monfmt[8];                     /*!< Format to use when recording calls */
        int montype;                        /*!< Monitor type  Monitor vs. MixMonitor */
        char membermacro[32];               /*!< Macro to run upon member connection */
+       char membergosub[32];               /*!< Gosub to run upon member connection */
        char sound_next[80];                /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
        char sound_thereare[80];            /*!< Sound file: "There are currently" (def. queue-thereare) */
        char sound_calls[80];               /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
@@ -729,6 +732,7 @@ static void init_queue(struct call_queue *q)
        q->autofill = autofill_default;
        q->montype = montype_default;
        q->membermacro[0] = '\0';
+       q->membergosub[0] = '\0';
        q->moh[0] = '\0';
        q->announce[0] = '\0';
        q->context[0] = '\0';
@@ -871,6 +875,8 @@ static void queue_set_param(struct call_queue *q, const char *param, const char
                ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
        } else if (!strcasecmp(param, "membermacro")) {
                ast_copy_string(q->membermacro, val, sizeof(q->membermacro));
+       } else if (!strcasecmp(param, "membergosub")) {
+               ast_copy_string(q->membergosub, val, sizeof(q->membergosub));
        } else if (!strcasecmp(param, "queue-youarenext")) {
                ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
        } else if (!strcasecmp(param, "queue-thereare")) {
@@ -2399,7 +2405,7 @@ static void send_agent_complete(const struct queue_ent *qe, const char *queuenam
                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
 }
 
-static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on, const char *agi, const char *macro)
+static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on, const char *agi, const char *macro, const char *gosub)
 {
        struct member *cur;
        struct callattempt *outgoing = NULL; /* the list of calls we are building */
@@ -2424,6 +2430,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
        char nondataquality = 1;
        char *agiexec = NULL;
        char *macroexec = NULL;
+       char *gosubexec = NULL;
        int ret = 0;
        const char *monitorfilename;
        const char *monitor_exec;
@@ -2768,6 +2775,43 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
                        }
                }
 
+               /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
+               /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
+               if (!ast_strlen_zero(gosub)) {
+                               gosubexec = ast_strdupa(gosub);
+               } else {
+                       if (qe->parent->membergosub)
+                               gosubexec = ast_strdupa(qe->parent->membergosub);
+               }
+
+               if (!ast_strlen_zero(gosubexec)) {
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "app_queue: gosub=%s.\n", gosubexec);
+                       
+                       res = ast_autoservice_start(qe->chan);
+                       if (res) {
+                               ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
+                               res = -1;
+                       }
+                       
+                       app = pbx_findapp("Gosub");
+                       
+                       if (app) {
+                               res = pbx_exec(qe->chan, app, gosubexec);
+                               if (option_debug)
+                                       ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
+                               res = 0;
+                       } else {
+                               ast_log(LOG_ERROR, "Could not find application Gosub\n");
+                               res = -1;
+                       }
+               
+                       if (ast_autoservice_stop(qe->chan) < 0) {
+                               ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
+                               res = -1;
+                       }
+               }
+
                if (!ast_strlen_zero(agi)) {
                        ast_debug(1, "app_queue: agi=%s.\n", agi);
                        app = pbx_findapp("agi");
@@ -3405,6 +3449,7 @@ static int queue_exec(struct ast_channel *chan, void *data)
                AST_APP_ARG(queuetimeoutstr);
                AST_APP_ARG(agi);
                AST_APP_ARG(macro);
+               AST_APP_ARG(gosub);
        );
        /* Our queue entry */
        struct queue_ent qe;
@@ -3548,7 +3593,7 @@ check_turns:
                                }
 
                                /* Try calling all queue members for 'timeout' seconds */
-                               res = try_calling(&qe, args.options, args.announceoverride, args.url, &go_on, args.agi, args.macro);
+                               res = try_calling(&qe, args.options, args.announceoverride, args.url, &go_on, args.agi, args.macro, args.gosub);
                                if (res) {
                                        if (res < 0) {
                                                if (!qe.handled) {
index 7ae10fb..40880da 100644 (file)
 
 #define        MAXDTMF 32
 #define        MAXMACRO 2048
+#define        MAXGOSUB 2048
 #define        MACROTIME 100
+#define        GOSUBTIME 100
 #define        MACROPTIME 500
+#define        GOSUBPTIME 500
 #define        DTMF_TIMEOUT 3
 
 #ifdef __RPT_NOTCH
 #define        NODES "nodes"
 #define MEMORY "memory"
 #define MACRO "macro"
+#define GOSUB "gosub"
 #define        FUNCTIONS "functions"
 #define TELEMETRY "telemetry"
 #define MORSE "morse"
@@ -183,7 +187,7 @@ enum {REM_OFF, REM_MONITOR, REM_TX};
 enum {ID, PROC, TERM, COMPLETE, UNKEY, REMDISC, REMALREADY, REMNOTFOUND, REMGO,
        CONNECTED, CONNFAIL, STATUS, TIMEOUT, ID1, STATS_TIME,
        STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE, REV_PATCH,
-       TAILMSG, MACRO_NOTFOUND, MACRO_BUSY, LASTNODEKEY};
+       TAILMSG, MACRO_NOTFOUND, GOSUB_NOTFOUND, MACRO_BUSY, GOSUB_BUSY, LASTNODEKEY};
 
 enum {REM_SIMPLEX, REM_MINUS, REM_PLUS};
 
@@ -418,7 +422,9 @@ static struct rpt
                );
                char memory[80];
                char macro[80];
+               char gosub[80];
                char startupmacro[80];
+               char startupgosub[80];
                int iobase;
                char funcchar;
                char endchar;
@@ -437,6 +443,7 @@ static struct rpt
        char enable;
        char dtmfbuf[MAXDTMF];
        char macrobuf[MAXMACRO];
+       char gosubbuf[MAXGOSUB];
        char rem_dtmfbuf[MAXDTMF];
        char lastdtmfcommand[MAXDTMF];
        char cmdnode[50];
@@ -474,6 +481,7 @@ static struct rpt
        char patchcontext[MAXPATCHCONTEXT];
        int patchdialtime;
        int macro_longest;
+       int gosub_longest;
        int phone_longestfunc;
        int dphone_longestfunc;
        int link_longestfunc;
@@ -484,6 +492,7 @@ static struct rpt
        time_t disgorgetime;
        time_t lastthreadrestarttime;
        long macrotimer;
+       long gosubtimer;
        char lastnodewhichkeyedusup[MAXNODESTR];
 #ifdef __RPT_NOTCH
        struct rptfilter
@@ -784,6 +793,7 @@ static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int c
 static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
 static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
 static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
+static int function_gosub(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
 /*
 * Function table
 */
@@ -796,6 +806,7 @@ static struct function_table_tag function_table[] = {
        {"status", function_status},
        {"remote", function_remote},
        {"macro", function_macro}
+       {"gosub", function_gosub}
 } ;
 
 /*
@@ -955,6 +966,7 @@ static void load_rpt_vars(int n, int init)
        rpt_vars[n].p.politeid = POLITEID;
        ast_copy_string(rpt_vars[n].p.memory, MEMORY, sizeof(rpt_vars[n].p.memory));
        ast_copy_string(rpt_vars[n].p.macro, MACRO, sizeof(rpt_vars[n].p.macro));
+       ast_copy_string(rpt_vars[n].p.gosub, GOSUB, sizeof(rpt_vars[n].p.gosub));
        rpt_vars[n].p.iobase = DEFAULT_IOBASE;
        ast_copy_string(rpt_vars[n].p.functions, FUNCTIONS, sizeof(rpt_vars[n].p.functions));
        rpt_vars[n].p.simple = 1;
@@ -1014,8 +1026,12 @@ static void load_rpt_vars(int n, int init)
                        ast_copy_string(rpt_vars[n].p.memory, var->value, sizeof(rpt_vars[n].p.memory));
                } else if (!strcmp(var->name, "macro")) {
                        ast_copy_string(rpt_vars[n].p.macro, var->value, sizeof(rpt_vars[n].p.macro));
+               } else if (!strcmp(var->name, "gosub")) {
+                       ast_copy_string(rpt_vars[n].p.gosub, var->value, sizeof(rpt_vars[n].p.gosub));
                } else if (!strcmp(var->name, "startup_macro")) {
                        ast_copy_string(rpt_vars[n].p.startupmacro, var->value, sizeof(rpt_vars[n].p.startupmacro));
+               } else if (!strcmp(var->name, "startup_gosub")) {
+                       ast_copy_string(rpt_vars[n].p.startupgosub, var->value, sizeof(rpt_vars[n].p.startupgosub));
                } else if (!strcmp(var->name, "iobase")) {
                        /* do not use atoi() here, we need to be able to have
                           the input specified in hex or decimal so we use
@@ -1101,6 +1117,12 @@ static void load_rpt_vars(int n, int init)
                if ((j = strlen(vp->name)) > rpt_vars[n].macro_longest)
                        rpt_vars[n].macro_longest = j;
        }
+
+       rpt_vars[n].gosub_longest = 1;
+       for (vp = ast_variable_browse(cfg, rpt_vars[n].p.gosub); vp; vp = vp->next) {
+               if ((j = strlen(vp->name)) > rpt_vars[n].gosub_longest)
+                       rpt_vars[n].gosub_longest = j;
+       }
        ast_mutex_unlock(&rpt_vars[n].lock);
 }
 
@@ -1961,11 +1983,21 @@ static void *rpt_tele_thread(void *this)
                wait_interval(myrpt, DLY_TELEM, mychannel);
                res = ast_streamfile(mychannel, "rpt/macro_notfound", mychannel->language);
                break;
+       case GOSUB_NOTFOUND:
+               /* wait a little bit */
+               wait_interval(myrpt, DLY_TELEM, mychannel);
+               res = ast_streamfile(mychannel, "rpt/gosub_notfound", mychannel->language);
+               break;
        case MACRO_BUSY:
                /* wait a little bit */
                wait_interval(myrpt, DLY_TELEM, mychannel);
                res = ast_streamfile(mychannel, "rpt/macro_busy", mychannel->language);
                break;
+       case GOSUB_BUSY:
+               /* wait a little bit */
+               wait_interval(myrpt, DLY_TELEM, mychannel);
+               res = ast_streamfile(mychannel, "rpt/gosub_busy", mychannel->language);
+               break;
        case UNKEY:
                if (myrpt->patchnoct && myrpt->callmode) { /* If no CT during patch configured, then don't send one */
                        imdone = 1;
@@ -3284,6 +3316,54 @@ static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int co
 }
 
 /*
+*  Gosub
+*/
+
+static int function_gosub(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
+{
+
+       const char *val;
+       int     i;
+       struct ast_channel *mychannel;
+
+       if ((!myrpt->remote) && (!myrpt->enable))
+               return DC_ERROR;
+
+       if (debug) 
+               ast_log(LOG_DEBUG, "@@@@ gosub param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
+       
+       mychannel = myrpt->remchannel;
+
+       if (ast_strlen_zero(digitbuf)) /* needs 1 digit */
+               return DC_INDETERMINATE;
+                       
+       for (i = 0; i < digitbuf[i]; i++) {
+               if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
+                       return DC_ERROR;
+       }
+   
+       if (*digitbuf == '0')
+               val = myrpt->p.startupgosub;
+       else
+               val = ast_variable_retrieve(myrpt->cfg, myrpt->p.gosub, digitbuf);
+       /* param was 1 for local buf */
+       if (!val) {
+               rpt_telemetry(myrpt, GOSUB_NOTFOUND, NULL);
+               return DC_COMPLETE;
+       }                       
+       rpt_mutex_lock(&myrpt->lock);
+       if ((sizeof(myrpt->gosubbuf) - strlen(myrpt->gosubbuf)) < strlen(val)) {
+               rpt_mutex_unlock(&myrpt->lock);
+               rpt_telemetry(myrpt, GOSUB_BUSY, NULL);
+               return DC_ERROR;
+       }
+       myrpt->gosubtimer = GOSUBTIME;
+       strncat(myrpt->gosubbuf, val, sizeof(myrpt->gosubbuf) - 1);
+       rpt_mutex_unlock(&myrpt->lock);
+       return DC_COMPLETE;     
+}
+
+/*
 * COP - Control operator
 */
 
@@ -5798,6 +5878,9 @@ static void *rpt(void *this)
        if (myrpt->p.startupmacro) {
                snprintf(myrpt->macrobuf, sizeof(myrpt->macrobuf), "PPPP%s", myrpt->p.startupmacro);
        }
+       if (myrpt->p.startupgosub) {
+               snprintf(myrpt->gosubbuf, sizeof(myrpt->gosubbuf), "PPPP%s", myrpt->p.startupgosub);
+       }
        rpt_mutex_unlock(&myrpt->lock);
        val = 0;
        ast_channel_setoption(myrpt->rxchannel, AST_OPTION_TONE_VERIFY, &val, sizeof(char), 0);
@@ -6228,6 +6311,11 @@ static void *rpt(void *this)
                        myrpt->macrotimer -= elap;
                if (myrpt->macrotimer < 0)
                        myrpt->macrotimer = 0;
+               /* do gosub timers */
+               if (myrpt->gosubtimer)
+                       myrpt->gosubtimer -= elap;
+               if (myrpt->gosubtimer < 0)
+                       myrpt->gosubtimer = 0;
                /* Execute scheduler appx. every 2 tenths of a second */
                if (myrpt->skedtimer <= 0) {
                        myrpt->skedtimer = 200;
@@ -6248,6 +6336,16 @@ static void *rpt(void *this)
                        local_dtmf_helper(myrpt, c);
                } else
                        rpt_mutex_unlock(&myrpt->lock);
+               c = myrpt->gosubbuf[0];
+               if (c && (!myrpt->gosubtimer)) {
+                       myrpt->gosubtimer = GOSUBTIME;
+                       memmove(myrpt->gosubbuf, myrpt->gosubbuf + 1, sizeof(myrpt->gosubbuf) - 1);
+                       if ((c == 'p') || (c == 'P'))
+                               myrpt->gosubtimer = GOSUBPTIME;
+                       rpt_mutex_unlock(&myrpt->lock);
+                       local_dtmf_helper(myrpt, c);
+               } else
+                       rpt_mutex_unlock(&myrpt->lock);
                if (who == myrpt->rxchannel) { /* if it was a read from rx */
                        f = ast_read(myrpt->rxchannel);
                        if (!f) {
@@ -7072,6 +7170,10 @@ static int rpt_exec(struct ast_channel *chan, void *data)
                myrpt->remchannel = chan; /* Save copy of channel */
                snprintf(myrpt->macrobuf, sizeof(myrpt->macrobuf), "PPPP%s", myrpt->p.startupmacro);
        }
+       if (myrpt->p.startupgosub) {
+               myrpt->remchannel = chan; /* Save copy of channel */
+               snprintf(myrpt->gosubbuf, sizeof(myrpt->gosubbuf), "PPPP%s", myrpt->p.startupgosub);
+       }
        myrpt->reload = 0;
        rpt_mutex_unlock(&myrpt->lock);
        setrem(myrpt); 
@@ -7123,6 +7225,10 @@ static int rpt_exec(struct ast_channel *chan, void *data)
                        myrpt->macrotimer -= elap;
                if (myrpt->macrotimer < 0)
                        myrpt->macrotimer = 0;
+               if (myrpt->gosubtimer)
+                       myrpt->gosubtimer -= elap;
+               if (myrpt->gosubtimer < 0)
+                       myrpt->gosubtimer = 0;
                rpt_mutex_unlock(&myrpt->lock);
                if (!ms)
                        continue;
@@ -7244,6 +7350,17 @@ static int rpt_exec(struct ast_channel *chan, void *data)
                                if (handle_remote_dtmf_digit(myrpt, c, &keyed, 0) == -1)
                                        break;
                                continue;
+                       }
+                       c = myrpt->gosubbuf[0];
+                       if (c && (!myrpt->gosubtimer)) {
+                               myrpt->gosubtimer = GOSUBTIME;
+                               memmove(myrpt->gosubbuf, myrpt->gosubbuf + 1, sizeof(myrpt->gosubbuf) - 1);
+                               if ((c == 'p') || (c == 'P'))
+                                       myrpt->gosubtimer = GOSUBPTIME;
+                               rpt_mutex_unlock(&myrpt->lock);
+                               if (handle_remote_dtmf_digit(myrpt, c, &keyed, 0) == -1)
+                                       break;
+                               continue;
                        } 
                        rpt_mutex_unlock(&myrpt->lock);
                        continue;