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