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