simplify autoconfig include mechanism (make tholo happy he can use lint again :-)
[asterisk/asterisk.git] / app.c
diff --git a/app.c b/app.c
index f6eb18e..13b402e 100644 (file)
--- a/app.c
+++ b/app.c
  * \author Mark Spencer <markster@digium.com> 
  */
 
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <regex.h>
 
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
 #include "asterisk/file.h"
@@ -55,7 +55,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 /* !
 This function presents a dialtone and reads an extension into 'collect' 
-which must be a pointer to a **pre-initilized** array of char having a 
+which must be a pointer to a **pre-initialized** array of char having a 
 size of 'size' suitable for writing to.  It will collect no more than the smaller 
 of 'maxlen' or 'size' minus the original strlen() of collect digits.
 \return 0 if extension does not exist, 1 if extension exists
@@ -79,7 +79,7 @@ int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect,
        else 
                ast_log(LOG_NOTICE,"Huh....? no dial for indications?\n");
        
-       for (x = strlen(collect); strlen(collect) < maxlen; ) {
+       for (x = strlen(collect); x < maxlen; ) {
                res = ast_waitfordigit(chan, timeout);
                if (!ast_ignore_pattern(context, collect))
                        ast_playtones_stop(chan);
@@ -94,12 +94,8 @@ int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect,
                        break;
                }
        }
-       if (res >= 0) {
-               if (ast_exists_extension(chan, context, collect, 1, chan->cid.cid_num))
-                       res = 1;
-               else
-                       res = 0;
-       }
+       if (res >= 0)
+               res = ast_exists_extension(chan, context, collect, 1, chan->cid.cid_num) ? 1 : 0;
        return res;
 }
 
@@ -148,102 +144,23 @@ int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxle
        return res;
 }
 
-int ast_app_getvoice(struct ast_channel *c, char *dest, char *dstfmt, char *prompt, int silence, int maxsec)
-{
-       int res;
-       struct ast_filestream *writer;
-       int rfmt;
-       int totalms=0, total;
-       
-       struct ast_frame *f;
-       struct ast_dsp *sildet;
-       /* Play prompt if requested */
-       if (prompt) {
-               res = ast_streamfile(c, prompt, c->language);
-               if (res < 0)
-                       return res;
-               res = ast_waitstream(c,"");
-               if (res < 0)
-                       return res;
-       }
-       rfmt = c->readformat;
-       res = ast_set_read_format(c, AST_FORMAT_SLINEAR);
-       if (res < 0) {
-               ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
-               return -1;
-       }
-       sildet = ast_dsp_new();
-       if (!sildet) {
-               ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
-               return -1;
-       }
-       writer = ast_writefile(dest, dstfmt, "Voice file", 0, 0, 0666);
-       if (!writer) {
-               ast_log(LOG_WARNING, "Unable to open file '%s' in format '%s' for writing\n", dest, dstfmt);
-               ast_dsp_free(sildet);
-               return -1;
-       }
-       for(;;) {
-               if ((res = ast_waitfor(c, 2000)) < 0) {
-                       ast_log(LOG_NOTICE, "Waitfor failed while recording file '%s' format '%s'\n", dest, dstfmt);
-                       break;
-               }
-               if (res) {
-                       f = ast_read(c);
-                       if (!f) {
-                               ast_log(LOG_NOTICE, "Hungup while recording file '%s' format '%s'\n", dest, dstfmt);
-                               break;
-                       }
-                       if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
-                               /* Ended happily with DTMF */
-                               ast_frfree(f);
-                               break;
-                       } else if (f->frametype == AST_FRAME_VOICE) {
-                               ast_dsp_silence(sildet, f, &total); 
-                               if (total > silence) {
-                                       /* Ended happily with silence */
-                                       ast_frfree(f);
-                                       break;
-                               }
-                               totalms += f->samples / 8;
-                               if (totalms > maxsec * 1000) {
-                                       /* Ended happily with too much stuff */
-                                       ast_log(LOG_NOTICE, "Constraining voice on '%s' to %d seconds\n", c->name, maxsec);
-                                       ast_frfree(f);
-                                       break;
-                               }
-                               res = ast_writestream(writer, f);
-                               if (res < 0) {
-                                       ast_log(LOG_WARNING, "Failed to write to stream at %s!\n", dest);
-                                       ast_frfree(f);
-                                       break;
-                               }
-                                       
-                       }
-                       ast_frfree(f);
-               }
-       }
-       res = ast_set_read_format(c, rfmt);
-       if (res)
-               ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", c->name);
-       ast_dsp_free(sildet);
-       ast_closestream(writer);
-       return 0;
-}
-
 static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
