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