add missing ~ to ast_parse_allow_deny that was breaking disallow
[asterisk/asterisk.git] / app.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Convenient Application Routines
5  * 
6  * Copyright (C) 1999-2004, 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 <regex.h>
24 #include <asterisk/channel.h>
25 #include <asterisk/pbx.h>
26 #include <asterisk/file.h>
27 #include <asterisk/app.h>
28 #include <asterisk/dsp.h>
29 #include <asterisk/logger.h>
30 #include <asterisk/options.h>
31 #include <asterisk/utils.h>
32 #include <asterisk/lock.h>
33 #include "asterisk.h"
34 #include "astconf.h"
35
36 #define MAX_OTHER_FORMATS 10
37
38 /* set timeout to 0 for "standard" timeouts. Set timeout to -1 for 
39    "ludicrous time" (essentially never times out) */
40 int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout)
41 {
42         int res,to,fto;
43         /* XXX Merge with full version? XXX */
44         if (maxlen)
45                 s[0] = '\0';
46         if (prompt) {
47                 res = ast_streamfile(c, prompt, c->language);
48                 if (res < 0)
49                         return res;
50         }
51         fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
52         to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
53
54         if (timeout > 0) fto = to = timeout;
55         if (timeout < 0) fto = to = 1000000000;
56         res = ast_readstring(c, s, maxlen, to, fto, "#");
57         return res;
58 }
59
60
61 int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
62 {
63         int res,to,fto;
64         if (prompt) {
65                 res = ast_streamfile(c, prompt, c->language);
66                 if (res < 0)
67                         return res;
68         }
69         fto = 6000;
70         to = 2000;
71         if (timeout > 0) fto = to = timeout;
72         if (timeout < 0) fto = to = 1000000000;
73         res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd);
74         return res;
75 }
76
77 int ast_app_getvoice(struct ast_channel *c, char *dest, char *dstfmt, char *prompt, int silence, int maxsec)
78 {
79         int res;
80         struct ast_filestream *writer;
81         int rfmt;
82         int totalms=0, total;
83         
84         struct ast_frame *f;
85         struct ast_dsp *sildet;
86         /* Play prompt if requested */
87         if (prompt) {
88                 res = ast_streamfile(c, prompt, c->language);
89                 if (res < 0)
90                         return res;
91                 res = ast_waitstream(c,"");
92                 if (res < 0)
93                         return res;
94         }
95         rfmt = c->readformat;
96         res = ast_set_read_format(c, AST_FORMAT_SLINEAR);
97         if (res < 0) {
98                 ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
99                 return -1;
100         }
101         sildet = ast_dsp_new();
102         if (!sildet) {
103                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
104                 return -1;
105         }
106         writer = ast_writefile(dest, dstfmt, "Voice file", 0, 0, 0666);
107         if (!writer) {
108                 ast_log(LOG_WARNING, "Unable to open file '%s' in format '%s' for writing\n", dest, dstfmt);
109                 ast_dsp_free(sildet);
110                 return -1;
111         }
112         for(;;) {
113                 if ((res = ast_waitfor(c, 2000)) < 0) {
114                         ast_log(LOG_NOTICE, "Waitfor failed while recording file '%s' format '%s'\n", dest, dstfmt);
115                         break;
116                 }
117                 if (res) {
118                         f = ast_read(c);
119                         if (!f) {
120                                 ast_log(LOG_NOTICE, "Hungup while recording file '%s' format '%s'\n", dest, dstfmt);
121                                 break;
122                         }
123                         if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
124                                 /* Ended happily with DTMF */
125                                 ast_frfree(f);
126                                 break;
127                         } else if (f->frametype == AST_FRAME_VOICE) {
128                                 ast_dsp_silence(sildet, f, &total); 
129                                 if (total > silence) {
130                                         /* Ended happily with silence */
131                                         ast_frfree(f);
132                                         break;
133                                 }
134                                 totalms += f->samples / 8;
135                                 if (totalms > maxsec * 1000) {
136                                         /* Ended happily with too much stuff */
137                                         ast_log(LOG_NOTICE, "Constraining voice on '%s' to %d seconds\n", c->name, maxsec);
138                                         ast_frfree(f);
139                                         break;
140                                 }
141                         }
142                         ast_frfree(f);
143                 }
144         }
145         res = ast_set_read_format(c, rfmt);
146         if (res)
147                 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", c->name);
148         ast_dsp_free(sildet);
149         ast_closestream(writer);
150         return 0;
151 }
152
153 int ast_app_has_voicemail(const char *mailbox, const char *folder)
154 {
155         DIR *dir;
156         struct dirent *de;
157         char fn[256];
158         char tmp[256]="";
159         char *mb, *cur;
160         char *context;
161         int ret;
162         if (!folder)
163                 folder = "INBOX";
164         /* If no mailbox, return immediately */
165         if (ast_strlen_zero(mailbox))
166                 return 0;
167         if (strchr(mailbox, ',')) {
168                 strncpy(tmp, mailbox, sizeof(tmp) - 1);
169                 mb = tmp;
170                 ret = 0;
171                 while((cur = strsep(&mb, ","))) {
172                         if (!ast_strlen_zero(cur)) {
173                                 if (ast_app_has_voicemail(cur, folder))
174                                         return 1; 
175                         }
176                 }
177                 return 0;
178         }
179         strncpy(tmp, mailbox, sizeof(tmp) - 1);
180         context = strchr(tmp, '@');
181         if (context) {
182                 *context = '\0';
183                 context++;
184         } else
185                 context = "default";
186         snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR, context, tmp, folder);
187         dir = opendir(fn);
188         if (!dir)
189                 return 0;
190         while ((de = readdir(dir))) {
191                 if (!strncasecmp(de->d_name, "msg", 3))
192                         break;
193         }
194         closedir(dir);
195         if (de)
196                 return 1;
197         return 0;
198 }
199
200 int ast_app_messagecount(const char *mailbox, int *newmsgs, int *oldmsgs)
201 {
202         DIR *dir;
203         struct dirent *de;
204         char fn[256];
205         char tmp[256]="";
206         char *mb, *cur;
207         char *context;
208         int ret;
209         if (newmsgs)
210                 *newmsgs = 0;
211         if (oldmsgs)
212                 *oldmsgs = 0;
213         /* If no mailbox, return immediately */
214         if (ast_strlen_zero(mailbox))
215                 return 0;
216         if (strchr(mailbox, ',')) {
217                 int tmpnew, tmpold;
218                 strncpy(tmp, mailbox, sizeof(tmp) - 1);
219                 mb = tmp;
220                 ret = 0;
221                 while((cur = strsep(&mb, ", "))) {
222                         if (!ast_strlen_zero(cur)) {
223                                 if (ast_app_messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
224                                         return -1;
225                                 else {
226                                         if (newmsgs)
227                                                 *newmsgs += tmpnew; 
228                                         if (oldmsgs)
229                                                 *oldmsgs += tmpold;
230                                 }
231                         }
232                 }
233                 return 0;
234         }
235         strncpy(tmp, mailbox, sizeof(tmp) - 1);
236         context = strchr(tmp, '@');
237         if (context) {
238                 *context = '\0';
239                 context++;
240         } else
241                 context = "default";
242         if (newmsgs) {
243                 snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/INBOX", (char *)ast_config_AST_SPOOL_DIR, context, tmp);
244                 dir = opendir(fn);
245                 if (dir) {
246                         while ((de = readdir(dir))) {
247                                 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
248                                         !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
249                                                 (*newmsgs)++;
250                                         
251                         }
252                         closedir(dir);
253                 }
254         }
255         if (oldmsgs) {
256                 snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/Old", (char *)ast_config_AST_SPOOL_DIR, context, tmp);
257                 dir = opendir(fn);
258                 if (dir) {
259                         while ((de = readdir(dir))) {
260                                 if ((strlen(de->d_name) > 3) && !strncasecmp(de->d_name, "msg", 3) &&
261                                         !strcasecmp(de->d_name + strlen(de->d_name) - 3, "txt"))
262                                                 (*oldmsgs)++;
263                                         
264                         }
265                         closedir(dir);
266                 }
267         }
268         return 0;
269 }
270
271 int ast_dtmf_stream(struct ast_channel *chan,struct ast_channel *peer,char *digits,int between) 
272 {
273         char *ptr=NULL;
274         int res=0;
275         struct ast_frame f;
276         if (!between)
277                 between = 100;
278
279         if (peer)
280                 res = ast_autoservice_start(peer);
281
282         if (!res) {
283                 res = ast_waitfor(chan,100);
284                 if (res > -1) {
285                         for (ptr=digits;*ptr;*ptr++) {
286                                 if (*ptr == 'w') {
287                                         res = ast_safe_sleep(chan, 500);
288                                         if (res) 
289                                                 break;
290                                         continue;
291                                 }
292                                 memset(&f, 0, sizeof(f));
293                                 f.frametype = AST_FRAME_DTMF;
294                                 f.subclass = *ptr;
295                                 f.src = "ast_dtmf_stream";
296                                 if (strchr("0123456789*#abcdABCD",*ptr)==NULL) {
297                                         ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
298                                 } else {
299                                         res = ast_write(chan, &f);
300                                         if (res) 
301                                                 break;
302                                         /* pause between digits */
303                                         res = ast_safe_sleep(chan,between);
304                                         if (res) 
305                                                 break;
306                                 }
307                         }
308                 }
309                 if (peer)
310                         res = ast_autoservice_stop(peer);
311         }
312         return res;
313 }
314
315 struct linear_state {
316         int fd;
317         int autoclose;
318         int allowoverride;
319         int origwfmt;
320 };
321
322 static void linear_release(struct ast_channel *chan, void *params)
323 {
324         struct linear_state *ls = params;
325         if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt)) {
326                 ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, ls->origwfmt);
327         }
328         if (ls->autoclose)
329                 close(ls->fd);
330         free(params);
331 }
332
333 static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
334 {
335         struct ast_frame f;
336         short buf[2048 + AST_FRIENDLY_OFFSET / 2];
337         struct linear_state *ls = data;
338         int res;
339         len = samples * 2;
340         if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
341                 ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" ,len);
342                 len = sizeof(buf) - AST_FRIENDLY_OFFSET;
343         }
344         memset(&f, 0, sizeof(f));
345         res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
346         if (res > 0) {
347                 f.frametype = AST_FRAME_VOICE;
348                 f.subclass = AST_FORMAT_SLINEAR;
349                 f.data = buf + AST_FRIENDLY_OFFSET/2;
350                 f.datalen = res;
351                 f.samples = res / 2;
352                 f.offset = AST_FRIENDLY_OFFSET;
353                 ast_write(chan, &f);
354                 if (res == len)
355                         return 0;
356         }
357         return -1;
358 }
359
360 static void *linear_alloc(struct ast_channel *chan, void *params)
361 {
362         struct linear_state *ls;
363         /* In this case, params is already malloc'd */
364         if (params) {
365                 ls = params;
366                 if (ls->allowoverride)
367                         chan->writeinterrupt = 1;
368                 else
369                         chan->writeinterrupt = 0;
370                 ls->origwfmt = chan->writeformat;
371                 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
372                         ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
373                         free(ls);
374                         ls = params = NULL;
375                 }
376         }
377         return params;
378 }
379
380 static struct ast_generator linearstream = 
381 {
382         alloc: linear_alloc,
383         release: linear_release,
384         generate: linear_generator,
385 };
386
387 int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride)
388 {
389         struct linear_state *lin;
390         char tmpf[256] = "";
391         int res = -1;
392         int autoclose = 0;
393         if (fd < 0) {
394                 if (!filename || ast_strlen_zero(filename))
395                         return -1;
396                 autoclose = 1;
397                 if (filename[0] == '/') 
398                         strncpy(tmpf, filename, sizeof(tmpf) - 1);
399                 else
400                         snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", (char *)ast_config_AST_VAR_DIR, "sounds", filename);
401                 fd = open(tmpf, O_RDONLY);
402                 if (fd < 0){
403                         ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
404                         return -1;
405                 }
406         }
407         lin = malloc(sizeof(struct linear_state));
408         if (lin) {
409                 memset(lin, 0, sizeof(lin));
410                 lin->fd = fd;
411                 lin->allowoverride = allowoverride;
412                 lin->autoclose = autoclose;
413                 res = ast_activate_generator(chan, &linearstream, lin);
414         }
415         return res;
416 }
417
418 int ast_control_streamfile(struct ast_channel *chan, char *file, char *fwd, char *rev, char *stop, char *pause, int skipms) 
419 {
420         struct timeval started, ended;
421         long elapsed = 0,last_elapsed =0;
422         char *breaks=NULL;
423         char *end=NULL;
424         int blen=2;
425         int res=0;
426
427         if (stop)
428                 blen += strlen(stop);
429         if (pause)
430                 blen += strlen(pause);
431
432         if (blen > 2) {
433                 breaks = alloca(blen + 1);
434                 breaks[0] = '\0';
435                 strcat(breaks, stop);
436                 strcat(breaks, pause);
437         }
438         if (chan->_state != AST_STATE_UP)
439                 res = ast_answer(chan);
440
441         if (chan)
442                 ast_stopstream(chan);
443
444
445         if (file) {
446                 if ((end = strchr(file,':'))) {
447                         if (!strcasecmp(end, ":end")) {
448                                 *end = '\0';
449                                 end++;
450                         }
451                 }
452         }
453
454         for (;;) {
455                 gettimeofday(&started,NULL);
456
457                 if (chan)
458                         ast_stopstream(chan);
459                 res = ast_streamfile(chan, file, chan->language);
460                 if (!res) {
461                         if (end) {
462                                 ast_seekstream(chan->stream, 0, SEEK_END);
463                                 end=NULL;
464                         }
465                         res = 1;
466                         if (elapsed) {
467                                 ast_stream_fastforward(chan->stream, elapsed);
468                                 last_elapsed = elapsed - 200;
469                         }
470                         if (res)
471                                 res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
472                         else
473                                 break;
474                 }
475
476                 if (res < 1)
477                         break;
478
479                 if (pause != NULL && strchr(pause, res)) {
480                         gettimeofday(&ended, NULL);
481                         elapsed = (((ended.tv_sec * 1000) + ended.tv_usec / 1000) - ((started.tv_sec * 1000) + started.tv_usec / 1000) + last_elapsed);
482                         for(;;) {
483                                 if (chan)
484                                         ast_stopstream(chan);
485                                 res = ast_waitfordigit(chan, 1000);
486                                 if (res == 0)
487                                         continue;
488                                 else if (res == -1 || strchr(pause, res) || (stop && strchr(stop, res)))
489                                         break;
490                         }
491                         if (res == *pause) {
492                                 res = 0;
493                                 continue;
494                         }
495                 }
496                 if (res == -1)
497                         break;
498
499                 /* if we get one of our stop chars, return it to the calling function */
500                 if (stop && strchr(stop, res)) {
501                         /* res = 0; */
502                         break;
503                 }
504         }
505         if (chan)
506                 ast_stopstream(chan);
507
508         return res;
509 }
510
511 int ast_play_and_wait(struct ast_channel *chan, char *fn)
512 {
513         int d;
514         d = ast_streamfile(chan, fn, chan->language);
515         if (d)
516                 return d;
517         d = ast_waitstream(chan, AST_DIGIT_ANY);
518         ast_stopstream(chan);
519         return d;
520 }
521
522 static int global_silence_threshold = 128;
523 static int global_maxsilence = 0;
524
525 int ast_play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int silencethreshold, int maxsilence)
526 {
527         char d, *fmts;
528         char comment[256];
529         int x, fmtcnt=1, res=-1,outmsg=0;
530         struct ast_frame *f;
531         struct ast_filestream *others[MAX_OTHER_FORMATS];
532         char *sfmt[MAX_OTHER_FORMATS];
533         char *stringp=NULL;
534         time_t start, end;
535         struct ast_dsp *sildet;         /* silence detector dsp */
536         int totalsilence = 0;
537         int dspsilence = 0;
538         int gotsilence = 0;             /* did we timeout for silence? */
539         int rfmt=0;
540
541         if (silencethreshold < 0)
542                 silencethreshold = global_silence_threshold;
543
544         if (maxsilence < 0)
545                 maxsilence = global_maxsilence;
546
547         /* barf if no pointer passed to store duration in */
548         if (duration == NULL) {
549                 ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
550                 return -1;
551         }
552
553         ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
554         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
555
556         if (playfile) {
557                 d = ast_play_and_wait(chan, playfile);
558                 if (d > -1)
559                         d = ast_streamfile(chan, "beep",chan->language);
560                 if (!d)
561                         d = ast_waitstream(chan,"");
562                 if (d < 0)
563                         return -1;
564         }
565
566         fmts = ast_strdupa(fmt);
567
568         stringp=fmts;
569         strsep(&stringp, "|");
570         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
571         sfmt[0] = ast_strdupa(fmts);
572
573         while((fmt = strsep(&stringp, "|"))) {
574                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
575                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
576                         break;
577                 }
578                 sfmt[fmtcnt++] = ast_strdupa(fmt);
579         }
580
581         time(&start);
582         end=start;  /* pre-initialize end to be same as start in case we never get into loop */
583         for (x=0;x<fmtcnt;x++) {
584                 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
585                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
586
587                 if (!others[x]) {
588                         break;
589                 }
590         }
591
592         sildet = ast_dsp_new(); /* Create the silence detector */
593         if (!sildet) {
594                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
595                 return -1;
596         }
597         ast_dsp_set_threshold(sildet, silencethreshold);
598         
599         if (maxsilence > 0) {
600                 rfmt = chan->readformat;
601                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
602                 if (res < 0) {
603                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
604                         return -1;
605                 }
606         }
607
608         if (x == fmtcnt) {
609         /* Loop forever, writing the packets we read to the writer(s), until
610            we read a # or get a hangup */
611                 f = NULL;
612                 for(;;) {
613                         res = ast_waitfor(chan, 2000);
614                         if (!res) {
615                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
616                                 /* Try one more time in case of masq */
617                                 res = ast_waitfor(chan, 2000);
618                                 if (!res) {
619                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
620                                         res = -1;
621                                 }
622                         }
623
624                         if (res < 0) {
625                                 f = NULL;
626                                 break;
627                         }
628                         f = ast_read(chan);
629                         if (!f)
630                                 break;
631                         if (f->frametype == AST_FRAME_VOICE) {
632                                 /* write each format */
633                                 for (x=0;x<fmtcnt;x++) {
634                                         res = ast_writestream(others[x], f);
635                                 }
636
637                                 /* Silence Detection */
638                                 if (maxsilence > 0) {
639                                         dspsilence = 0;
640                                         ast_dsp_silence(sildet, f, &dspsilence);
641                                         if (dspsilence)
642                                                 totalsilence = dspsilence;
643                                         else
644                                                 totalsilence = 0;
645
646                                         if (totalsilence > maxsilence) {
647                                         /* Ended happily with silence */
648                                         if (option_verbose > 2)
649                                                 ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
650                                         ast_frfree(f);
651                                         gotsilence = 1;
652                                         outmsg=2;
653                                         break;
654                                         }
655                                 }
656                                 /* Exit on any error */
657                                 if (res) {
658                                         ast_log(LOG_WARNING, "Error writing frame\n");
659                                         ast_frfree(f);
660                                         break;
661                                 }
662                         } else if (f->frametype == AST_FRAME_VIDEO) {
663                                 /* Write only once */
664                                 ast_writestream(others[0], f);
665                         } else if (f->frametype == AST_FRAME_DTMF) {
666                                 if (f->subclass == '#') {
667                                         if (option_verbose > 2)
668                                                 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
669                                         res = '#';
670                                         outmsg = 2;
671                                         ast_frfree(f);
672                                         break;
673                                 }
674                         }
675                                 if (f->subclass == '0') {
676                                 /* Check for a '0' during message recording also, in case caller wants operator */
677                                         if (option_verbose > 2)
678                                                 ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
679                                         res = '0';
680                                         outmsg = 0;
681                                         ast_frfree(f);
682                                         break;
683                                 }
684                         if (maxtime) {
685                                 time(&end);
686                                 if (maxtime < (end - start)) {
687                                         if (option_verbose > 2)
688                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
689                                         outmsg = 2;
690                                         res = 't';
691                                         ast_frfree(f);
692                                         break;
693                                 }
694                         }
695                         ast_frfree(f);
696                 }
697                 if (end == start) time(&end);
698                 if (!f) {
699                         if (option_verbose > 2)
700                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
701                         res = -1;
702                         outmsg=1;
703                 }
704         } else {
705                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
706         }
707
708         *duration = end - start;
709
710         for (x=0;x<fmtcnt;x++) {
711                 if (!others[x])
712                         break;
713                 if (res > 0) {
714                         if (totalsilence)
715                                 ast_stream_rewind(others[x], totalsilence-200);
716                         else
717                                 ast_stream_rewind(others[x], 200);
718                 }
719                 ast_truncstream(others[x]);
720                 ast_closestream(others[x]);
721         }
722         if (rfmt) {
723                 if (ast_set_read_format(chan, rfmt)) {
724                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
725                 }
726         }
727         if (outmsg > 1) {
728                 /* Let them know recording is stopped */
729                 if(!ast_streamfile(chan, "auth-thankyou", chan->language))
730                         ast_waitstream(chan, "");
731         }
732         return res;
733 }
734
735 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)
736 {
737         char d = 0, *fmts;
738         char comment[256];
739         int x, fmtcnt=1, res=-1,outmsg=0;
740         struct ast_frame *f;
741         struct ast_filestream *others[MAX_OTHER_FORMATS];
742         struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
743         char *sfmt[MAX_OTHER_FORMATS];
744         char *stringp=NULL;
745         time_t start, end;
746         struct ast_dsp *sildet;         /* silence detector dsp */
747         int totalsilence = 0;
748         int dspsilence = 0;
749         int gotsilence = 0;             /* did we timeout for silence? */
750         int rfmt=0;     
751         char prependfile[80];
752         
753         if (silencethreshold < 0)
754                 silencethreshold = global_silence_threshold;
755
756         if (maxsilence < 0)
757                 maxsilence = global_maxsilence;
758
759         /* barf if no pointer passed to store duration in */
760         if (duration == NULL) {
761                 ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
762                 return -1;
763         }
764
765         ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
766         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
767
768         if (playfile || beep) { 
769                 if (!beep)
770                         d = ast_play_and_wait(chan, playfile);
771                 if (d > -1)
772                         d = ast_streamfile(chan, "beep",chan->language);
773                 if (!d)
774                         d = ast_waitstream(chan,"");
775                 if (d < 0)
776                         return -1;
777         }
778         strncpy(prependfile, recordfile, sizeof(prependfile) -1);       
779         strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
780                         
781         fmts = ast_strdupa(fmt);
782         
783         stringp=fmts;
784         strsep(&stringp, "|");
785         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
786         sfmt[0] = ast_strdupa(fmts);
787         
788         while((fmt = strsep(&stringp, "|"))) {
789                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
790                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
791                         break;
792                 }
793                 sfmt[fmtcnt++] = ast_strdupa(fmt);
794         }
795
796         time(&start);
797         end=start;  /* pre-initialize end to be same as start in case we never get into loop */
798         for (x=0;x<fmtcnt;x++) {
799                 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
800                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
801                 if (!others[x]) {
802                         break;
803                 }
804         }
805         
806         sildet = ast_dsp_new(); /* Create the silence detector */
807         if (!sildet) {
808                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
809                 return -1;
810         }
811         ast_dsp_set_threshold(sildet, silencethreshold);
812
813         if (maxsilence > 0) {
814                 rfmt = chan->readformat;
815                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
816                 if (res < 0) {
817                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
818                         return -1;
819                 }
820         }
821                                                 
822         if (x == fmtcnt) {
823         /* Loop forever, writing the packets we read to the writer(s), until
824            we read a # or get a hangup */
825                 f = NULL;
826                 for(;;) {
827                         res = ast_waitfor(chan, 2000);
828                         if (!res) {
829                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
830                                 /* Try one more time in case of masq */
831                                 res = ast_waitfor(chan, 2000);
832                                 if (!res) {
833                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
834                                         res = -1;
835                                 }
836                         }
837                         
838                         if (res < 0) {
839                                 f = NULL;
840                                 break;
841                         }
842                         f = ast_read(chan);
843                         if (!f)
844                                 break;
845                         if (f->frametype == AST_FRAME_VOICE) {
846                                 /* write each format */
847                                 for (x=0;x<fmtcnt;x++) {
848                                         if (!others[x])
849                                                 break;
850                                         res = ast_writestream(others[x], f);
851                                 }
852                                 
853                                 /* Silence Detection */
854                                 if (maxsilence > 0) {
855                                         dspsilence = 0;
856                                         ast_dsp_silence(sildet, f, &dspsilence);
857                                         if (dspsilence)
858                                                 totalsilence = dspsilence;
859                                         else
860                                                 totalsilence = 0;
861                                         
862                                         if (totalsilence > maxsilence) {
863                                         /* Ended happily with silence */
864                                         if (option_verbose > 2) 
865                                                 ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
866                                         ast_frfree(f);
867                                         gotsilence = 1;
868                                         outmsg=2;
869                                         break;
870                                         }
871                                 }
872                                 /* Exit on any error */
873                                 if (res) {
874                                         ast_log(LOG_WARNING, "Error writing frame\n");
875                                         ast_frfree(f);
876                                         break;
877                                 }
878                         } else if (f->frametype == AST_FRAME_VIDEO) {
879                                 /* Write only once */
880                                 ast_writestream(others[0], f);
881                         } else if (f->frametype == AST_FRAME_DTMF) {
882                                 /* stop recording with any digit */
883                                 if (option_verbose > 2) 
884                                         ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
885                                 res = 't';
886                                 outmsg = 2;
887                                 ast_frfree(f);
888                                 break;
889                         }
890                         if (maxtime) {
891                                 time(&end);
892                                 if (maxtime < (end - start)) {
893                                         if (option_verbose > 2)
894                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
895                                         res = 't';
896                                         outmsg=2;
897                                         ast_frfree(f);
898                                         break;
899                                 }
900                         }
901                         ast_frfree(f);
902                 }
903                 if (end == start) time(&end);
904                 if (!f) {
905                         if (option_verbose > 2) 
906                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
907                         res = -1;
908                         outmsg=1;
909 #if 0
910                         /* delete all the prepend files */
911                         for (x=0;x<fmtcnt;x++) {
912                                 if (!others[x])
913                                         break;
914                                 ast_closestream(others[x]);
915                                 ast_filedelete(prependfile, sfmt[x]);
916                         }
917 #endif
918                 }
919         } else {
920                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); 
921         }
922         *duration = end - start;
923 #if 0
924         if (outmsg > 1) {
925 #else
926         if (outmsg) {
927 #endif
928                 struct ast_frame *fr;
929                 for (x=0;x<fmtcnt;x++) {
930                         snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
931                         realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
932                         if (!others[x] || !realfiles[x])
933                                 break;
934                         if (totalsilence)
935                                 ast_stream_rewind(others[x], totalsilence-200);
936                         else
937                                 ast_stream_rewind(others[x], 200);
938                         ast_truncstream(others[x]);
939                         /* add the original file too */
940                         while ((fr = ast_readframe(realfiles[x]))) {
941                                 ast_writestream(others[x],fr);
942                         }
943                         ast_closestream(others[x]);
944                         ast_closestream(realfiles[x]);
945                         ast_filerename(prependfile, recordfile, sfmt[x]);
946 #if 0
947                         ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
948 #endif
949                         ast_filedelete(prependfile, sfmt[x]);
950                 }
951         }
952         if (rfmt) {
953                 if (ast_set_read_format(chan, rfmt)) {
954                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
955                 }
956         }
957         if (outmsg) {
958                 if (outmsg > 1) {
959                         /* Let them know it worked */
960                         ast_streamfile(chan, "auth-thankyou", chan->language);
961                         ast_waitstream(chan, "");
962                 }
963         }       
964         return res;
965 }
966
967 /* Channel group core functions */
968
969 int ast_app_group_split_group(char *data, char *group, int group_max, char *category, int category_max)
970 {
971         int res=0;
972         char tmp[256] = "";
973         char *grp=NULL, *cat=NULL;
974
975         if (data && !ast_strlen_zero(data)) {
976                 strncpy(tmp, data, sizeof(tmp) - 1);
977                 grp = tmp;
978                 cat = strchr(tmp, '@');
979                 if (cat) {
980                         *cat = '\0';
981                         cat++;
982                 }
983         }
984
985         if (grp && !ast_strlen_zero(grp))
986                 strncpy(group, grp, group_max -1);
987         else
988                 res = -1;
989
990         if (cat)
991                 snprintf(category, category_max, "%s_%s", GROUP_CATEGORY_PREFIX, cat);
992         else
993                 strncpy(category, GROUP_CATEGORY_PREFIX, category_max - 1);
994
995         return res;
996 }
997
998 int ast_app_group_set_channel(struct ast_channel *chan, char *data)
999 {
1000         int res=0;
1001         char group[80] = "";
1002         char category[80] = "";
1003
1004         if (!ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category))) {
1005                 pbx_builtin_setvar_helper(chan, category, group);
1006         } else
1007                 res = -1;
1008
1009         return res;
1010 }
1011
1012 int ast_app_group_get_count(char *group, char *category)
1013 {
1014         struct ast_channel *chan;
1015         int count = 0;
1016         char *test;
1017         char cat[80] = "";
1018
1019         if (category && !ast_strlen_zero(category)) {
1020                 strncpy(cat, category, sizeof(cat) - 1);
1021         } else {
1022                 strncpy(cat, GROUP_CATEGORY_PREFIX, sizeof(cat) - 1);
1023         }
1024
1025         if (group && !ast_strlen_zero(group)) {
1026                 chan = ast_channel_walk_locked(NULL);
1027                 while(chan) {
1028                         test = pbx_builtin_getvar_helper(chan, cat);
1029                         if (test && !strcasecmp(test, group))
1030                                 count++;
1031                         ast_mutex_unlock(&chan->lock);
1032                         chan = ast_channel_walk_locked(chan);
1033                 }
1034         }
1035
1036         return count;
1037 }
1038
1039 int ast_app_group_match_get_count(char *groupmatch, char *category)
1040 {
1041         regex_t regexbuf;
1042         struct ast_channel *chan;
1043         int count = 0;
1044         char *test;
1045         char cat[80] = "";
1046
1047         if (!groupmatch || ast_strlen_zero(groupmatch))
1048                 return 0;
1049
1050         /* if regex compilation fails, return zero matches */
1051         if (regcomp(&regexbuf, groupmatch, REG_EXTENDED | REG_NOSUB))
1052                 return 0;
1053
1054         if (category && !ast_strlen_zero(category)) {
1055                 strncpy(cat, category, sizeof(cat) - 1);
1056         } else {
1057                 strncpy(cat, GROUP_CATEGORY_PREFIX, sizeof(cat) - 1);
1058         }
1059
1060         chan = ast_channel_walk_locked(NULL);
1061         while(chan) {
1062                 test = pbx_builtin_getvar_helper(chan, cat);
1063                 if (test && !regexec(&regexbuf, test, 0, NULL, 0))
1064                         count++;
1065                 ast_mutex_unlock(&chan->lock);
1066                 chan = ast_channel_walk_locked(chan);
1067         }
1068
1069         regfree(&regexbuf);
1070
1071         return count;
1072 }