-static int (*ast_messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
+static int (*ast_inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = 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 (*messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs))
+                             int (*inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs),
+                             int (*messagecount_func)(const char *context, const char *mailbox, const char *folder))
 {
        ast_has_voicemail_func = has_voicemail_func;
+       ast_inboxcount_func = inboxcount_func;
        ast_messagecount_func = messagecount_func;
 }
 
 void ast_uninstall_vm_functions(void)
 {
        ast_has_voicemail_func = NULL;
+       ast_inboxcount_func = NULL;
        ast_messagecount_func = NULL;
 }
 
@@ -261,15 +178,15 @@ int ast_app_has_voicemail(const char *mailbox, const char *folder)
 }
 
 
-int ast_app_messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
+int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
 {
        static int warned = 0;
        if (newmsgs)
                *newmsgs = 0;
        if (oldmsgs)
                *oldmsgs = 0;
-       if (ast_messagecount_func)
-               return ast_messagecount_func(mailbox, newmsgs, oldmsgs);
+       if (ast_inboxcount_func)
+               return ast_inboxcount_func(mailbox, newmsgs, oldmsgs);
 
        if (!warned && (option_verbose > 2)) {
                warned++;
@@ -279,47 +196,71 @@ int ast_app_messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
        return 0;
 }
 
-int ast_dtmf_stream(struct ast_channel *chan,struct ast_channel *peer,char *digits,int between) 
+int ast_app_messagecount(const char *context, const char *mailbox, const char *folder)
 {
-       char *ptr;
+       static int warned = 0;
+       if (ast_messagecount_func)
+               return ast_messagecount_func(context, mailbox, folder);
+
+       if (!warned && (option_verbose > 2)) {
+               warned++;
+               ast_verbose(VERBOSE_PREFIX_3 "Message count requested for mailbox %s@%s/%s but voicemail not loaded.\n", mailbox, context, folder);
+       }
+
+       return 0;
+}
+
+int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between) 
+{
+       const char *ptr;
        int res = 0;
-       struct ast_frame f;
+       struct ast_frame f = {
+               .frametype = AST_FRAME_DTMF,
+               .src = "ast_dtmf_stream"
+       };
+
        if (!between)
                between = 100;
 
        if (peer)
                res = ast_autoservice_start(peer);
 
-       if (!res) {
-               res = ast_waitfor(chan,100);
-               if (res > -1) {
-                       for (ptr=digits; *ptr; ptr++) {
-                               if (*ptr == 'w') {
-                                       res = ast_safe_sleep(chan, 500);
-                                       if (res) 
-                                               break;
-                                       continue;
-                               }
-                               memset(&f, 0, sizeof(f));
-                               f.frametype = AST_FRAME_DTMF;
+       if (!res)
+               res = ast_waitfor(chan, 100);
+
+       /* ast_waitfor will return the number of remaining ms on success */
+       if (res < 0)
+               return res;
+
+       for (ptr = digits; *ptr; ptr++) {
+               if (*ptr == 'w') {
+                       /* 'w' -- wait half a second */
+                       if ((res = ast_safe_sleep(chan, 500)))
+                               break;
+               } else if (strchr("0123456789*#abcdfABCDF", *ptr)) {
+                       /* Character represents valid DTMF */
+                       if (*ptr == 'f' || *ptr == 'F') {
+                               /* ignore return values if not supported by channel */
+                               ast_indicate(chan, AST_CONTROL_FLASH);
+                       } else {
                                f.subclass = *ptr;
-                               f.src = "ast_dtmf_stream";
-                               if (strchr("0123456789*#abcdABCD",*ptr)==NULL) {
-                                       ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
-                               } else {
-                                       res = ast_write(chan, &f);
-                                       if (res) 
-                                               break;
-                                       /* pause between digits */
-                                       res = ast_safe_sleep(chan,between);
-                                       if (res) 
-                                               break;
-                               }
+                               if ((res = ast_write(chan, &f)))
+                                       break;
                        }
-               }
-               if (peer)
-                       res = ast_autoservice_stop(peer);
+                       /* pause between digits */
+                       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);
        }
+
+       if (peer) {
+               /* Stop autoservice on the peer channel, but don't overwrite any error condition 
+                  that has occurred previously while acting on the primary channel */
+               if (ast_autoservice_stop(peer) && !res)
+                       res = -1;
+       }
+
        return res;
 }
 
@@ -408,16 +349,14 @@ int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, in
                if (filename[0] == '/') 
                        ast_copy_string(tmpf, filename, sizeof(tmpf));
                else
-                       snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", (char *)ast_config_AST_VAR_DIR, "sounds", filename);
+                       snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", (char *)ast_config_AST_DATA_DIR, "sounds", filename);
                fd = open(tmpf, O_RDONLY);
                if (fd < 0){
                        ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
                        return -1;
                }
        }
-       lin = malloc(sizeof(struct linear_state));
-       if (lin) {
-               memset(lin, 0, sizeof(lin));
+       if ((lin = ast_calloc(1, sizeof(*lin)))) {
                lin->fd = fd;
                lin->allowoverride = allowoverride;
                lin->autoclose = autoclose;
@@ -534,23 +473,37 @@ int ast_play_and_wait(struct ast_channel *chan, const char *fn)
 static int global_silence_threshold = 128;
 static int global_maxsilence = 0;
 
-int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path)
+/*! Optionally play a sound file or a beep, then record audio and video from the channel.
+ * @param chan Channel to playback to/record from.
+ * @param playfile Filename of sound to play before recording begins.
+ * @param recordfile Filename to record to.
+ * @param maxtime Maximum length of recording (in milliseconds).
+ * @param fmt Format(s) to record message in. Multiple formats may be specified by separating them with a '|'.
+ * @param duration Where to store actual length of the recorded message (in milliseconds).
+ * @param beep Whether to play a beep before starting to record.
+ * @param silencethreshold 
+ * @param maxsilence Length of silence that will end a recording (in milliseconds).
+ * @param path Optional filesystem path to unlock.
+ * @param prepend If true, prepend the recorded audio to an existing file.
+ * @param acceptdtmf DTMF digits that will end the recording.
+ * @param canceldtmf DTMF digits that will cancel the recording.
+ */
+
+static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int beep, int silencethreshold, int maxsilence, const char *path, int prepend, const char *acceptdtmf, const char *canceldtmf)
 {
-       int d;
+       int d = 0;
        char *fmts;
        char comment[256];
-       int x, fmtcnt=1, res=-1,outmsg=0;
-       struct ast_frame *f;
+       int x, fmtcnt = 1, res = -1, outmsg = 0;
        struct ast_filestream *others[MAX_OTHER_FORMATS];
        char *sfmt[MAX_OTHER_FORMATS];
-       char *stringp=NULL;
+       char *stringp = NULL;
        time_t start, end;
-       struct ast_dsp *sildet=NULL;    /* silence detector dsp */
+       struct ast_dsp *sildet = NULL;   /* silence detector dsp */
        int totalsilence = 0;
-       int dspsilence = 0;
-       int gotsilence = 0;             /* did we timeout for silence? */
-       int rfmt=0;
+       int rfmt = 0;
        struct ast_silence_generator *silgen = NULL;
+       char prependfile[80];
 
        if (silencethreshold < 0)
                silencethreshold = global_silence_threshold;
@@ -565,38 +518,42 @@ int ast_play_and_record(struct ast_channel *chan, const char *playfile, const ch
        }
 
        ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
-       snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
+       snprintf(comment, sizeof(comment), "Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
 
-       if (playfile) {
-               d = ast_play_and_wait(chan, playfile);
+       if (playfile || beep) {
+               if (!beep)
+                       d = ast_play_and_wait(chan, playfile);
                if (d > -1)
-                       d = ast_streamfile(chan, "beep",chan->language);
-               if (!d)
-                       d = ast_waitstream(chan,"");
+                       d = ast_stream_and_wait(chan, "beep", chan->language, "");
                if (d < 0)
                        return -1;
        }
 
+       if (prepend) {
+               ast_copy_string(prependfile, recordfile, sizeof(prependfile));  
+               strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
+       }
+
        fmts = ast_strdupa(fmt);
 
-       stringp=fmts;
+       stringp = fmts;
        strsep(&stringp, "|");
        ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
        sfmt[0] = ast_strdupa(fmts);
 
-       while((fmt = strsep(&stringp, "|"))) {
+       while ((fmt = strsep(&stringp, "|"))) {
                if (fmtcnt > MAX_OTHER_FORMATS - 1) {
-                       ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
+                       ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app.c\n");
                        break;
                }
                sfmt[fmtcnt++] = ast_strdupa(fmt);
        }
 
-       time(&start);
-       end=start;  /* pre-initialize end to be same as start in case we never get into loop */
-       for (x=0;x<fmtcnt;x++) {
-               others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
-               ast_verbose( VERBOSE_PREFIX_3 "x=%d, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
+       end = start = time(NULL);  /* pre-initialize end to be same as start in case we never get into loop */
+       for (x = 0; x < fmtcnt; x++) {
+               others[x] = ast_writefile(prepend ? prependfile : recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "x=%d, open writing:  %s format: %s, %p\n", x, prepend ? prependfile : recordfile, sfmt[x], others[x]);
 
                if (!others[x]) {
                        break;
@@ -622,17 +579,19 @@ int ast_play_and_record(struct ast_channel *chan, const char *playfile, const ch
                }
        }
 
-       /* Request a video update */
-       ast_indicate(chan, AST_CONTROL_VIDUPDATE);
+       if (!prepend) {
+               /* Request a video update */
+               ast_indicate(chan, AST_CONTROL_VIDUPDATE);
 
-       if (ast_opt_transmit_silence)
-               silgen = ast_channel_start_silence_generator(chan);
+               if (ast_opt_transmit_silence)
+                       silgen = ast_channel_start_silence_generator(chan);
+       }
 
        if (x == fmtcnt) {
-       /* Loop forever, writing the packets we read to the writer(s), until
-          we read a # or get a hangup */
-               f = NULL;
-               for(;;) {
+               /* Loop forever, writing the packets we read to the writer(s), until
+                  we read a digit or get a hangup */
+               struct ast_frame *f;
+               for (;;) {
                        res = ast_waitfor(chan, 2000);
                        if (!res) {
                                ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
@@ -653,13 +612,15 @@ int ast_play_and_record(struct ast_channel *chan, const char *playfile, const ch
                                break;
                        if (f->frametype == AST_FRAME_VOICE) {
                                /* write each format */
-                               for (x=0;x<fmtcnt;x++) {
+                               for (x = 0; x < fmtcnt; x++) {
+                                       if (prepend && !others[x])
+                                               break;
                                        res = ast_writestream(others[x], f);
                                }
 
                                /* Silence Detection */
                                if (maxsilence > 0) {
-                                       dspsilence = 0;
+                                       int dspsilence = 0;
                                        ast_dsp_silence(sildet, f, &dspsilence);
                                        if (dspsilence)
                                                totalsilence = dspsilence;
@@ -670,329 +631,139 @@ int ast_play_and_record(struct ast_channel *chan, const char *playfile, const ch
                                                /* Ended happily with silence */
                                                if (option_verbose > 2)
                                                        ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
-                                               ast_frfree(f);
-                                               gotsilence = 1;
-                                               outmsg=2;
+                                               res = 'S';
+                                               outmsg = 2;
                                                break;
                                        }
                                }
                                /* Exit on any error */
                                if (res) {
                                        ast_log(LOG_WARNING, "Error writing frame\n");
-                                       ast_frfree(f);
                                        break;
                                }
                        } else if (f->frametype == AST_FRAME_VIDEO) {
                                /* Write only once */
                                ast_writestream(others[0], f);
                        } else if (f->frametype == AST_FRAME_DTMF) {
-                               if (f->subclass == '#') {
+                               if (prepend) {
+                               /* stop recording with any digit */
+                                       if (option_verbose > 2) 
+                                               ast_verbose(VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
+                                       res = 't';
+                                       outmsg = 2;
+                                       break;
+                               }
+                               if (strchr(acceptdtmf, f->subclass)) {
                                        if (option_verbose > 2)
-                                               ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
-                                       res = '#';
+                                               ast_verbose(VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
+                                       res = f->subclass;
                                        outmsg = 2;
-                                       ast_frfree(f);
                                        break;
                                }
-                               if (f->subclass == '0') {
-                               /* Check for a '0' during message recording also, in case caller wants operator */
+                               if (strchr(canceldtmf, f->subclass)) {
                                        if (option_verbose > 2)
-                                               ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
-                                       res = '0';
+                                               ast_verbose(VERBOSE_PREFIX_3 "User cancelled message by pressing %c\n", f->subclass);
+                                       res = f->subclass;
                                        outmsg = 0;
-                                       ast_frfree(f);
                                        break;
                                }
                        }
                        if (maxtime) {
-                               time(&end);
+                               end = time(NULL);
                                if (maxtime < (end - start)) {
                                        if (option_verbose > 2)
-                                               ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
-                                       outmsg = 2;
+                                               ast_verbose(VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
                                        res = 't';
-                                       ast_frfree(f);
+                                       outmsg = 2;
                                        break;
                                }
                        }
                        ast_frfree(f);
                }
-               if (end == start) time(&end);
                if (!f) {
                        if (option_verbose > 2)
-                               ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
+                               ast_verbose(VERBOSE_PREFIX_3 "User hung up\n");
                        res = -1;
-                       outmsg=1;
+                       outmsg = 1;
+               } 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]);
        }
 
-       if (silgen)
-               ast_channel_stop_silence_generator(chan, silgen);
-
-       *duration = end - start;
-
-       for (x=0;x<fmtcnt;x++) {
-               if (!others[x])
-                       break;
-               if (res > 0) {
-                       if (totalsilence)
-                               ast_stream_rewind(others[x], totalsilence-200);
-                       else
-                               ast_stream_rewind(others[x], 200);
-               }
-               ast_truncstream(others[x]);
-               ast_closestream(others[x]);
-       }
-       if (rfmt) {
-               if (ast_set_read_format(chan, rfmt)) {
-                       ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
-               }
-       }
-       if (outmsg > 1) {
-               /* Let them know recording is stopped */
-               if(!ast_streamfile(chan, "auth-thankyou", chan->language))
-                       ast_waitstream(chan, "");
-       }
-       if (sildet)
-               ast_dsp_free(sildet);
-       return res;
-}
-
-int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep, int silencethreshold, int maxsilence)
-{
-       int d = 0;
-       char *fmts;
-       char comment[256];
-       int x, fmtcnt=1, res=-1,outmsg=0;
-       struct ast_frame *f;
-       struct ast_filestream *others[MAX_OTHER_FORMATS];
-       struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
-       char *sfmt[MAX_OTHER_FORMATS];
-       char *stringp=NULL;
-       time_t start, end;
-       struct ast_dsp *sildet;         /* silence detector dsp */
-       int totalsilence = 0;
-       int dspsilence = 0;
-       int gotsilence = 0;             /* did we timeout for silence? */
-       int rfmt=0;     
-       char prependfile[80];
-       
-       if (silencethreshold < 0)
-               silencethreshold = global_silence_threshold;
-
-       if (maxsilence < 0)
-               maxsilence = global_maxsilence;
-
-       /* barf if no pointer passed to store duration in */
-       if (duration == NULL) {
-               ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
-               return -1;
-       }
-
-       ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
-       snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
-
-       if (playfile || beep) { 
-               if (!beep)
-                       d = ast_play_and_wait(chan, playfile);
-               if (d > -1)
-                       d = ast_streamfile(chan, "beep",chan->language);
-               if (!d)
-                       d = ast_waitstream(chan,"");
-               if (d < 0)
-                       return -1;
-       }
-       ast_copy_string(prependfile, recordfile, sizeof(prependfile));  
-       strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
-                       
-       fmts = ast_strdupa(fmt);
-       
-       stringp=fmts;
-       strsep(&stringp, "|");
-       ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
-       sfmt[0] = ast_strdupa(fmts);
-       
-       while((fmt = strsep(&stringp, "|"))) {
-               if (fmtcnt > MAX_OTHER_FORMATS - 1) {
-                       ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
-                       break;
-               }
-               sfmt[fmtcnt++] = ast_strdupa(fmt);
-       }
-
-       time(&start);
-       end=start;  /* pre-initialize end to be same as start in case we never get into loop */
-       for (x=0;x<fmtcnt;x++) {
-               others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
-               ast_verbose( VERBOSE_PREFIX_3 "x=%d, open writing:  %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
-               if (!others[x]) {
-                       break;
-               }
+       if (!prepend) {
+               if (silgen)
+                       ast_channel_stop_silence_generator(chan, silgen);
        }
-       
-       sildet = ast_dsp_new(); /* Create the silence detector */
-       if (!sildet) {
-               ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
-               return -1;
-       }
-       ast_dsp_set_threshold(sildet, silencethreshold);
+       *duration = end - start;
 
-       if (maxsilence > 0) {
-               rfmt = chan->readformat;
-               res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
-               if (res < 0) {
-                       ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
-                       return -1;
-               }
-       }
-                                               
-       if (x == fmtcnt) {
-       /* Loop forever, writing the packets we read to the writer(s), until
-          we read a # or get a hangup */
-               f = NULL;
-               for(;;) {
-                       res = ast_waitfor(chan, 2000);
-                       if (!res) {
-                               ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
-                               /* Try one more time in case of masq */
-                               res = ast_waitfor(chan, 2000);
-                               if (!res) {
-                                       ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
-                                       res = -1;
-                               }
-                       }
-                       
-                       if (res < 0) {
-                               f = NULL;
-                               break;
-                       }
-                       f = ast_read(chan);
-                       if (!f)
+       if (!prepend) {
+               for (x = 0; x < fmtcnt; x++) {
+                       if (!others[x])
                                break;
-                       if (f->frametype == AST_FRAME_VOICE) {
-                               /* write each format */
-                               for (x=0;x<fmtcnt;x++) {
-                                       if (!others[x])
-                                               break;
-                                       res = ast_writestream(others[x], f);
-                               }
-                               
-                               /* Silence Detection */
-                               if (maxsilence > 0) {
-                                       dspsilence = 0;
-                                       ast_dsp_silence(sildet, f, &dspsilence);
-                                       if (dspsilence)
-                                               totalsilence = dspsilence;
-                                       else
-                                               totalsilence = 0;
-                                       
-                                       if (totalsilence > maxsilence) {
-                                       /* Ended happily with silence */
-                                       if (option_verbose > 2) 
-                                               ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
-                                       ast_frfree(f);
-                                       gotsilence = 1;
-                                       outmsg=2;
-                                       break;
-                                       }
-                               }
-                               /* Exit on any error */
-                               if (res) {
-                                       ast_log(LOG_WARNING, "Error writing frame\n");
-                                       ast_frfree(f);
-                                       break;
-                               }
-                       } else if (f->frametype == AST_FRAME_VIDEO) {
-                               /* Write only once */
-                               ast_writestream(others[0], f);
-                       } else if (f->frametype == AST_FRAME_DTMF) {
-                               /* stop recording with any digit */
-                               if (option_verbose > 2) 
-                                       ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
-                               res = 't';
-                               outmsg = 2;
-                               ast_frfree(f);
-                               break;
-                       }
-                       if (maxtime) {
-                               time(&end);
-                               if (maxtime < (end - start)) {
-                                       if (option_verbose > 2)
-                                               ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
-                                       res = 't';
-                                       outmsg=2;
-                                       ast_frfree(f);
-                                       break;
-                               }
-                       }
-                       ast_frfree(f);
-               }
-               if (end == start) time(&end);
-               if (!f) {
-                       if (option_verbose > 2) 
-                               ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
-                       res = -1;
-                       outmsg=1;
-#if 0
-                       /* delete all the prepend files */
-                       for (x=0;x<fmtcnt;x++) {
-                               if (!others[x])
-                                       break;
-                               ast_closestream(others[x]);
-                               ast_filedelete(prependfile, sfmt[x]);
-                       }
-#endif
+                       if (res > 0)
+                               ast_stream_rewind(others[x], totalsilence ? totalsilence-200 : 200);
+                       ast_truncstream(others[x]);
+                       ast_closestream(others[x]);
                }
-       } else {
-               ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); 
        }
-       *duration = end - start;
-#if 0
-       if (outmsg > 1) {
-#else
-       if (outmsg) {
-#endif
+
+       if (prepend && outmsg) {
+               struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
                struct ast_frame *fr;
-               for (x=0;x<fmtcnt;x++) {
+
+               for (x = 0; x < fmtcnt; x++) {
                        snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
                        realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
                        if (!others[x] || !realfiles[x])
                                break;
-                       if (totalsilence)
-                               ast_stream_rewind(others[x], totalsilence-200);
-                       else
-                               ast_stream_rewind(others[x], 200);
+                       ast_stream_rewind(others[x], totalsilence ? totalsilence-200 : 200);
                        ast_truncstream(others[x]);
                        /* add the original file too */
                        while ((fr = ast_readframe(realfiles[x]))) {
-                               ast_writestream(others[x],fr);
+                               ast_writestream(others[x], fr);
+                               ast_frfree(fr);
                        }
                        ast_closestream(others[x]);
                        ast_closestream(realfiles[x]);
                        ast_filerename(prependfile, recordfile, sfmt[x]);
-#if 0
-                       ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
-#endif
+                       if (option_verbose > 3)
+                               ast_verbose(VERBOSE_PREFIX_4 "Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x], prependfile, recordfile);
                        ast_filedelete(prependfile, sfmt[x]);
                }
        }
-       if (rfmt) {
-               if (ast_set_read_format(chan, rfmt)) {
-                       ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
-               }
+       if (rfmt && ast_set_read_format(chan, rfmt)) {
+               ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
        }
-       if (outmsg) {
-               if (outmsg > 1) {
-                       /* Let them know it worked */
-                       ast_streamfile(chan, "auth-thankyou", chan->language);
-                       ast_waitstream(chan, "");
-               }
-       }       
+       if (outmsg == 2) {
+               ast_stream_and_wait(chan, "auth-thankyou", chan->language, "");
+       }
+       if (sildet)
+               ast_dsp_free(sildet);
        return res;
 }
 
+static char default_acceptdtmf[] = "#";
+static char default_canceldtmf[] = "";
+
+int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf)
+{
+       return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, 0, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf));
+}
+
+int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path)
+{
+       return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, 0, silencethreshold, maxsilence, path, 0, default_acceptdtmf, default_canceldtmf);
+}
+
+int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep, int silencethreshold, int maxsilence)
+{
+       return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, beep, silencethreshold, maxsilence, NULL, 1, default_acceptdtmf, default_canceldtmf);
+}
+
 /* Channel group core functions */
 
 int ast_app_group_split_group(const char *data, char *group, int group_max, char *category, int category_max)
@@ -1049,7 +820,7 @@ int ast_app_group_get_count(const char *group, const char *category)
        if (ast_strlen_zero(group))
                return 0;
 
-       s = (!ast_strlen_zero(category)) ? category : GROUP_CATEGORY_PREFIX;
+       s = S_OR(category, GROUP_CATEGORY_PREFIX);
        ast_copy_string(cat, s, sizeof(cat));
 
        chan = NULL;
@@ -1057,7 +828,7 @@ int ast_app_group_get_count(const char *group, const char *category)
                test = pbx_builtin_getvar_helper(chan, cat);
                if (test && !strcasecmp(test, group))
                        count++;
-               ast_mutex_unlock(&chan->lock);
+               ast_channel_unlock(chan);
        }
 
        return count;
@@ -1079,7 +850,7 @@ int ast_app_group_match_get_count(const char *groupmatch, const char *category)
        if (regcomp(&regexbuf, groupmatch, REG_EXTENDED | REG_NOSUB))
                return 0;
 
-       s = (!ast_strlen_zero(category)) ? category : GROUP_CATEGORY_PREFIX;
+       s = S_OR(category, GROUP_CATEGORY_PREFIX);
        ast_copy_string(cat, s, sizeof(cat));
 
        chan = NULL;
@@ -1087,7 +858,7 @@ int ast_app_group_match_get_count(const char *groupmatch, const char *category)
                test = pbx_builtin_getvar_helper(chan, cat);
                if (test && !regexec(&regexbuf, test, 0, NULL, 0))
                        count++;
-               ast_mutex_unlock(&chan->lock);
+               ast_channel_unlock(chan);
        }
 
        regfree(&regexbuf);
@@ -1099,7 +870,7 @@ unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arra
 {
        int argc;
        char *scan;
-       int paren = 0;
+       int paren = 0, quote = 0;
 
        if (!buf || !array || !arraylen)
                return 0;
@@ -1116,7 +887,15 @@ unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arra
                        else if (*scan == ')') {
                                if (paren)
                                        paren--;
-                       } else if ((*scan == delim) && !paren) {
+                       } else if (*scan == '"') {
+                               quote = quote ? 0 : 1;
+                               /* Remove quote character from argument */
+                               memmove(scan, scan + 1, strlen(scan));
+                               scan--;
+                       } else if (*scan == '\\') {
+                               /* Literal character, don't parse */
+                               memmove(scan, scan + 1, strlen(scan));
+                       } else if ((*scan == delim) && !paren && !quote) {
                                *scan++ = '\0';
                                break;
                        }
@@ -1135,17 +914,15 @@ enum AST_LOCK_RESULT ast_lock_path(const char *path)
        char *fs;
        int res;
        int fd;
+       int lp = strlen(path);
        time_t start;
 
-       s = alloca(strlen(path) + 10);
-       fs = alloca(strlen(path) + 20);
-
-       if (!fs || !s) {
+       if (!(s = alloca(lp + 10)) || !(fs = alloca(lp + 20))) {
                ast_log(LOG_WARNING, "Out of memory!\n");
                return AST_LOCK_FAILURE;
        }
 
-       snprintf(fs, strlen(path) + 19, "%s/.lock-%08x", path, rand());
+       snprintf(fs, strlen(path) + 19, "%s/.lock-%08lx", path, ast_random());
        fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, 0600);
        if (fd < 0) {
                fprintf(stderr, "Unable to create lock file '%s': %s\n", path, strerror(errno));
@@ -1154,7 +931,7 @@ enum AST_LOCK_RESULT ast_lock_path(const char *path)
        close(fd);
 
        snprintf(s, strlen(path) + 9, "%s/.lock", path);
-       time(&start);
+       start = time(NULL);
        while (((res = link(fs, s)) < 0) && (errno == EEXIST) && (time(NULL) - start < 5))
                usleep(1);
        if (res) {
@@ -1170,8 +947,7 @@ enum AST_LOCK_RESULT ast_lock_path(const char *path)
 int ast_unlock_path(const char *path)
 {
        char *s;
-       s = alloca(strlen(path) + 10);
-       if (!s)
+       if (!(s = alloca(strlen(path) + 10)))
                return -1;
        snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
        ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
@@ -1206,16 +982,14 @@ int ast_record_review(struct ast_channel *chan, const char *playfile, const char
                                cmd = '3';
                                break;
                        } else {
-                               ast_streamfile(chan, "vm-msgsaved", chan->language);
-                               ast_waitstream(chan, "");
+                               ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
                                cmd = 't';
                                return res;
                        }
                case '2':
                        /* Review */
                        ast_verbose(VERBOSE_PREFIX_3 "Reviewing the recording\n");
-                       ast_streamfile(chan, recordfile, chan->language);
-                       cmd = ast_waitstream(chan, AST_DIGIT_ANY);
+                       cmd = ast_stream_and_wait(chan, recordfile, chan->language, AST_DIGIT_ANY);
                        break;
                case '3':
                        message_exists = 0;
@@ -1301,19 +1075,15 @@ static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option,
        case AST_ACTION_NOOP:
                return 0;
        case AST_ACTION_BACKGROUND:
-               res = ast_streamfile(chan, (char *)option->adata, chan->language);
-               if (!res) {
-                       res = ast_waitstream(chan, AST_DIGIT_ANY);
-               } else {
+               res = ast_stream_and_wait(chan, (char *)option->adata, chan->language, AST_DIGIT_ANY);
+               if (res < 0) {
                        ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
                        res = 0;
                }
                return res;
        case AST_ACTION_PLAYBACK:
-               res = ast_streamfile(chan, (char *)option->adata, chan->language);
-               if (!res) {
-                       res = ast_waitstream(chan, "");
-               } else {
+               res = ast_stream_and_wait(chan, (char *)option->adata, chan->language, "");
+               if (res < 0) {
                        ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
                        res = 0;
                }
@@ -1340,12 +1110,12 @@ static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option,
        case AST_ACTION_BACKLIST:
                res = 0;
                c = ast_strdupa(option->adata);
-               if (c) {
-                       while((n = strsep(&c, ";")))
-                               if ((res = ast_streamfile(chan, n, chan->language)) || (res = ast_waitstream(chan, (option->action == AST_ACTION_BACKLIST) ? AST_DIGIT_ANY : "")))
-                                       break;
-                       ast_stopstream(chan);
+               while ((n = strsep(&c, ";"))) {
+                       if ((res = ast_stream_and_wait(chan, n, chan->language,
+                                       (option->action == AST_ACTION_BACKLIST) ? AST_DIGIT_ANY : "")))
+                               break;
                }
+               ast_stopstream(chan);
                return res;
        default:
                ast_log(LOG_NOTICE, "Unknown dispatch function %d, ignoring!\n", option->action);
@@ -1497,8 +1267,7 @@ char *ast_read_textfile(const char *filename)
                ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", filename, strerror(errno));
                return NULL;
        }
-       output=(char *)malloc(count);
-       if (output) {
+       if ((output = ast_malloc(count))) {
                res = read(fd, output, count - 1);
                if (res == count - 1) {
                        output[res] = '\0';
@@ -1507,8 +1276,7 @@ char *ast_read_textfile(const char *filename)
                        free(output);
                        output = NULL;
                }
-       } else 
-               ast_log(LOG_WARNING, "Out of memory!\n");
+       }
        close(fd);
        return output;
 }
@@ -1528,14 +1296,13 @@ int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags
 
        s = optstr;
        while (*s) {
-               curarg = *s++ & 0x7f;
+               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 */
                        arg = ++s;
-                       while (*s && (*s != ')'))
-                               s++;
+                       s = strchr(s, ')');
                        if (*s) {
                                if (argloc)
                                        args[argloc - 1] = arg;