Merge "show features" (bug #3515)
[asterisk/asterisk.git] / app.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Convenient Application Routines
5  * 
6  * Copyright (C) 1999 - 2005, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/time.h>
18 #include <signal.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <dirent.h>
22 #include <sys/types.h>
23 #include <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, const char *file, const char *fwd, const char *rev, const char *stop, const 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, const 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, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path)
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=NULL;    /* 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         if (path)
568                 ast_unlock_path(path);
569
570
571         
572         if (maxsilence > 0) {
573                 sildet = ast_dsp_new(); /* Create the silence detector */
574                 if (!sildet) {
575                         ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
576                         return -1;
577                 }
578                 ast_dsp_set_threshold(sildet, silencethreshold);
579                 rfmt = chan->readformat;
580                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
581                 if (res < 0) {
582                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
583                         ast_dsp_free(sildet);
584                         return -1;
585                 }
586         }
587
588         if (x == fmtcnt) {
589         /* Loop forever, writing the packets we read to the writer(s), until
590            we read a # or get a hangup */
591                 f = NULL;
592                 for(;;) {
593                         res = ast_waitfor(chan, 2000);
594                         if (!res) {
595                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
596                                 /* Try one more time in case of masq */
597                                 res = ast_waitfor(chan, 2000);
598                                 if (!res) {
599                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
600                                         res = -1;
601                                 }
602                         }
603
604                         if (res < 0) {
605                                 f = NULL;
606                                 break;
607                         }
608                         f = ast_read(chan);
609                         if (!f)
610                                 break;
611                         if (f->frametype == AST_FRAME_VOICE) {
612                                 /* write each format */
613                                 for (x=0;x<fmtcnt;x++) {
614                                         res = ast_writestream(others[x], f);
615                                 }
616
617                                 /* Silence Detection */
618                                 if (maxsilence > 0) {
619                                         dspsilence = 0;
620                                         ast_dsp_silence(sildet, f, &dspsilence);
621                                         if (dspsilence)
622                                                 totalsilence = dspsilence;
623                                         else
624                                                 totalsilence = 0;
625
626                                         if (totalsilence > maxsilence) {
627                                                 /* Ended happily with silence */
628                                                 if (option_verbose > 2)
629                                                         ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
630                                                 ast_frfree(f);
631                                                 gotsilence = 1;
632                                                 outmsg=2;
633                                                 break;
634                                         }
635                                 }
636                                 /* Exit on any error */
637                                 if (res) {
638                                         ast_log(LOG_WARNING, "Error writing frame\n");
639                                         ast_frfree(f);
640                                         break;
641                                 }
642                         } else if (f->frametype == AST_FRAME_VIDEO) {
643                                 /* Write only once */
644                                 ast_writestream(others[0], f);
645                         } else if (f->frametype == AST_FRAME_DTMF) {
646                                 if (f->subclass == '#') {
647                                         if (option_verbose > 2)
648                                                 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
649                                         res = '#';
650                                         outmsg = 2;
651                                         ast_frfree(f);
652                                         break;
653                                 }
654                         }
655                                 if (f->subclass == '0') {
656                                 /* Check for a '0' during message recording also, in case caller wants operator */
657                                         if (option_verbose > 2)
658                                                 ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
659                                         res = '0';
660                                         outmsg = 0;
661                                         ast_frfree(f);
662                                         break;
663                                 }
664                         if (maxtime) {
665                                 time(&end);
666                                 if (maxtime < (end - start)) {
667                                         if (option_verbose > 2)
668                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
669                                         outmsg = 2;
670                                         res = 't';
671                                         ast_frfree(f);
672                                         break;
673                                 }
674                         }
675                         ast_frfree(f);
676                 }
677                 if (end == start) time(&end);
678                 if (!f) {
679                         if (option_verbose > 2)
680                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
681                         res = -1;
682                         outmsg=1;
683                 }
684         } else {
685                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
686         }
687
688         *duration = end - start;
689
690         for (x=0;x<fmtcnt;x++) {
691                 if (!others[x])
692                         break;
693                 if (res > 0) {
694                         if (totalsilence)
695                                 ast_stream_rewind(others[x], totalsilence-200);
696                         else
697                                 ast_stream_rewind(others[x], 200);
698                 }
699                 ast_truncstream(others[x]);
700                 ast_closestream(others[x]);
701         }
702         if (rfmt) {
703                 if (ast_set_read_format(chan, rfmt)) {
704                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
705                 }
706         }
707         if (outmsg > 1) {
708                 /* Let them know recording is stopped */
709                 if(!ast_streamfile(chan, "auth-thankyou", chan->language))
710                         ast_waitstream(chan, "");
711         }
712         if (sildet)
713                 ast_dsp_free(sildet);
714         return res;
715 }
716
717 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)
718 {
719         char d = 0, *fmts;
720         char comment[256];
721         int x, fmtcnt=1, res=-1,outmsg=0;
722         struct ast_frame *f;
723         struct ast_filestream *others[MAX_OTHER_FORMATS];
724         struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
725         char *sfmt[MAX_OTHER_FORMATS];
726         char *stringp=NULL;
727         time_t start, end;
728         struct ast_dsp *sildet;         /* silence detector dsp */
729         int totalsilence = 0;
730         int dspsilence = 0;
731         int gotsilence = 0;             /* did we timeout for silence? */
732         int rfmt=0;     
733         char prependfile[80];
734         
735         if (silencethreshold < 0)
736                 silencethreshold = global_silence_threshold;
737
738         if (maxsilence < 0)
739                 maxsilence = global_maxsilence;
740
741         /* barf if no pointer passed to store duration in */
742         if (duration == NULL) {
743                 ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
744                 return -1;
745         }
746
747         ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
748         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
749
750         if (playfile || beep) { 
751                 if (!beep)
752                         d = ast_play_and_wait(chan, playfile);
753                 if (d > -1)
754                         d = ast_streamfile(chan, "beep",chan->language);
755                 if (!d)
756                         d = ast_waitstream(chan,"");
757                 if (d < 0)
758                         return -1;
759         }
760         strncpy(prependfile, recordfile, sizeof(prependfile) -1);       
761         strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
762                         
763         fmts = ast_strdupa(fmt);
764         
765         stringp=fmts;
766         strsep(&stringp, "|");
767         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
768         sfmt[0] = ast_strdupa(fmts);
769         
770         while((fmt = strsep(&stringp, "|"))) {
771                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
772                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
773                         break;
774                 }
775                 sfmt[fmtcnt++] = ast_strdupa(fmt);
776         }
777
778         time(&start);
779         end=start;  /* pre-initialize end to be same as start in case we never get into loop */
780         for (x=0;x<fmtcnt;x++) {
781                 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
782                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
783                 if (!others[x]) {
784                         break;
785                 }
786         }
787         
788         sildet = ast_dsp_new(); /* Create the silence detector */
789         if (!sildet) {
790                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
791                 return -1;
792         }
793         ast_dsp_set_threshold(sildet, silencethreshold);
794
795         if (maxsilence > 0) {
796                 rfmt = chan->readformat;
797                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
798                 if (res < 0) {
799                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
800                         return -1;
801                 }
802         }
803                                                 
804         if (x == fmtcnt) {
805         /* Loop forever, writing the packets we read to the writer(s), until
806            we read a # or get a hangup */
807                 f = NULL;
808                 for(;;) {
809                         res = ast_waitfor(chan, 2000);
810                         if (!res) {
811                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
812                                 /* Try one more time in case of masq */
813                                 res = ast_waitfor(chan, 2000);
814                                 if (!res) {
815                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
816                                         res = -1;
817                                 }
818                         }
819                         
820                         if (res < 0) {
821                                 f = NULL;
822                                 break;
823                         }
824                         f = ast_read(chan);
825                         if (!f)
826                                 break;
827                         if (f->frametype == AST_FRAME_VOICE) {
828                                 /* write each format */
829                                 for (x=0;x<fmtcnt;x++) {
830                                         if (!others[x])
831                                                 break;
832                                         res = ast_writestream(others[x], f);
833                                 }
834                                 
835                                 /* Silence Detection */
836                                 if (maxsilence > 0) {
837                                         dspsilence = 0;
838                                         ast_dsp_silence(sildet, f, &dspsilence);
839                                         if (dspsilence)
840                                                 totalsilence = dspsilence;
841                                         else
842                                                 totalsilence = 0;
843                                         
844                                         if (totalsilence > maxsilence) {
845                                         /* Ended happily with silence */
846                                         if (option_verbose > 2) 
847                                                 ast_verbose( VERBOSE_PREFIX_3 "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
848                                         ast_frfree(f);
849                                         gotsilence = 1;
850                                         outmsg=2;
851                                         break;
852                                         }
853                                 }
854                                 /* Exit on any error */
855                                 if (res) {
856                                         ast_log(LOG_WARNING, "Error writing frame\n");
857                                         ast_frfree(f);
858                                         break;
859                                 }
860                         } else if (f->frametype == AST_FRAME_VIDEO) {
861                                 /* Write only once */
862                                 ast_writestream(others[0], f);
863                         } else if (f->frametype == AST_FRAME_DTMF) {
864                                 /* stop recording with any digit */
865                                 if (option_verbose > 2) 
866                                         ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
867                                 res = 't';
868                                 outmsg = 2;
869                                 ast_frfree(f);
870                                 break;
871                         }
872                         if (maxtime) {
873                                 time(&end);
874                                 if (maxtime < (end - start)) {
875                                         if (option_verbose > 2)
876                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
877                                         res = 't';
878                                         outmsg=2;
879                                         ast_frfree(f);
880                                         break;
881                                 }
882                         }
883                         ast_frfree(f);
884                 }
885                 if (end == start) time(&end);
886                 if (!f) {
887                         if (option_verbose > 2) 
888                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
889                         res = -1;
890                         outmsg=1;
891 #if 0
892                         /* delete all the prepend files */
893                         for (x=0;x<fmtcnt;x++) {
894                                 if (!others[x])
895                                         break;
896                                 ast_closestream(others[x]);
897                                 ast_filedelete(prependfile, sfmt[x]);
898                         }
899 #endif
900                 }
901         } else {
902                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); 
903         }
904         *duration = end - start;
905 #if 0
906         if (outmsg > 1) {
907 #else
908         if (outmsg) {
909 #endif
910                 struct ast_frame *fr;
911                 for (x=0;x<fmtcnt;x++) {
912                         snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
913                         realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
914                         if (!others[x] || !realfiles[x])
915                                 break;
916                         if (totalsilence)
917                                 ast_stream_rewind(others[x], totalsilence-200);
918                         else
919                                 ast_stream_rewind(others[x], 200);
920                         ast_truncstream(others[x]);
921                         /* add the original file too */
922                         while ((fr = ast_readframe(realfiles[x]))) {
923                                 ast_writestream(others[x],fr);
924                         }
925                         ast_closestream(others[x]);
926                         ast_closestream(realfiles[x]);
927                         ast_filerename(prependfile, recordfile, sfmt[x]);
928 #if 0
929                         ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
930 #endif
931                         ast_filedelete(prependfile, sfmt[x]);
932                 }
933         }
934         if (rfmt) {
935                 if (ast_set_read_format(chan, rfmt)) {
936                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
937                 }
938         }
939         if (outmsg) {
940                 if (outmsg > 1) {
941                         /* Let them know it worked */
942                         ast_streamfile(chan, "auth-thankyou", chan->language);
943                         ast_waitstream(chan, "");
944                 }
945         }       
946         return res;
947 }
948
949 /* Channel group core functions */
950
951 int ast_app_group_split_group(char *data, char *group, int group_max, char *category, int category_max)
952 {
953         int res=0;
954         char tmp[256] = "";
955         char *grp=NULL, *cat=NULL;
956
957         if (data && !ast_strlen_zero(data)) {
958                 strncpy(tmp, data, sizeof(tmp) - 1);
959                 grp = tmp;
960                 cat = strchr(tmp, '@');
961                 if (cat) {
962                         *cat = '\0';
963                         cat++;
964                 }
965         }
966
967         if (grp && !ast_strlen_zero(grp))
968                 strncpy(group, grp, group_max -1);
969         else
970                 res = -1;
971
972         if (cat)
973                 snprintf(category, category_max, "%s_%s", GROUP_CATEGORY_PREFIX, cat);
974         else
975                 strncpy(category, GROUP_CATEGORY_PREFIX, category_max - 1);
976
977         return res;
978 }
979
980 int ast_app_group_set_channel(struct ast_channel *chan, char *data)
981 {
982         int res=0;
983         char group[80] = "";
984         char category[80] = "";
985
986         if (!ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category))) {
987                 pbx_builtin_setvar_helper(chan, category, group);
988         } else
989                 res = -1;
990
991         return res;
992 }
993
994 int ast_app_group_get_count(char *group, char *category)
995 {
996         struct ast_channel *chan;
997         int count = 0;
998         char *test;
999         char cat[80] = "";
1000
1001         if (category && !ast_strlen_zero(category)) {
1002                 strncpy(cat, category, sizeof(cat) - 1);
1003         } else {
1004                 strncpy(cat, GROUP_CATEGORY_PREFIX, sizeof(cat) - 1);
1005         }
1006
1007         if (group && !ast_strlen_zero(group)) {
1008                 chan = ast_channel_walk_locked(NULL);
1009                 while(chan) {
1010                         test = pbx_builtin_getvar_helper(chan, cat);
1011                         if (test && !strcasecmp(test, group))
1012                                 count++;
1013                         ast_mutex_unlock(&chan->lock);
1014                         chan = ast_channel_walk_locked(chan);
1015                 }
1016         }
1017
1018         return count;
1019 }
1020
1021 int ast_app_group_match_get_count(char *groupmatch, char *category)
1022 {
1023         regex_t regexbuf;
1024         struct ast_channel *chan;
1025         int count = 0;
1026         char *test;
1027         char cat[80] = "";
1028
1029         if (!groupmatch || ast_strlen_zero(groupmatch))
1030                 return 0;
1031
1032         /* if regex compilation fails, return zero matches */
1033         if (regcomp(&regexbuf, groupmatch, REG_EXTENDED | REG_NOSUB))
1034                 return 0;
1035
1036         if (category && !ast_strlen_zero(category)) {
1037                 strncpy(cat, category, sizeof(cat) - 1);
1038         } else {
1039                 strncpy(cat, GROUP_CATEGORY_PREFIX, sizeof(cat) - 1);
1040         }
1041
1042         chan = ast_channel_walk_locked(NULL);
1043         while(chan) {
1044                 test = pbx_builtin_getvar_helper(chan, cat);
1045                 if (test && !regexec(&regexbuf, test, 0, NULL, 0))
1046                         count++;
1047                 ast_mutex_unlock(&chan->lock);
1048                 chan = ast_channel_walk_locked(chan);
1049         }
1050
1051         regfree(&regexbuf);
1052
1053         return count;
1054 }
1055
1056 int ast_seperate_app_args(char *buf, char delim, char **array, int arraylen)
1057 {
1058         int x = 0;
1059         memset(array, 0, arraylen * sizeof(char *));
1060         if (!buf)
1061                 return 0;
1062         for (array[x] = buf ; x < arraylen && array[x]; x++) {
1063                 if ((array[x+1] = strchr(array[x], delim))) {
1064                         *array[x+1] = '\0';
1065                         array[x+1]++;
1066                 }
1067         }
1068         return x;
1069 }
1070
1071 int ast_lock_path(const char *path)
1072 {
1073         char *s;
1074         char *fs;
1075         int res;
1076         int fd;
1077         time_t start;
1078         s = alloca(strlen(path) + 10);
1079         fs = alloca(strlen(path) + 20);
1080         if (!fs || !s) {
1081                 ast_log(LOG_WARNING, "Out of memory!\n");
1082                 return -1;
1083         }
1084         snprintf(fs, strlen(path) + 19, "%s/%s-%08x", path, ".lock", rand());
1085         fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, 0600);
1086         if (fd < 0) {
1087                 fprintf(stderr, "Unable to create lock file: %s\n", strerror(errno));
1088                 return -1;
1089         }
1090         close(fd);
1091         snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
1092         time(&start);
1093         while (((res = link(fs, s)) < 0) && (errno == EEXIST) && (time(NULL) - start < 5))
1094                 usleep(1);
1095         if (res < 0) {
1096                 ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n", path, strerror(errno));
1097         }
1098         unlink(fs);
1099         ast_log(LOG_DEBUG, "Locked path '%s'\n", path);
1100         return res;
1101 }
1102
1103 int ast_unlock_path(const char *path)
1104 {
1105         char *s;
1106         s = alloca(strlen(path) + 10);
1107         if (!s)
1108                 return -1;
1109         snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
1110         ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
1111         return unlink(s);
1112 }
1113
1114 int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path) 
1115 {
1116         int silencethreshold = 128; 
1117         int maxsilence=0;
1118         int res = 0;
1119         int cmd = 0;
1120         int max_attempts = 3;
1121         int attempts = 0;
1122         int recorded = 0;
1123         int message_exists = 0;
1124         /* Note that urgent and private are for flagging messages as such in the future */
1125
1126         /* barf if no pointer passed to store duration in */
1127         if (duration == NULL) {
1128                 ast_log(LOG_WARNING, "Error ast_record_review called without duration pointer\n");
1129                 return -1;
1130         }
1131
1132         cmd = '3';       /* Want to start by recording */
1133
1134         while ((cmd >= 0) && (cmd != 't')) {
1135                 switch (cmd) {
1136                 case '1':
1137                         if (!message_exists) {
1138                                 /* In this case, 1 is to record a message */
1139                                 cmd = '3';
1140                                 break;
1141                         } else {
1142                                 ast_streamfile(chan, "vm-msgsaved", chan->language);
1143                                 ast_waitstream(chan, "");
1144                                 cmd = 't';
1145                                 return res;
1146                         }
1147                 case '2':
1148                         /* Review */
1149                         ast_verbose(VERBOSE_PREFIX_3 "Reviewing the recording\n");
1150                         ast_streamfile(chan, recordfile, chan->language);
1151                         cmd = ast_waitstream(chan, AST_DIGIT_ANY);
1152                         break;
1153                 case '3':
1154                         message_exists = 0;
1155                         /* Record */
1156                         if (recorded == 1)
1157                                 ast_verbose(VERBOSE_PREFIX_3 "Re-recording\n");
1158                         else    
1159                                 ast_verbose(VERBOSE_PREFIX_3 "Recording\n");
1160                         recorded = 1;
1161                         cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, path);
1162                         if (cmd == -1) {
1163                         /* User has hung up, no options to give */
1164                                 return cmd;
1165                         }
1166                         if (cmd == '0') {
1167                                 break;
1168                         } else if (cmd == '*') {
1169                                 break;
1170                         } 
1171                         else {
1172                                 /* If all is well, a message exists */
1173                                 message_exists = 1;
1174                                 cmd = 0;
1175                         }
1176                         break;
1177                 case '4':
1178                 case '5':
1179                 case '6':
1180                 case '7':
1181                 case '8':
1182                 case '9':
1183                 case '*':
1184                 case '#':
1185                         cmd = ast_play_and_wait(chan, "vm-sorry");
1186                         break;
1187                 default:
1188                         if (message_exists) {
1189                                 cmd = ast_play_and_wait(chan, "vm-review");
1190                         }
1191                         else {
1192                                 cmd = ast_play_and_wait(chan, "vm-torerecord");
1193                                 if (!cmd)
1194                                         cmd = ast_waitfordigit(chan, 600);
1195                         }
1196                         
1197                         if (!cmd)
1198                                 cmd = ast_waitfordigit(chan, 6000);
1199                         if (!cmd) {
1200                                 attempts++;
1201                         }
1202                         if (attempts > max_attempts) {
1203                                 cmd = 't';
1204                         }
1205                 }
1206         }
1207         if (cmd == 't')
1208                 cmd = 0;
1209         return cmd;
1210 }