add missing break (issue #5154)
[asterisk/asterisk.git] / app.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Convenient Application Routines
5  * 
6  * Copyright (C) 1999 - 2005, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/time.h>
18 #include <signal.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <dirent.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <regex.h>
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "asterisk/channel.h"
31 #include "asterisk/pbx.h"
32 #include "asterisk/file.h"
33 #include "asterisk/app.h"
34 #include "asterisk/dsp.h"
35 #include "asterisk/logger.h"
36 #include "asterisk/options.h"
37 #include "asterisk/utils.h"
38 #include "asterisk/lock.h"
39 #include "asterisk/indications.h"
40
41 #define MAX_OTHER_FORMATS 10
42
43
44 /* 
45 This function presents a dialtone and reads an extension into 'collect' 
46 which must be a pointer to a **pre-initilized** array of char having a 
47 size of 'size' suitable for writing to.  It will collect no more than the smaller 
48 of 'maxlen' or 'size' minus the original strlen() of collect digits.
49 */
50 int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout) 
51 {
52         struct tone_zone_sound *ts;
53         int res=0, x=0;
54
55         if(maxlen > size)
56                 maxlen = size;
57         
58         if(!timeout && chan->pbx)
59                 timeout = chan->pbx->dtimeout;
60         else if(!timeout)
61                 timeout = 5;
62         
63         ts = ast_get_indication_tone(chan->zone,"dial");
64         if (ts && ts->data[0])
65                 res = ast_playtones_start(chan, 0, ts->data, 0);
66         else 
67                 ast_log(LOG_NOTICE,"Huh....? no dial for indications?\n");
68         
69         for (x = strlen(collect); strlen(collect) < maxlen; ) {
70                 res = ast_waitfordigit(chan, timeout);
71                 if (!ast_ignore_pattern(context, collect))
72                         ast_playtones_stop(chan);
73                 if (res < 1)
74                         break;
75                 collect[x++] = res;
76                 if (!ast_matchmore_extension(chan, context, collect, 1, chan->cid.cid_num)) {
77                         if (collect[x-1] == '#') {
78                                 /* Not a valid extension, ending in #, assume the # was to finish dialing */
79                                 collect[x-1] = '\0';
80                         }
81                         break;
82                 }
83         }
84         if (res >= 0) {
85                 if (ast_exists_extension(chan, context, collect, 1, chan->cid.cid_num))
86                         res = 1;
87                 else
88                         res = 0;
89         }
90         return res;
91 }
92
93
94
95 /* set timeout to 0 for "standard" timeouts. Set timeout to -1 for 
96    "ludicrous time" (essentially never times out) */
97 int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout)
98 {
99         int res,to,fto;
100         /* XXX Merge with full version? XXX */
101         if (maxlen)
102                 s[0] = '\0';
103         if (prompt) {
104                 res = ast_streamfile(c, prompt, c->language);
105                 if (res < 0)
106                         return res;
107         }
108         fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
109         to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
110
111         if (timeout > 0) 
112                 fto = to = timeout;
113         if (timeout < 0) 
114                 fto = to = 1000000000;
115         res = ast_readstring(c, s, maxlen, to, fto, "#");
116         return res;
117 }
118
119
120 int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
121 {
122         int res,to,fto;
123         if (prompt) {
124                 res = ast_streamfile(c, prompt, c->language);
125                 if (res < 0)
126                         return res;
127         }
128         fto = 6000;
129         to = 2000;
130         if (timeout > 0) 
131                 fto = to = timeout;
132         if (timeout < 0) 
133                 fto = to = 1000000000;
134         res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd);
135         return res;
136 }
137
138 int ast_app_getvoice(struct ast_channel *c, char *dest, char *dstfmt, char *prompt, int silence, int maxsec)
139 {
140         int res;
141         struct ast_filestream *writer;
142         int rfmt;
143         int totalms=0, total;
144         
145         struct ast_frame *f;
146         struct ast_dsp *sildet;
147         /* Play prompt if requested */
148         if (prompt) {
149                 res = ast_streamfile(c, prompt, c->language);
150                 if (res < 0)
151                         return res;
152                 res = ast_waitstream(c,"");
153                 if (res < 0)
154                         return res;
155         }
156         rfmt = c->readformat;
157         res = ast_set_read_format(c, AST_FORMAT_SLINEAR);
158         if (res < 0) {
159                 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
160                 return -1;
161         }
162         sildet = ast_dsp_new();
163         if (!sildet) {
164                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
165                 return -1;
166         }
167         writer = ast_writefile(dest, dstfmt, "Voice file", 0, 0, 0666);
168         if (!writer) {
169                 ast_log(LOG_WARNING, "Unable to open file '%s' in format '%s' for writing\n", dest, dstfmt);
170                 ast_dsp_free(sildet);
171                 return -1;
172         }
173         for(;;) {
174                 if ((res = ast_waitfor(c, 2000)) < 0) {
175                         ast_log(LOG_NOTICE, "Waitfor failed while recording file '%s' format '%s'\n", dest, dstfmt);
176                         break;
177                 }
178                 if (res) {
179                         f = ast_read(c);
180                         if (!f) {
181                                 ast_log(LOG_NOTICE, "Hungup while recording file '%s' format '%s'\n", dest, dstfmt);
182                                 break;
183                         }
184                         if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
185                                 /* Ended happily with DTMF */
186                                 ast_frfree(f);
187                                 break;
188                         } else if (f->frametype == AST_FRAME_VOICE) {
189                                 ast_dsp_silence(sildet, f, &total); 
190                                 if (total > silence) {
191                                         /* Ended happily with silence */
192                                         ast_frfree(f);
193                                         break;
194                                 }
195                                 totalms += f->samples / 8;
196                                 if (totalms > maxsec * 1000) {
197                                         /* Ended happily with too much stuff */
198                                         ast_log(LOG_NOTICE, "Constraining voice on '%s' to %d seconds\n", c->name, maxsec);
199                                         ast_frfree(f);
200                                         break;
201                                 }
202                         }
203                         ast_frfree(f);
204                 }
205         }
206         res = ast_set_read_format(c, rfmt);
207         if (res)
208                 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", c->name);
209         ast_dsp_free(sildet);
210         ast_closestream(writer);
211         return 0;
212 }
213
214 static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
215 static int (*ast_messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
216
217 void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder),
218                               int (*messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs))
219 {
220         ast_has_voicemail_func = has_voicemail_func;
221         ast_messagecount_func = messagecount_func;
222 }
223
224 void ast_uninstall_vm_functions(void)
225 {
226         ast_has_voicemail_func = NULL;
227         ast_messagecount_func = NULL;
228 }
229
230 int ast_app_has_voicemail(const char *mailbox, const char *folder)
231 {
232         static int warned = 0;
233         if (ast_has_voicemail_func)
234                 return ast_has_voicemail_func(mailbox, folder);
235
236         if ((option_verbose > 2) && !warned) {
237                 ast_verbose(VERBOSE_PREFIX_3 "Message check requested for mailbox %s/folder %s but voicemail not loaded.\n", mailbox, folder ? folder : "INBOX");
238                 warned++;
239         }
240         return 0;
241 }
242
243
244 int ast_app_messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
245 {
246         static int warned = 0;
247         if (newmsgs)
248                 *newmsgs = 0;
249         if (oldmsgs)
250                 *oldmsgs = 0;
251         if (ast_messagecount_func)
252                 return ast_messagecount_func(mailbox, newmsgs, oldmsgs);
253
254         if (!warned && (option_verbose > 2)) {
255                 warned++;
256                 ast_verbose(VERBOSE_PREFIX_3 "Message count requested for mailbox %s but voicemail not loaded.\n", mailbox);
257         }
258
259         return 0;
260 }
261
262 int ast_dtmf_stream(struct ast_channel *chan,struct ast_channel *peer,char *digits,int between) 
263 {
264         char *ptr;
265         int res = 0;
266         struct ast_frame f;
267         if (!between)
268                 between = 100;
269
270         if (peer)
271                 res = ast_autoservice_start(peer);
272
273         if (!res) {
274                 res = ast_waitfor(chan,100);
275                 if (res > -1) {
276                         for (ptr=digits; *ptr; ptr++) {
277                                 if (*ptr == 'w') {
278                                         res = ast_safe_sleep(chan, 500);
279                                         if (res) 
280                                                 break;
281                                         continue;
282                                 }
283                                 memset(&f, 0, sizeof(f));
284                                 f.frametype = AST_FRAME_DTMF;
285                                 f.subclass = *ptr;
286                                 f.src = "ast_dtmf_stream";
287                                 if (strchr("0123456789*#abcdABCD",*ptr)==NULL) {
288                                         ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
289                                 } else {
290                                         res = ast_write(chan, &f);
291                                         if (res) 
292                                                 break;
293                                         /* pause between digits */
294                                         res = ast_safe_sleep(chan,between);
295                                         if (res) 
296                                                 break;
297                                 }
298                         }
299                 }
300                 if (peer)
301                         res = ast_autoservice_stop(peer);
302         }
303         return res;
304 }
305
306 struct linear_state {
307         int fd;
308         int autoclose;
309         int allowoverride;
310         int origwfmt;
311 };
312
313 static void linear_release(struct ast_channel *chan, void *params)
314 {
315         struct linear_state *ls = params;
316         if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt)) {
317                 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, ls->origwfmt);
318         }
319         if (ls->autoclose)
320                 close(ls->fd);
321         free(params);
322 }
323
324 static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
325 {
326         struct ast_frame f;
327         short buf[2048 + AST_FRIENDLY_OFFSET / 2];
328         struct linear_state *ls = data;
329         int res;
330         len = samples * 2;
331         if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
332                 ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" ,len);
333                 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
334         }
335         memset(&f, 0, sizeof(f));
336         res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
337         if (res > 0) {
338                 f.frametype = AST_FRAME_VOICE;
339                 f.subclass = AST_FORMAT_SLINEAR;
340                 f.data = buf + AST_FRIENDLY_OFFSET/2;
341                 f.datalen = res;
342                 f.samples = res / 2;
343                 f.offset = AST_FRIENDLY_OFFSET;
344                 ast_write(chan, &f);
345                 if (res == len)
346                         return 0;
347         }
348         return -1;
349 }
350
351 static void *linear_alloc(struct ast_channel *chan, void *params)
352 {
353         struct linear_state *ls;
354         /* In this case, params is already malloc'd */
355         if (params) {
356                 ls = params;
357                 if (ls->allowoverride)
358                         ast_set_flag(chan, AST_FLAG_WRITE_INT);
359                 else
360                         ast_clear_flag(chan, AST_FLAG_WRITE_INT);
361                 ls->origwfmt = chan->writeformat;
362                 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
363                         ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
364                         free(ls);
365                         ls = params = NULL;
366                 }
367         }
368         return params;
369 }
370
371 static struct ast_generator linearstream = 
372 {
373         alloc: linear_alloc,
374         release: linear_release,
375         generate: linear_generator,
376 };
377
378 int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride)
379 {
380         struct linear_state *lin;
381         char tmpf[256];
382         int res = -1;
383         int autoclose = 0;
384         if (fd < 0) {
385                 if (!filename || ast_strlen_zero(filename))
386                         return -1;
387                 autoclose = 1;
388                 if (filename[0] == '/') 
389                         ast_copy_string(tmpf, filename, sizeof(tmpf));
390                 else
391                         snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", (char *)ast_config_AST_VAR_DIR, "sounds", filename);
392                 fd = open(tmpf, O_RDONLY);
393                 if (fd < 0){
394                         ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
395                         return -1;
396                 }
397         }
398         lin = malloc(sizeof(struct linear_state));
399         if (lin) {
400                 memset(lin, 0, sizeof(lin));
401                 lin->fd = fd;
402                 lin->allowoverride = allowoverride;
403                 lin->autoclose = autoclose;
404                 res = ast_activate_generator(chan, &linearstream, lin);
405         }
406         return res;
407 }
408
409 int ast_control_streamfile(struct ast_channel *chan, const char *file,
410                            const char *fwd, const char *rev,
411                            const char *stop, const char *pause,
412                            const char *restart, int skipms) 
413 {
414         long elapsed = 0, last_elapsed = 0;
415         char *breaks = NULL;
416         char *end = NULL;
417         int blen = 2;
418         int res;
419
420         if (stop)
421                 blen += strlen(stop);
422         if (pause)
423                 blen += strlen(pause);
424         if (restart)
425                 blen += strlen(restart);
426
427         if (blen > 2) {
428                 breaks = alloca(blen + 1);
429                 breaks[0] = '\0';
430                 if (stop)
431                         strcat(breaks, stop);
432                 if (pause)
433                         strcat(breaks, pause);
434                 if (restart)
435                         strcat(breaks, restart);
436         }
437         if (chan->_state != AST_STATE_UP)
438                 res = ast_answer(chan);
439
440         if (chan)
441                 ast_stopstream(chan);
442
443         if (file) {
444                 if ((end = strchr(file,':'))) {
445                         if (!strcasecmp(end, ":end")) {
446                                 *end = '\0';
447                                 end++;
448                         }
449                 }
450         }
451
452         for (;;) {
453                 struct timeval started = ast_tvnow();
454
455                 if (chan)
456                         ast_stopstream(chan);
457                 res = ast_streamfile(chan, file, chan->language);
458                 if (!res) {
459                         if (end) {
460                                 ast_seekstream(chan->stream, 0, SEEK_END);
461                                 end=NULL;
462                         }
463                         res = 1;
464                         if (elapsed) {
465                                 ast_stream_fastforward(chan->stream, elapsed);
466                                 last_elapsed = elapsed - 200;
467                         }
468                         if (res)
469                                 res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
470                         else
471                                 break;
472                 }
473
474                 if (res < 1)
475                         break;
476
477                 /* We go at next loop if we got the restart char */
478                 if (restart && strchr(restart, res)) {
479                         ast_log(LOG_DEBUG, "we'll restart the stream here at next loop\n");
480                         elapsed=0; /* To make sure the next stream will start at beginning */
481                         continue;
482                 }
483
484                 if (pause != NULL && strchr(pause, res)) {
485                         elapsed = ast_tvdiff_ms(ast_tvnow(), started) + last_elapsed;
486                         for(;;) {
487                                 if (chan)
488                                         ast_stopstream(chan);
489                                 res = ast_waitfordigit(chan, 1000);
490                                 if (res == 0)
491                                         continue;
492                                 else if (res == -1 || strchr(pause, res) || (stop && strchr(stop, res)))
493                                         break;
494                         }
495                         if (res == *pause) {
496                                 res = 0;
497                                 continue;
498                         }
499                 }
500                 if (res == -1)
501                         break;
502
503                 /* if we get one of our stop chars, return it to the calling function */
504                 if (stop && strchr(stop, res)) {
505                         /* res = 0; */
506                         break;
507                 }
508         }
509         if (chan)
510                 ast_stopstream(chan);
511
512         return res;
513 }
514
515 int ast_play_and_wait(struct ast_channel *chan, const char *fn)
516 {
517         int d;
518         d = ast_streamfile(chan, fn, chan->language);
519         if (d)
520                 return d;
521         d = ast_waitstream(chan, AST_DIGIT_ANY);
522         ast_stopstream(chan);
523         return d;
524 }
525
526 static int global_silence_threshold = 128;
527 static int global_maxsilence = 0;
528
529 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)
530 {
531         int d;
532         char *fmts;
533         char comment[256];
534         int x, fmtcnt=1, res=-1,outmsg=0;
535         struct ast_frame *f;
536         struct ast_filestream *others[MAX_OTHER_FORMATS];
537         char *sfmt[MAX_OTHER_FORMATS];
538         char *stringp=NULL;
539         time_t start, end;
540         struct ast_dsp *sildet=NULL;    /* silence detector dsp */
541         int totalsilence = 0;
542         int dspsilence = 0;
543         int gotsilence = 0;             /* did we timeout for silence? */
544         int rfmt=0;
545
546         if (silencethreshold < 0)
547                 silencethreshold = global_silence_threshold;
548
549         if (maxsilence < 0)
550                 maxsilence = global_maxsilence;
551
552         /* barf if no pointer passed to store duration in */
553         if (duration == NULL) {
554                 ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
555                 return -1;
556         }
557
558         ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
559         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
560
561         if (playfile) {
562                 d = ast_play_and_wait(chan, playfile);
563                 if (d > -1)
564                         d = ast_streamfile(chan, "beep",chan->language);
565                 if (!d)
566                         d = ast_waitstream(chan,"");
567                 if (d < 0)
568                         return -1;
569         }
570
571         fmts = ast_strdupa(fmt);
572
573         stringp=fmts;
574         strsep(&stringp, "|");
575         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
576         sfmt[0] = ast_strdupa(fmts);
577
578         while((fmt = strsep(&stringp, "|"))) {
579                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
580                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
581                         break;
582                 }
583                 sfmt[fmtcnt++] = ast_strdupa(fmt);
584         }
585
586         time(&start);
587         end=start;  /* pre-initialize end to be same as start in case we never get into loop */
588         for (x=0;x<fmtcnt;x++) {
589                 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
590                 ast_verbose( VERBOSE_PREFIX_3 "x=%d, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
591
592                 if (!others[x]) {
593                         break;
594                 }
595         }
596
597         if (path)
598                 ast_unlock_path(path);
599
600
601         
602         if (maxsilence > 0) {
603                 sildet = ast_dsp_new(); /* Create the silence detector */
604                 if (!sildet) {
605                         ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
606                         return -1;
607                 }
608                 ast_dsp_set_threshold(sildet, silencethreshold);
609                 rfmt = chan->readformat;
610                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
611                 if (res < 0) {
612                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
613                         ast_dsp_free(sildet);
614                         return -1;
615                 }
616         }
617         /* Request a video update */
618         ast_indicate(chan, AST_CONTROL_VIDUPDATE);
619
620         if (x == fmtcnt) {
621         /* Loop forever, writing the packets we read to the writer(s), until
622            we read a # or get a hangup */
623                 f = NULL;
624                 for(;;) {
625                         res = ast_waitfor(chan, 2000);
626                         if (!res) {
627                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
628                                 /* Try one more time in case of masq */
629                                 res = ast_waitfor(chan, 2000);
630                                 if (!res) {
631                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
632                                         res = -1;
633                                 }
634                         }
635
636                         if (res < 0) {
637                                 f = NULL;
638                                 break;
639                         }
640                         f = ast_read(chan);
641                         if (!f)
642                                 break;
643                         if (f->frametype == AST_FRAME_VOICE) {
644                                 /* write each format */
645                                 for (x=0;x<fmtcnt;x++) {
646                                         res = ast_writestream(others[x], f);
647                                 }
648
649                                 /* Silence Detection */
650                                 if (maxsilence > 0) {
651                                         dspsilence = 0;
652                                         ast_dsp_silence(sildet, f, &dspsilence);
653                                         if (dspsilence)
654                                                 totalsilence = dspsilence;
655                                         else
656                                                 totalsilence = 0;
657
658                                         if (totalsilence > maxsilence) {
659                                                 /* Ended happily with silence */
660                                                 if (option_verbose > 2)
661                                                         ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
662                                                 ast_frfree(f);
663                                                 gotsilence = 1;
664                                                 outmsg=2;
665                                                 break;
666                                         }
667                                 }
668                                 /* Exit on any error */
669                                 if (res) {
670                                         ast_log(LOG_WARNING, "Error writing frame\n");
671                                         ast_frfree(f);
672                                         break;
673                                 }
674                         } else if (f->frametype == AST_FRAME_VIDEO) {
675                                 /* Write only once */
676                                 ast_writestream(others[0], f);
677                         } else if (f->frametype == AST_FRAME_DTMF) {
678                                 if (f->subclass == '#') {
679                                         if (option_verbose > 2)
680                                                 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
681                                         res = '#';
682                                         outmsg = 2;
683                                         ast_frfree(f);
684                                         break;
685                                 }
686                         }
687                                 if (f->subclass == '0') {
688                                 /* Check for a '0' during message recording also, in case caller wants operator */
689                                         if (option_verbose > 2)
690                                                 ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
691                                         res = '0';
692                                         outmsg = 0;
693                                         ast_frfree(f);
694                                         break;
695                                 }
696                         if (maxtime) {
697                                 time(&end);
698                                 if (maxtime < (end - start)) {
699                                         if (option_verbose > 2)
700                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
701                                         outmsg = 2;
702                                         res = 't';
703                                         ast_frfree(f);
704                                         break;
705                                 }
706                         }
707                         ast_frfree(f);
708                 }
709                 if (end == start) time(&end);
710                 if (!f) {
711                         if (option_verbose > 2)
712                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
713                         res = -1;
714                         outmsg=1;
715                 }
716         } else {
717                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
718         }
719
720         *duration = end - start;
721
722         for (x=0;x<fmtcnt;x++) {
723                 if (!others[x])
724                         break;
725                 if (res > 0) {
726                         if (totalsilence)
727                                 ast_stream_rewind(others[x], totalsilence-200);
728                         else
729                                 ast_stream_rewind(others[x], 200);
730                 }
731                 ast_truncstream(others[x]);
732                 ast_closestream(others[x]);
733         }
734         if (rfmt) {
735                 if (ast_set_read_format(chan, rfmt)) {
736                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
737                 }
738         }
739         if (outmsg > 1) {
740                 /* Let them know recording is stopped */
741                 if(!ast_streamfile(chan, "auth-thankyou", chan->language))
742                         ast_waitstream(chan, "");
743         }
744         if (sildet)
745                 ast_dsp_free(sildet);
746         return res;
747 }
748
749 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)
750 {
751         int d = 0;
752         char *fmts;
753         char comment[256];
754         int x, fmtcnt=1, res=-1,outmsg=0;
755         struct ast_frame *f;
756         struct ast_filestream *others[MAX_OTHER_FORMATS];
757         struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
758         char *sfmt[MAX_OTHER_FORMATS];
759         char *stringp=NULL;
760         time_t start, end;
761         struct ast_dsp *sildet;         /* silence detector dsp */
762         int totalsilence = 0;
763         int dspsilence = 0;
764         int gotsilence = 0;             /* did we timeout for silence? */
765         int rfmt=0;     
766         char prependfile[80];
767         
768         if (silencethreshold < 0)
769                 silencethreshold = global_silence_threshold;
770
771         if (maxsilence < 0)
772                 maxsilence = global_maxsilence;
773
774         /* barf if no pointer passed to store duration in */
775         if (duration == NULL) {
776                 ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
777                 return -1;
778         }
779
780         ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
781         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
782
783         if (playfile || beep) { 
784                 if (!beep)
785                         d = ast_play_and_wait(chan, playfile);
786                 if (d > -1)
787                         d = ast_streamfile(chan, "beep",chan->language);
788                 if (!d)
789                         d = ast_waitstream(chan,"");
790                 if (d < 0)
791                         return -1;
792         }
793         ast_copy_string(prependfile, recordfile, sizeof(prependfile));  
794         strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
795                         
796         fmts = ast_strdupa(fmt);
797         
798         stringp=fmts;
799         strsep(&stringp, "|");
800         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
801         sfmt[0] = ast_strdupa(fmts);
802         
803         while((fmt = strsep(&stringp, "|"))) {
804                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
805                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
806                         break;
807                 }
808                 sfmt[fmtcnt++] = ast_strdupa(fmt);
809         }
810
811         time(&start);
812         end=start;  /* pre-initialize end to be same as start in case we never get into loop */
813         for (x=0;x<fmtcnt;x++) {
814                 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
815                 ast_verbose( VERBOSE_PREFIX_3 "x=%d, open writing:  %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
816                 if (!others[x]) {
817                         break;
818                 }
819         }
820         
821         sildet = ast_dsp_new(); /* Create the silence detector */
822         if (!sildet) {
823                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
824                 return -1;
825         }
826         ast_dsp_set_threshold(sildet, silencethreshold);
827
828         if (maxsilence > 0) {
829                 rfmt = chan->readformat;
830                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
831                 if (res < 0) {
832                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
833                         return -1;
834                 }
835         }
836                                                 
837         if (x == fmtcnt) {
838         /* Loop forever, writing the packets we read to the writer(s), until
839            we read a # or get a hangup */
840                 f = NULL;
841                 for(;;) {
842                         res = ast_waitfor(chan, 2000);
843                         if (!res) {
844                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
845                                 /* Try one more time in case of masq */
846                                 res = ast_waitfor(chan, 2000);
847                                 if (!res) {
848                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
849                                         res = -1;
850                                 }
851                         }
852                         
853                         if (res < 0) {
854                                 f = NULL;
855                                 break;
856                         }
857                         f = ast_read(chan);
858                         if (!f)
859                                 break;
860                         if (f->frametype == AST_FRAME_VOICE) {
861                                 /* write each format */
862                                 for (x=0;x<fmtcnt;x++) {
863                                         if (!others[x])
864                                                 break;
865                                         res = ast_writestream(others[x], f);
866                                 }
867                                 
868                                 /* Silence Detection */
869                                 if (maxsilence > 0) {
870                                         dspsilence = 0;
871                                         ast_dsp_silence(sildet, f, &dspsilence);
872                                         if (dspsilence)
873                                                 totalsilence = dspsilence;
874                                         else
875                                                 totalsilence = 0;
876                                         
877                                         if (totalsilence > maxsilence) {
878                                         /* Ended happily with silence */
879                                         if (option_verbose > 2) 
880                                                 ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
881                                         ast_frfree(f);
882                                         gotsilence = 1;
883                                         outmsg=2;
884                                         break;
885                                         }
886                                 }
887                                 /* Exit on any error */
888                                 if (res) {
889                                         ast_log(LOG_WARNING, "Error writing frame\n");
890                                         ast_frfree(f);
891                                         break;
892                                 }
893                         } else if (f->frametype == AST_FRAME_VIDEO) {
894                                 /* Write only once */
895                                 ast_writestream(others[0], f);
896                         } else if (f->frametype == AST_FRAME_DTMF) {
897                                 /* stop recording with any digit */
898                                 if (option_verbose > 2) 
899                                         ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
900                                 res = 't';
901                                 outmsg = 2;
902                                 ast_frfree(f);
903                                 break;
904                         }
905                         if (maxtime) {
906                                 time(&end);
907                                 if (maxtime < (end - start)) {
908                                         if (option_verbose > 2)
909                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
910                                         res = 't';
911                                         outmsg=2;
912                                         ast_frfree(f);
913                                         break;
914                                 }
915                         }
916                         ast_frfree(f);
917                 }
918                 if (end == start) time(&end);
919                 if (!f) {
920                         if (option_verbose > 2) 
921                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
922                         res = -1;
923                         outmsg=1;
924 #if 0
925                         /* delete all the prepend files */
926                         for (x=0;x<fmtcnt;x++) {
927                                 if (!others[x])
928                                         break;
929                                 ast_closestream(others[x]);
930                                 ast_filedelete(prependfile, sfmt[x]);
931                         }
932 #endif
933                 }
934         } else {
935                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); 
936         }
937         *duration = end - start;
938 #if 0
939         if (outmsg > 1) {
940 #else
941         if (outmsg) {
942 #endif
943                 struct ast_frame *fr;
944                 for (x=0;x<fmtcnt;x++) {
945                         snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
946                         realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
947                         if (!others[x] || !realfiles[x])
948                                 break;
949                         if (totalsilence)
950                                 ast_stream_rewind(others[x], totalsilence-200);
951                         else
952                                 ast_stream_rewind(others[x], 200);
953                         ast_truncstream(others[x]);
954                         /* add the original file too */
955                         while ((fr = ast_readframe(realfiles[x]))) {
956                                 ast_writestream(others[x],fr);
957                         }
958                         ast_closestream(others[x]);
959                         ast_closestream(realfiles[x]);
960                         ast_filerename(prependfile, recordfile, sfmt[x]);
961 #if 0
962                         ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
963 #endif
964                         ast_filedelete(prependfile, sfmt[x]);
965                 }
966         }
967         if (rfmt) {
968                 if (ast_set_read_format(chan, rfmt)) {
969                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
970                 }
971         }
972         if (outmsg) {
973                 if (outmsg > 1) {
974                         /* Let them know it worked */
975                         ast_streamfile(chan, "auth-thankyou", chan->language);
976                         ast_waitstream(chan, "");
977                 }
978         }       
979         return res;
980 }
981
982 /* Channel group core functions */
983
984 int ast_app_group_split_group(char *data, char *group, int group_max, char *category, int category_max)
985 {
986         int res=0;
987         char tmp[256];
988         char *grp=NULL, *cat=NULL;
989
990         if (data && !ast_strlen_zero(data)) {
991                 ast_copy_string(tmp, data, sizeof(tmp));
992                 grp = tmp;
993                 cat = strchr(tmp, '@');
994                 if (cat) {
995                         *cat = '\0';
996                         cat++;
997                 }
998         }
999
1000         if (grp && !ast_strlen_zero(grp))
1001                 ast_copy_string(group, grp, group_max);
1002         else
1003                 res = -1;
1004
1005         if (cat)
1006                 snprintf(category, category_max, "%s_%s", GROUP_CATEGORY_PREFIX, cat);
1007         else
1008                 ast_copy_string(category, GROUP_CATEGORY_PREFIX, category_max);
1009
1010         return res;
1011 }
1012
1013 int ast_app_group_set_channel(struct ast_channel *chan, char *data)
1014 {
1015         int res=0;
1016         char group[80] = "";
1017         char category[80] = "";
1018
1019         if (!ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category))) {
1020                 pbx_builtin_setvar_helper(chan, category, group);
1021         } else
1022                 res = -1;
1023
1024         return res;
1025 }
1026
1027 int ast_app_group_get_count(char *group, char *category)
1028 {
1029         struct ast_channel *chan;
1030         int count = 0;
1031         char *test;
1032         char cat[80];
1033         char *s;
1034
1035         if (group == NULL || ast_strlen_zero(group))
1036                 return 0;
1037
1038         s = (category && !ast_strlen_zero(category)) ? category : GROUP_CATEGORY_PREFIX;
1039         ast_copy_string(cat, s, sizeof(cat));
1040
1041         chan = NULL;
1042         while ((chan = ast_channel_walk_locked(chan)) != NULL) {
1043                 test = pbx_builtin_getvar_helper(chan, cat);
1044                 if (test && !strcasecmp(test, group))
1045                         count++;
1046                 ast_mutex_unlock(&chan->lock);
1047         }
1048
1049         return count;
1050 }
1051
1052 int ast_app_group_match_get_count(char *groupmatch, char *category)
1053 {
1054         regex_t regexbuf;
1055         struct ast_channel *chan;
1056         int count = 0;
1057         char *test;
1058         char cat[80];
1059         char *s;
1060
1061         if (!groupmatch || ast_strlen_zero(groupmatch))
1062                 return 0;
1063
1064         /* if regex compilation fails, return zero matches */
1065         if (regcomp(&regexbuf, groupmatch, REG_EXTENDED | REG_NOSUB))
1066                 return 0;
1067
1068         s = (category && !ast_strlen_zero(category)) ? category : GROUP_CATEGORY_PREFIX;
1069         ast_copy_string(cat, s, sizeof(cat));
1070
1071         chan = NULL;
1072         while ((chan = ast_channel_walk_locked(chan)) != NULL) {
1073                 test = pbx_builtin_getvar_helper(chan, cat);
1074                 if (test && !regexec(&regexbuf, test, 0, NULL, 0))
1075                         count++;
1076                 ast_mutex_unlock(&chan->lock);
1077         }
1078
1079         regfree(&regexbuf);
1080
1081         return count;
1082 }
1083
1084 int ast_separate_app_args(char *buf, char delim, char **array, int arraylen)
1085 {
1086         int argc;
1087         char *scan;
1088         int paren = 0;
1089
1090         if (!buf || !array || !arraylen)
1091                 return 0;
1092
1093         memset(array, 0, arraylen * sizeof(*array));
1094
1095         scan = buf;
1096
1097         for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
1098                 array[argc] = scan;
1099                 for (; *scan; scan++) {
1100                         if (*scan == '(')
1101                                 paren++;
1102                         else if (*scan == ')') {
1103                                 if (paren)
1104                                         paren--;
1105                         } else if ((*scan == delim) && !paren) {
1106                                 *scan++ = '\0';
1107                                 break;
1108                         }
1109                 }
1110         }
1111
1112         if (*scan)
1113                 array[argc++] = scan;
1114
1115         return argc;
1116 }
1117
1118 enum AST_LOCK_RESULT ast_lock_path(const char *path)
1119 {
1120         char *s;
1121         char *fs;
1122         int res;
1123         int fd;
1124         time_t start;
1125
1126         s = alloca(strlen(path) + 10);
1127         fs = alloca(strlen(path) + 20);
1128
1129         if (!fs || !s) {
1130                 ast_log(LOG_WARNING, "Out of memory!\n");
1131                 return AST_LOCK_FAILURE;
1132         }
1133
1134         snprintf(fs, strlen(path) + 19, "%s/.lock-%08x", path, rand());
1135         fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, 0600);
1136         if (fd < 0) {
1137                 fprintf(stderr, "Unable to create lock file '%s': %s\n", path, strerror(errno));
1138                 return AST_LOCK_PATH_NOT_FOUND;
1139         }
1140         close(fd);
1141
1142         snprintf(s, strlen(path) + 9, "%s/.lock", path);
1143         time(&start);
1144         while (((res = link(fs, s)) < 0) && (errno == EEXIST) && (time(NULL) - start < 5))
1145                 usleep(1);
1146         if (res) {
1147                 ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n", path, strerror(errno));
1148                 return AST_LOCK_TIMEOUT;
1149         } else {
1150                 unlink(fs);
1151                 ast_log(LOG_DEBUG, "Locked path '%s'\n", path);
1152                 return AST_LOCK_SUCCESS;
1153         }
1154 }
1155
1156 int ast_unlock_path(const char *path)
1157 {
1158         char *s;
1159         s = alloca(strlen(path) + 10);
1160         if (!s)
1161                 return -1;
1162         snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
1163         ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
1164         return unlink(s);
1165 }
1166
1167 int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path) 
1168 {
1169         int silencethreshold = 128; 
1170         int maxsilence=0;
1171         int res = 0;
1172         int cmd = 0;
1173         int max_attempts = 3;
1174         int attempts = 0;
1175         int recorded = 0;
1176         int message_exists = 0;
1177         /* Note that urgent and private are for flagging messages as such in the future */
1178
1179         /* barf if no pointer passed to store duration in */
1180         if (duration == NULL) {
1181                 ast_log(LOG_WARNING, "Error ast_record_review called without duration pointer\n");
1182                 return -1;
1183         }
1184
1185         cmd = '3';       /* Want to start by recording */
1186
1187         while ((cmd >= 0) && (cmd != 't')) {
1188                 switch (cmd) {
1189                 case '1':
1190                         if (!message_exists) {
1191                                 /* In this case, 1 is to record a message */
1192                                 cmd = '3';
1193                                 break;
1194                         } else {
1195                                 ast_streamfile(chan, "vm-msgsaved", chan->language);
1196                                 ast_waitstream(chan, "");
1197                                 cmd = 't';
1198                                 return res;
1199                         }
1200                 case '2':
1201                         /* Review */
1202                         ast_verbose(VERBOSE_PREFIX_3 "Reviewing the recording\n");
1203                         ast_streamfile(chan, recordfile, chan->language);
1204                         cmd = ast_waitstream(chan, AST_DIGIT_ANY);
1205                         break;
1206                 case '3':
1207                         message_exists = 0;
1208                         /* Record */
1209                         if (recorded == 1)
1210                                 ast_verbose(VERBOSE_PREFIX_3 "Re-recording\n");
1211                         else    
1212                                 ast_verbose(VERBOSE_PREFIX_3 "Recording\n");
1213                         recorded = 1;
1214                         cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, path);
1215                         if (cmd == -1) {
1216                         /* User has hung up, no options to give */
1217                                 return cmd;
1218                         }
1219                         if (cmd == '0') {
1220                                 break;
1221                         } else if (cmd == '*') {
1222                                 break;
1223                         } 
1224                         else {
1225                                 /* If all is well, a message exists */
1226                                 message_exists = 1;
1227                                 cmd = 0;
1228                         }
1229                         break;
1230                 case '4':
1231                 case '5':
1232                 case '6':
1233                 case '7':
1234                 case '8':
1235                 case '9':
1236                 case '*':
1237                 case '#':
1238                         cmd = ast_play_and_wait(chan, "vm-sorry");
1239                         break;
1240                 default:
1241                         if (message_exists) {
1242                                 cmd = ast_play_and_wait(chan, "vm-review");
1243                         }
1244                         else {
1245                                 cmd = ast_play_and_wait(chan, "vm-torerecord");
1246                                 if (!cmd)
1247                                         cmd = ast_waitfordigit(chan, 600);
1248                         }
1249                         
1250                         if (!cmd)
1251                                 cmd = ast_waitfordigit(chan, 6000);
1252                         if (!cmd) {
1253                                 attempts++;
1254                         }
1255                         if (attempts > max_attempts) {
1256                                 cmd = 't';
1257                         }
1258                 }
1259         }
1260         if (cmd == 't')
1261                 cmd = 0;
1262         return cmd;
1263 }
1264
1265 #define RES_UPONE (1 << 16)
1266 #define RES_EXIT  (1 << 17)
1267 #define RES_REPEAT (1 << 18)
1268 #define RES_RESTART ((1 << 19) | RES_REPEAT)
1269
1270 static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata);
1271 static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option, char *exten, void *cbdata)
1272 {
1273         int res;
1274         int (*ivr_func)(struct ast_channel *, void *);
1275         char *c;
1276         char *n;
1277         
1278         switch(option->action) {
1279         case AST_ACTION_UPONE:
1280                 return RES_UPONE;
1281         case AST_ACTION_EXIT:
1282                 return RES_EXIT | (((unsigned long)(option->adata)) & 0xffff);
1283         case AST_ACTION_REPEAT:
1284                 return RES_REPEAT | (((unsigned long)(option->adata)) & 0xffff);
1285         case AST_ACTION_RESTART:
1286                 return RES_RESTART ;
1287         case AST_ACTION_NOOP:
1288                 return 0;
1289         case AST_ACTION_BACKGROUND:
1290                 res = ast_streamfile(chan, (char *)option->adata, chan->language);
1291                 if (!res) {
1292                         res = ast_waitstream(chan, AST_DIGIT_ANY);
1293                 } else {
1294                         ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
1295                         res = 0;
1296                 }
1297                 return res;
1298         case AST_ACTION_PLAYBACK:
1299                 res = ast_streamfile(chan, (char *)option->adata, chan->language);
1300                 if (!res) {
1301                         res = ast_waitstream(chan, "");
1302                 } else {
1303                         ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
1304                         res = 0;
1305                 }
1306                 return res;
1307         case AST_ACTION_MENU:
1308                 res = ast_ivr_menu_run_internal(chan, (struct ast_ivr_menu *)option->adata, cbdata);
1309                 /* Do not pass entry errors back up, treaat ast though ti was an "UPONE" */
1310                 if (res == -2)
1311                         res = 0;
1312                 return res;
1313         case AST_ACTION_WAITOPTION:
1314                 res = ast_waitfordigit(chan, 1000 * (chan->pbx ? chan->pbx->rtimeout : 10));
1315                 if (!res)
1316                         return 't';
1317                 return res;
1318         case AST_ACTION_CALLBACK:
1319                 ivr_func = option->adata;
1320                 res = ivr_func(chan, cbdata);
1321                 return res;
1322         case AST_ACTION_TRANSFER:
1323                 res = ast_parseable_goto(chan, option->adata);
1324                 return 0;
1325         case AST_ACTION_PLAYLIST:
1326         case AST_ACTION_BACKLIST:
1327                 res = 0;
1328                 c = ast_strdupa(option->adata);
1329                 if (c) {
1330                         while((n = strsep(&c, ";")))
1331                                 if ((res = ast_streamfile(chan, n, chan->language)) || (res = ast_waitstream(chan, (option->action == AST_ACTION_BACKLIST) ? AST_DIGIT_ANY : "")))
1332                                         break;
1333                         ast_stopstream(chan);
1334                 }
1335                 return res;
1336         default:
1337                 ast_log(LOG_NOTICE, "Unknown dispatch function %d, ignoring!\n", option->action);
1338                 return 0;
1339         };
1340         return -1;
1341 }
1342
1343 static int option_exists(struct ast_ivr_menu *menu, char *option)
1344 {
1345         int x;
1346         for (x=0;menu->options[x].option;x++)
1347                 if (!strcasecmp(menu->options[x].option, option))
1348                         return x;
1349         return -1;
1350 }
1351
1352 static int option_matchmore(struct ast_ivr_menu *menu, char *option)
1353 {
1354         int x;
1355         for (x=0;menu->options[x].option;x++)
1356                 if ((!strncasecmp(menu->options[x].option, option, strlen(option))) && 
1357                                 (menu->options[x].option[strlen(option)]))
1358                         return x;
1359         return -1;
1360 }
1361
1362 static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, char *exten, int maxexten)
1363 {
1364         int res=0;
1365         int ms;
1366         while(option_matchmore(menu, exten)) {
1367                 ms = chan->pbx ? chan->pbx->dtimeout : 5000;
1368                 if (strlen(exten) >= maxexten - 1) 
1369                         break;
1370                 res = ast_waitfordigit(chan, ms);
1371                 if (res < 1)
1372                         break;
1373                 exten[strlen(exten) + 1] = '\0';
1374                 exten[strlen(exten)] = res;
1375         }
1376         return res > 0 ? 0 : res;
1377 }
1378
1379 static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
1380 {
1381         /* Execute an IVR menu structure */
1382         int res=0;
1383         int pos = 0;
1384         int retries = 0;
1385         char exten[AST_MAX_EXTENSION] = "s";
1386         if (option_exists(menu, "s") < 0) {
1387                 strcpy(exten, "g");
1388                 if (option_exists(menu, "g") < 0) {
1389                         ast_log(LOG_WARNING, "No 's' nor 'g' extension in menu '%s'!\n", menu->title);
1390                         return -1;
1391                 }
1392         }
1393         while(!res) {
1394                 while(menu->options[pos].option) {
1395                         if (!strcasecmp(menu->options[pos].option, exten)) {
1396                                 res = ivr_dispatch(chan, menu->options + pos, exten, cbdata);
1397                                 ast_log(LOG_DEBUG, "IVR Dispatch of '%s' (pos %d) yields %d\n", exten, pos, res);
1398                                 if (res < 0)
1399                                         break;
1400                                 else if (res & RES_UPONE)
1401                                         return 0;
1402                                 else if (res & RES_EXIT)
1403                                         return res;
1404                                 else if (res & RES_REPEAT) {
1405                                         int maxretries = res & 0xffff;
1406                                         if ((res & RES_RESTART) == RES_RESTART) {
1407                                                 retries = 0;
1408                                         } else
1409                                                 retries++;
1410                                         if (!maxretries)
1411                                                 maxretries = 3;
1412                                         if ((maxretries > 0) && (retries >= maxretries)) {
1413                                                 ast_log(LOG_DEBUG, "Max retries %d exceeded\n", maxretries);
1414                                                 return -2;
1415                                         } else {
1416                                                 if (option_exists(menu, "g") > -1) 
1417                                                         strcpy(exten, "g");
1418                                                 else if (option_exists(menu, "s") > -1)
1419                                                         strcpy(exten, "s");
1420                                         }
1421                                         pos=0;
1422                                         continue;
1423                                 } else if (res && strchr(AST_DIGIT_ANY, res)) {
1424                                         ast_log(LOG_DEBUG, "Got start of extension, %c\n", res);
1425                                         exten[1] = '\0';
1426                                         exten[0] = res;
1427                                         if ((res = read_newoption(chan, menu, exten, sizeof(exten))))
1428                                                 break;
1429                                         if (option_exists(menu, exten) < 0) {
1430                                                 if (option_exists(menu, "i")) {
1431                                                         ast_log(LOG_DEBUG, "Invalid extension entered, going to 'i'!\n");
1432                                                         strcpy(exten, "i");
1433                                                         pos = 0;
1434                                                         continue;
1435                                                 } else {
1436                                                         ast_log(LOG_DEBUG, "Aborting on invalid entry, with no 'i' option!\n");
1437                                                         res = -2;
1438                                                         break;
1439                                                 }
1440                                         } else {
1441                                                 ast_log(LOG_DEBUG, "New existing extension: %s\n", exten);
1442                                                 pos = 0;
1443                                                 continue;
1444                                         }
1445                                 }
1446                         }
1447                         pos++;
1448                 }
1449                 ast_log(LOG_DEBUG, "Stopping option '%s', res is %d\n", exten, res);
1450                 pos = 0;
1451                 if (!strcasecmp(exten, "s"))
1452                         strcpy(exten, "g");
1453                 else
1454                         break;
1455         }
1456         return res;
1457 }
1458
1459 int ast_ivr_menu_run(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
1460 {
1461         int res;
1462         res = ast_ivr_menu_run_internal(chan, menu, cbdata);
1463         /* Hide internal coding */
1464         if (res > 0)
1465                 res = 0;
1466         return res;
1467 }
1468         
1469 char *ast_read_textfile(const char *filename)
1470 {
1471         int fd;
1472         char *output=NULL;
1473         struct stat filesize;
1474         int count=0;
1475         int res;
1476         if(stat(filename,&filesize)== -1){
1477                 ast_log(LOG_WARNING,"Error can't stat %s\n", filename);
1478                 return NULL;
1479         }
1480         count=filesize.st_size + 1;
1481         fd = open(filename, O_RDONLY);
1482         if (fd < 0) {
1483                 ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", filename, strerror(errno));
1484                 return NULL;
1485         }
1486         output=(char *)malloc(count);
1487         if (output) {
1488                 res = read(fd, output, count - 1);
1489                 if (res == count - 1) {
1490                         output[res] = '\0';
1491                 } else {
1492                         ast_log(LOG_WARNING, "Short read of %s (%d of %d): %s\n", filename, res, count -  1, strerror(errno));
1493                         free(output);
1494                         output = NULL;
1495                 }
1496         } else 
1497                 ast_log(LOG_WARNING, "Out of memory!\n");
1498         close(fd);
1499         return output;
1500 }
1501
1502 int ast_parseoptions(const struct ast_option *options, struct ast_flags *flags, char **args, char *optstr)
1503 {
1504         char *s;
1505         int curarg;
1506         int argloc;
1507         char *arg;
1508         int res = 0;
1509         flags->flags = 0;
1510         if (!optstr)
1511                 return 0;
1512         s = optstr;
1513         while(*s) {
1514                 curarg = *s & 0x7f;
1515                 flags->flags |= options[curarg].flag;
1516                 argloc = options[curarg].argoption;
1517                 s++;
1518                 if (*s == '(') {
1519                         /* Has argument */
1520                         s++;
1521                         arg = s;
1522                         while(*s && (*s != ')')) s++;
1523                         if (*s) {
1524                                 if (argloc)
1525                                         args[argloc - 1] = arg;
1526                                 *s = '\0';
1527                                 s++;
1528                         } else {
1529                                 ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c'\n", curarg);
1530                                 res = -1;
1531                         }
1532                 } else if (argloc)
1533                         args[argloc - 1] = NULL;
1534         }
1535         return res;
1536 }
1537