Merged revisions 127663 via svnmerge from
[asterisk/asterisk.git] / main / app.c
index d3304b9..0ea5cef 100644 (file)
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-#include <signal.h>
-#include <errno.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
+#endif
 #include <regex.h>
+#include <sys/file.h> /* added this to allow to compile, sorry! */
+#include <signal.h>
 
+#include "asterisk/paths.h"    /* use ast_config_AST_DATA_DIR */
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
 #include "asterisk/file.h"
 #include "asterisk/app.h"
 #include "asterisk/dsp.h"
-#include "asterisk/logger.h"
-#include "asterisk/options.h"
 #include "asterisk/utils.h"
 #include "asterisk/lock.h"
 #include "asterisk/indications.h"
@@ -78,14 +72,14 @@ int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect,
                maxlen = size;
        
        if (!timeout && chan->pbx)
-               timeout = chan->pbx->dtimeout;
+               timeout = chan->pbx->dtimeoutms / 1000.0;
        else if (!timeout)
                timeout = 5;
 
        if ((ts = ast_get_indication_tone(chan->zone, "dial")) && ts->data[0])
                res = ast_playtones_start(chan, 0, ts->data, 0);
        else 
-               ast_log(LOG_NOTICE,"Huh....? no dial for indications?\n");
+               ast_log(LOG_NOTICE, "Huh....? no dial for indications?\n");
        
        for (x = strlen(collect); x < maxlen; ) {
                res = ast_waitfordigit(chan, timeout);
@@ -125,7 +119,7 @@ int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxl
                s[0] = '\0';
 
        if (!prompt)
-               prompt="";
+               prompt = "";
 
        filename = ast_strdupa(prompt);
        while ((front = strsep(&filename, "&"))) {
@@ -136,8 +130,8 @@ int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxl
                }
                if (ast_strlen_zero(filename)) {
                        /* set timeouts for the last prompt */
-                       fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
-                       to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
+                       fto = c->pbx ? c->pbx->rtimeoutms : 6000;
+                       to = c->pbx ? c->pbx->dtimeoutms : 2000;
 
                        if (timeout > 0) 
                                fto = to = timeout;
@@ -148,7 +142,7 @@ int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxl
                           get rid of the long timeout between 
                           prompts, and make it 50ms */
                        fto = 50;
-                       to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
+                       to = c->pbx ? c->pbx->dtimeoutms : 2000;
                }
                res = ast_readstring(c, s, maxlen, to, fto, "#");
                if (!ast_strlen_zero(s))
@@ -158,6 +152,8 @@ int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxl
        return res;
 }
 
+/* The lock type used by ast_lock_path() / ast_unlock_path() */
+static enum AST_LOCK_TYPE ast_lock_type = AST_LOCK_TYPE_LOCKFILE;
 
 int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
 {
@@ -181,22 +177,30 @@ int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxle
 
 static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
 static int (*ast_inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
+static int (*ast_inboxcount2_func)(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs) = NULL;
+static int (*ast_sayname_func)(struct ast_channel *chan, const char *mailbox, const char *context) = NULL;
 static int (*ast_messagecount_func)(const char *context, const char *mailbox, const char *folder) = NULL;
 
 void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder),
                              int (*inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs),
-                             int (*messagecount_func)(const char *context, const char *mailbox, const char *folder))
+                             int (*inboxcount2_func)(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs),
+                             int (*messagecount_func)(const char *context, const char *mailbox, const char *folder),
+                             int (*sayname_func)(struct ast_channel *chan, const char *mailbox, const char *context))
 {
        ast_has_voicemail_func = has_voicemail_func;
        ast_inboxcount_func = inboxcount_func;
+       ast_inboxcount2_func = inboxcount2_func;
        ast_messagecount_func = messagecount_func;
+       ast_sayname_func = sayname_func;
 }
 
 void ast_uninstall_vm_functions(void)
 {
        ast_has_voicemail_func = NULL;
        ast_inboxcount_func = NULL;
+       ast_inboxcount2_func = NULL;
        ast_messagecount_func = NULL;
+       ast_sayname_func = NULL;
 }
 
 int ast_app_has_voicemail(const char *mailbox, const char *folder)
