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