@@ -216,12 +220,39 @@ int ast_app_has_voicemail(const char *mailbox, const char *folder)
 int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
 {
        static int warned = 0;
-       if (newmsgs)
+       if (newmsgs) {
                *newmsgs = 0;
-       if (oldmsgs)
+       }
+       if (oldmsgs) {
                *oldmsgs = 0;
-       if (ast_inboxcount_func)
+       }
+       if (ast_inboxcount_func) {
                return ast_inboxcount_func(mailbox, newmsgs, oldmsgs);
+       }
+
+       if (!warned) {
+               warned++;
+               ast_verb(3, "Message count requested for mailbox %s but voicemail not loaded.\n", mailbox);
+       }
+
+       return 0;
+}
+
+int ast_app_inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+{
+       static int warned = 0;
+       if (newmsgs) {
+               *newmsgs = 0;
+       }
+       if (oldmsgs) {
+               *oldmsgs = 0;
+       }
+       if (urgentmsgs) {
+               *urgentmsgs = 0;
+       }
+       if (ast_inboxcount_func) {
+               return ast_inboxcount2_func(mailbox, urgentmsgs, newmsgs, oldmsgs);
+       }
 
        if (!warned) {
                warned++;
@@ -231,6 +262,13 @@ int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
        return 0;
 }
 
+int ast_app_sayname(struct ast_channel *chan, const char *mailbox, const char *context)
+{
+       if (ast_sayname_func)
+               return ast_sayname_func(chan, mailbox, context);
+       return -1;
+}
+
 int ast_app_messagecount(const char *context, const char *mailbox, const char *folder)
 {
        static int warned = 0;
@@ -249,6 +287,7 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch
 {
        const char *ptr;
        int res = 0;
+       struct ast_silence_generator *silgen = NULL;
 
        if (!between)
                between = 100;
@@ -263,6 +302,10 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch
        if (res < 0)
                return res;
 
+       if (ast_opt_transmit_silence) {
+               silgen = ast_channel_start_silence_generator(chan);
+       }
+
        for (ptr = digits; *ptr; ptr++) {
                if (*ptr == 'w') {
                        /* 'w' -- wait half a second */
@@ -279,7 +322,7 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch
                        if ((res = ast_safe_sleep(chan, between)))
                                break;
                } else
-                       ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
+                       ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n", *ptr);
        }
 
        if (peer) {
@@ -289,6 +332,10 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch
                        res = -1;
        }
 
+       if (silgen) {
+               ast_channel_stop_silence_generator(chan, silgen);
+       }
+
        return res;
 }
 
@@ -317,16 +364,16 @@ static int linear_generator(struct ast_channel *chan, void *data, int len, int s
        short buf[2048 + AST_FRIENDLY_OFFSET / 2];
        struct linear_state *ls = data;
        struct ast_frame f = {
-                .frametype = AST_FRAME_VOICE,
-                .subclass = AST_FORMAT_SLINEAR,
-                .data = buf + AST_FRIENDLY_OFFSET / 2,
+               .frametype = AST_FRAME_VOICE,
+               .subclass = AST_FORMAT_SLINEAR,
+               .data.ptr = buf + AST_FRIENDLY_OFFSET / 2,
                .offset = AST_FRIENDLY_OFFSET,
-        };
+       };
        int res;
 
        len = samples * 2;
        if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
-               ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" ,len);
+               ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" , len);
                len = sizeof(buf) - AST_FRIENDLY_OFFSET;
        }
        res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
@@ -435,7 +482,7 @@ int ast_control_streamfile(struct ast_channel *chan, const char *file,
                res = ast_answer(chan);
 
        if (file) {
-               if ((end = strchr(file,':'))) {
+               if ((end = strchr(file, ':'))) {
                        if (!strcasecmp(end, ":end")) {
                                *end = '\0';
                                end++;
@@ -514,6 +561,10 @@ int ast_control_streamfile(struct ast_channel *chan, const char *file,
        if (offsetms) 
                *offsetms = offset / 8; /* samples --> ms ... XXX Assumes 8 kHz */
 
+       /* If we are returning a digit cast it as char */
+       if (res > 0 || chan->stream)
+               res = (char)res;
+
        ast_stopstream(chan);
 
        return res;
@@ -743,8 +794,6 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                } else {
                        ast_frfree(f);
                }
-               if (end == start)
-                       end = time(NULL);
        } else {
                ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
        }
@@ -753,14 +802,29 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                if (silgen)
                        ast_channel_stop_silence_generator(chan, silgen);
        }
-       *duration = end - start;
+
+       /*!\note
+        * Instead of asking how much time passed (end - start), calculate the number
+        * of seconds of audio which actually went into the file.  This fixes a
+        * problem where audio is stopped up on the network and never gets to us.
+        *
+        * Note that we still want to use the number of seconds passed for the max
+        * message, otherwise we could get a situation where this stream is never
+        * closed (which would create a resource leak).
+        */
+       *duration = others[0] ? ast_tellstream(others[0]) / 8000 : 0;
 
        if (!prepend) {
                for (x = 0; x < fmtcnt; x++) {
                        if (!others[x])
                                break;
-                       if (res > 0)
-                               ast_stream_rewind(others[x], totalsilence ? totalsilence - 200 : 200);
+                       /*!\note
+                        * If we ended with silence, trim all but the first 200ms of silence
+                        * off the recording.  However, if we ended with '#', we don't want
+                        * to trim ANY part of the recording.
+                        */
+                       if (res > 0 && totalsilence)
+                               ast_stream_rewind(others[x], totalsilence - 200);
                        ast_truncstream(others[x]);
                        ast_closestream(others[x]);
                }
@@ -775,7 +839,9 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                        realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
                        if (!others[x] || !realfiles[x])
                                break;
-                       ast_stream_rewind(others[x], totalsilence ? totalsilence - 200 : 200);
+                       /*!\note Same logic as above. */
+                       if (totalsilence)
+                               ast_stream_rewind(others[x], totalsilence - 200);
                        ast_truncstream(others[x]);
                        /* add the original file too */
                        while ((fr = ast_readframe(realfiles[x]))) {
@@ -839,7 +905,7 @@ int ast_app_group_split_group(const char *data, char *group, int group_max, char
        if (!ast_strlen_zero(grp))
                ast_copy_string(group, grp, group_max);
        else
-               res = -1;
+               *group = '\0';
 
        if (!ast_strlen_zero(cat))
                ast_copy_string(category, cat, category_max);
@@ -865,14 +931,16 @@ int ast_app_group_set_channel(struct ast_channel *chan, const char *data)
        AST_RWLIST_WRLOCK(&groups);
        AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, list) {
                if ((gi->chan == chan) && ((ast_strlen_zero(category) && ast_strlen_zero(gi->category)) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {
-                       AST_RWLIST_REMOVE_CURRENT(&groups, list);
+                       AST_RWLIST_REMOVE_CURRENT(list);
                        free(gi);
                        break;
                }
        }
-       AST_RWLIST_TRAVERSE_SAFE_END
+       AST_RWLIST_TRAVERSE_SAFE_END;
 
-       if ((gi = calloc(1, len))) {
+       if (ast_strlen_zero(group)) {
+               /* Enable unsetting the group */
+       } else if ((gi = calloc(1, len))) {
                gi->chan = chan;
                gi->group = (char *) gi + sizeof(*gi);
                strcpy(gi->group, group);
@@ -954,11 +1022,11 @@ int ast_app_group_discard(struct ast_channel *chan)
        AST_RWLIST_WRLOCK(&groups);
        AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, list) {
                if (gi->chan == chan) {
-                       AST_RWLIST_REMOVE_CURRENT(&groups, list);
+                       AST_RWLIST_REMOVE_CURRENT(list);
                        ast_free(gi);
                }
        }
-        AST_RWLIST_TRAVERSE_SAFE_END
+       AST_RWLIST_TRAVERSE_SAFE_END;
        AST_RWLIST_UNLOCK(&groups);
        
        return 0;
@@ -987,7 +1055,7 @@ int ast_app_group_list_unlock(void)
 unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
 {
        int argc;
-       char *scan;
+       char *scan, *wasdelim = NULL;
        int paren = 0, quote = 0;
 
        if (!buf || !array || !arraylen)
@@ -1014,19 +1082,23 @@ unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arra
                                /* Literal character, don't parse */
                                memmove(scan, scan + 1, strlen(scan));
                        } else if ((*scan == delim) && !paren && !quote) {
+                               wasdelim = scan;
                                *scan++ = '\0';
                                break;
                        }
                }
        }
 
-       if (*scan)
+       /* If the last character in the original string was the delimiter, then
+        * there is one additional argument. */
+       if (*scan || (scan > buf && (scan - 1) == wasdelim)) {
                array[argc++] = scan;
+       }
 
        return argc;
 }
 
-enum AST_LOCK_RESULT ast_lock_path(const char *path)
+static enum AST_LOCK_RESULT ast_lock_path_lockfile(const char *path)
 {
        char *s;
        char *fs;
@@ -1035,10 +1107,8 @@ enum AST_LOCK_RESULT ast_lock_path(const char *path)
        int lp = strlen(path);
        time_t start;
 
-       if (!(s = alloca(lp + 10)) || !(fs = alloca(lp + 20))) {
-               ast_log(LOG_WARNING, "Out of memory!\n");
-               return AST_LOCK_FAILURE;
-       }
+       s = alloca(lp + 10); 
+       fs = alloca(lp + 20);
 
        snprintf(fs, strlen(path) + 19, "%s/.lock-%08lx", path, ast_random());
        fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, AST_FILE_MODE);
@@ -1064,15 +1134,12 @@ enum AST_LOCK_RESULT ast_lock_path(const char *path)
        }
 }
 
-int ast_unlock_path(const char *path)
+static int ast_unlock_path_lockfile(const char *path)
 {
        char *s;
        int res;
 
-       if (!(s = alloca(strlen(path) + 10))) {
-               ast_log(LOG_WARNING, "Out of memory!\n");
-               return -1;
-       }
+       s = alloca(strlen(path) + 10);
 
        snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
 
@@ -1085,9 +1152,179 @@ int ast_unlock_path(const char *path)
        return res;
 }
 
+struct path_lock {
+       AST_LIST_ENTRY(path_lock) le;
+       int fd;
+       char *path;
+};
+
+static AST_LIST_HEAD_STATIC(path_lock_list, path_lock);
+
+static void path_lock_destroy(struct path_lock *obj)
+{
+       if (obj->fd >= 0)
+               close(obj->fd);
+       if (obj->path)
+               free(obj->path);
+       free(obj);
+}
+
+static enum AST_LOCK_RESULT ast_lock_path_flock(const char *path)
+{
+       char *fs;
+       int res;
+       int fd;
+       time_t start;
+       struct path_lock *pl;
+       struct stat st, ost;
+
+       fs = alloca(strlen(path) + 20);
+
+       snprintf(fs, strlen(path) + 19, "%s/lock", path);
+       if (lstat(fs, &st) == 0) {
+               if ((st.st_mode & S_IFMT) == S_IFLNK) {
+                       ast_log(LOG_WARNING, "Unable to create lock file "
+                                       "'%s': it's already a symbolic link\n",
+                                       fs);
+                       return AST_LOCK_FAILURE;
+               }
+               if (st.st_nlink > 1) {
+                       ast_log(LOG_WARNING, "Unable to create lock file "
+                                       "'%s': %u hard links exist\n",
+                                       fs, (unsigned int) st.st_nlink);
+                       return AST_LOCK_FAILURE;
+               }
+       }
+       fd = open(fs, O_WRONLY | O_CREAT, 0600);
+       if (fd < 0) {
+               ast_log(LOG_WARNING, "Unable to create lock file '%s': %s\n",
+                               fs, strerror(errno));
+               return AST_LOCK_PATH_NOT_FOUND;
+       }
+       pl = ast_calloc(1, sizeof(*pl));
+       if (!pl) {
+               /* We don't unlink the lock file here, on the possibility that
+                * someone else created it - better to leave a little mess
+                * than create a big one by destroying someone else's lock
+                * and causing something to be corrupted.
+                */
+               close(fd);
+               return AST_LOCK_FAILURE;
+       }
+       pl->fd = fd;
+       pl->path = strdup(path);
+
+       time(&start);
+       while ((
+               #ifdef SOLARIS
+               (res = fcntl(pl->fd, F_SETLK, fcntl(pl->fd, F_GETFL) | O_NONBLOCK)) < 0) &&
+               #else
+               (res = flock(pl->fd, LOCK_EX | LOCK_NB)) < 0) &&
+               #endif
+                       (errno == EWOULDBLOCK) && 
+                       (time(NULL) - start < 5))
+               usleep(1000);
+       if (res) {
+               ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n",
+                               path, strerror(errno));
+               /* No unlinking of lock done, since we tried and failed to
+                * flock() it.
+                */
+               path_lock_destroy(pl);
+               return AST_LOCK_TIMEOUT;
+       }
+
+       /* Check for the race where the file is recreated or deleted out from
+        * underneath us.
+        */
+       if (lstat(fs, &st) != 0 && fstat(pl->fd, &ost) != 0 &&
+                       st.st_dev != ost.st_dev &&
+                       st.st_ino != ost.st_ino) {
+               ast_log(LOG_WARNING, "Unable to create lock file '%s': "
+                               "file changed underneath us\n", fs);
+               path_lock_destroy(pl);
+               return AST_LOCK_FAILURE;
+       }
+
+       /* Success: file created, flocked, and is the one we started with */
+       AST_LIST_LOCK(&path_lock_list);
+       AST_LIST_INSERT_TAIL(&path_lock_list, pl, le);
+       AST_LIST_UNLOCK(&path_lock_list);
+
+       ast_debug(1, "Locked path '%s'\n", path);
+
+       return AST_LOCK_SUCCESS;
+}
+
+static int ast_unlock_path_flock(const char *path)
+{
+       char *s;
+       struct path_lock *p;
+
+       s = alloca(strlen(path) + 20);
+
+       AST_LIST_LOCK(&path_lock_list);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&path_lock_list, p, le) {
+               if (!strcmp(p->path, path)) {
+                       AST_LIST_REMOVE_CURRENT(le);
+                       break;
+               }
+       }
+       AST_LIST_TRAVERSE_SAFE_END;
+       AST_LIST_UNLOCK(&path_lock_list);
+
+       if (p) {
+               snprintf(s, strlen(path) + 19, "%s/lock", path);
+               unlink(s);
+               path_lock_destroy(p);
+               ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
+       } else
+               ast_log(LOG_DEBUG, "Failed to unlock path '%s': "
+                               "lock not found\n", path);
+
+       return 0;
+}
+
+void ast_set_lock_type(enum AST_LOCK_TYPE type)
+{
+       ast_lock_type = type;
+}
+
+enum AST_LOCK_RESULT ast_lock_path(const char *path)
+{
+       enum AST_LOCK_RESULT r = AST_LOCK_FAILURE;
+
+       switch (ast_lock_type) {
+       case AST_LOCK_TYPE_LOCKFILE:
+               r = ast_lock_path_lockfile(path);
+               break;
+       case AST_LOCK_TYPE_FLOCK:
+               r = ast_lock_path_flock(path);
+               break;
+       }
+
+       return r;
+}
+
+int ast_unlock_path(const char *path)
+{
+       int r = 0;
+
+       switch (ast_lock_type) {
+       case AST_LOCK_TYPE_LOCKFILE:
+               r = ast_unlock_path_lockfile(path);
+               break;
+       case AST_LOCK_TYPE_FLOCK:
+               r = ast_unlock_path_flock(path);
+               break;
+       }
+
+       return r;
+}
+
 int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path) 
 {
-       int silencethreshold = 128; 
+       int silencethreshold; 
        int maxsilence = 0;
        int res = 0;
        int cmd = 0;
@@ -1105,6 +1342,8 @@ int ast_record_review(struct ast_channel *chan, const char *playfile, const char
 
        cmd = '3';       /* Want to start by recording */
 
+       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
+
        while ((cmd >= 0) && (cmd != 't')) {
                switch (cmd) {
                case '1':
@@ -1222,12 +1461,12 @@ static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option,
                return res;
        case AST_ACTION_MENU:
                res = ast_ivr_menu_run_internal(chan, (struct ast_ivr_menu *)option->adata, cbdata);
-               /* Do not pass entry errors back up, treaat ast though ti was an "UPONE" */
+               /* Do not pass entry errors back up, treat as though it was an "UPONE" */
                if (res == -2)
                        res = 0;
                return res;
        case AST_ACTION_WAITOPTION:
-               res = ast_waitfordigit(chan, 1000 * (chan->pbx ? chan->pbx->rtimeout : 10));
+               res = ast_waitfordigit(chan, chan->pbx ? chan->pbx->rtimeoutms : 10000);
                if (!res)
                        return 't';
                return res;
@@ -1277,10 +1516,10 @@ static int option_matchmore(struct ast_ivr_menu *menu, char *option)
 
 static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, char *exten, int maxexten)
 {
-       int res=0;
+       int res = 0;
        int ms;
        while (option_matchmore(menu, exten)) {
-               ms = chan->pbx ? chan->pbx->dtimeout : 5000;
+               ms = chan->pbx ? chan->pbx->dtimeoutms : 5000;
                if (strlen(exten) >= maxexten - 1) 
                        break;
                res = ast_waitfordigit(chan, ms);
@@ -1295,7 +1534,7 @@ static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, c
 static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
 {
        /* Execute an IVR menu structure */
-       int res=0;
+       int res = 0;
        int pos = 0;
        int retries = 0;
        char exten[AST_MAX_EXTENSION] = "s";
@@ -1427,7 +1666,6 @@ int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags
        s = optstr;
        while (*s) {
                curarg = *s++ & 0x7f;   /* the array (in app.h) has 128 entries */
-               ast_set_flag(flags, options[curarg].flag);
                argloc = options[curarg].arg_index;
                if (*s == '(') {
                        /* Has argument */
@@ -1442,8 +1680,9 @@ int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags
                                break;
                        }
                } else if (argloc) {
-                       args[argloc - 1] = NULL;
+                       args[argloc - 1] = "";
                }
+               ast_set_flag(flags, options[curarg].flag);
        }
 
        return res;
@@ -1489,3 +1728,143 @@ int ast_app_parse_options64(const struct ast_app_option *options, struct ast_fla
        return res;
 }
 
+void ast_app_options2str64(const struct ast_app_option *options, struct ast_flags64 *flags, char *buf, size_t len)
+{
+       unsigned int i, found = 0;
+       for (i = 32; i < 128 && found < len; i++) {
+               if (ast_test_flag64(flags, options[i].flag)) {
+                       buf[found++] = i;
+               }
+       }
+       buf[found] = '\0';
+}
+
+int ast_get_encoded_char(const char *stream, char *result, size_t *consumed)
+{
+       int i;
+       *consumed = 1;
+       *result = 0;
+       if (*stream == '\\') {
+               *consumed = 2;
+               switch (*(stream + 1)) {
+               case 'n':
+                       *result = '\n';
+                       break;
+               case 'r':
+                       *result = '\r';
+                       break;
+               case 't':
+                       *result = '\t';
+                       break;
+               case 'x':
+                       /* Hexadecimal */
+                       if (strchr("0123456789ABCDEFabcdef", *(stream + 2)) && *(stream + 2) != '\0') {
+                               *consumed = 3;
+                               if (*(stream + 2) <= '9')
+                                       *result = *(stream + 2) - '0';
+                               else if (*(stream + 2) <= 'F')
+                                       *result = *(stream + 2) - 'A' + 10;
+                               else
+                                       *result = *(stream + 2) - 'a' + 10;
+                       } else {
+                               ast_log(LOG_ERROR, "Illegal character '%c' in hexadecimal string\n", *(stream + 2));
+                               return -1;
+                       }
+
+                       if (strchr("0123456789ABCDEFabcdef", *(stream + 3)) && *(stream + 3) != '\0') {
+                               *consumed = 4;
+                               *result <<= 4;
+                               if (*(stream + 3) <= '9')
+                                       *result += *(stream + 3) - '0';
+                               else if (*(stream + 3) <= 'F')
+                                       *result += *(stream + 3) - 'A' + 10;
+                               else
+                                       *result += *(stream + 3) - 'a' + 10;
+                       }
+                       break;
+               case '0':
+                       /* Octal */
+                       *consumed = 2;
+                       for (i = 2; ; i++) {
+                               if (strchr("01234567", *(stream + i)) && *(stream + i) != '\0') {
+                                       (*consumed)++;
+                                       ast_debug(5, "result was %d, ", *result);
+                                       *result <<= 3;
+                                       *result += *(stream + i) - '0';
+                                       ast_debug(5, "is now %d\n", *result);
+                               } else
+                                       break;
+                       }
+                       break;
+               default:
+                       *result = *(stream + 1);
+               }
+       } else {
+               *result = *stream;
+               *consumed = 1;
+       }
+       return 0;
+}
+
+void ast_close_fds_above_n(int n)
+{
+       int x, null;
+       null = open("/dev/null", O_RDONLY);
+       for (x = n + 1; x <= (null >= 8192 ? null : 8192); x++) {
+               if (x != null) {
+                       /* Side effect of dup2 is that it closes any existing fd without error.
+                        * This prevents valgrind and other debugging tools from sending up
+                        * false error reports. */
+                       dup2(null, x);
+                       close(x);
+               }
+       }
+       close(null);
+}
+
+int ast_safe_fork(int stop_reaper)
+{
+       sigset_t signal_set, old_set;
+       int pid;
+
+       /* Don't let the default signal handler for children reap our status */
+       if (stop_reaper) {
+               ast_replace_sigchld();
+       }
+
+       sigfillset(&signal_set);
+       pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
+
+       pid = fork();
+
+       if (pid != 0) {
+               /* Fork failed or parent */
+               pthread_sigmask(SIG_SETMASK, &old_set, NULL);
+               return pid;
+       } else {
+               /* Child */
+
+               /* Before we unblock our signals, return our trapped signals back to the defaults */
+               signal(SIGHUP, SIG_DFL);
+               signal(SIGCHLD, SIG_DFL);
+               signal(SIGINT, SIG_DFL);
+               signal(SIGURG, SIG_DFL);
+               signal(SIGTERM, SIG_DFL);
+               signal(SIGPIPE, SIG_DFL);
+               signal(SIGXFSZ, SIG_DFL);
+
+               /* unblock important signal handlers */
+               if (pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL)) {
+                       ast_log(LOG_WARNING, "unable to unblock signals: %s\n", strerror(errno));
+                       _exit(1);
+               }
+
+               return pid;
+       }
+}
+
+void ast_safe_fork_cleanup(void)
+{
+       ast_unreplace_sigchld();
+}
+