Make voicemail timeout configurable
[asterisk/asterisk.git] / file.c
1 /*m
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Generic File Format Support.
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <asterisk/frame.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/sched.h>
19 #include <asterisk/options.h>
20 #include <asterisk/translate.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <pthread.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <dirent.h>
29 #include <sys/stat.h>
30 #include "asterisk.h"
31 #include "astconf.h"
32
33 struct ast_format {
34         /* Name of format */
35         char name[80];
36         /* Extensions (separated by | if more than one) 
37            this format can read.  First is assumed for writing (e.g. .mp3) */
38         char exts[80];
39         /* Format of frames it uses/provides (one only) */
40         int format;
41         /* Open an input stream, and start playback */
42         struct ast_filestream * (*open)(int fd);
43         /* Open an output stream, of a given file descriptor and comment it appropriately if applicable */
44         struct ast_filestream * (*rewrite)(int fd, char *comment);
45         /* Apply a reading filestream to a channel */
46         int (*apply)(struct ast_channel *, struct ast_filestream *);
47         /* play filestream on a channel */
48         int (*play)(struct ast_filestream *);
49         /* Write a frame to a channel */
50         int (*write)(struct ast_filestream *, struct ast_frame *);
51         /* seek num samples into file, whence(think normal seek) */
52         int (*seek)(struct ast_filestream *, long offset, int whence);
53         /* trunc file to current position */
54         int (*trunc)(struct ast_filestream *fs);
55         /* tell current position */
56         long (*tell)(struct ast_filestream *fs);
57         /* Read the next frame from the filestream (if available) */
58         struct ast_frame * (*read)(struct ast_filestream *);
59         /* Close file, and destroy filestream structure */
60         void (*close)(struct ast_filestream *);
61         /* Retrieve file comment */
62         char * (*getcomment)(struct ast_filestream *);
63         /* Link */
64         struct ast_format *next;
65 };
66
67 struct ast_filestream {
68         /* Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */
69         struct ast_format *fmt;
70         /* Transparently translate from another format -- just once */
71         struct ast_trans_pvt *trans;
72         struct ast_tranlator_pvt *tr;
73 };
74
75 static pthread_mutex_t formatlock = AST_MUTEX_INITIALIZER;
76
77 static struct ast_format *formats = NULL;
78
79 int ast_format_register(char *name, char *exts, int format,
80                                                 struct ast_filestream * (*open)(int fd),
81                                                 struct ast_filestream * (*rewrite)(int fd, char *comment),
82                                                 int (*apply)(struct ast_channel *, struct ast_filestream *),
83                                                 int (*play)(struct ast_filestream *),
84                                                 int (*write)(struct ast_filestream *, struct ast_frame *),
85                                                 int (*seek)(struct ast_filestream *, long sample_offset, int whence),
86                                                 int (*trunc)(struct ast_filestream *),
87                                                 long (*tell)(struct ast_filestream *),
88                                                 struct ast_frame * (*read)(struct ast_filestream *),
89                                                 void (*close)(struct ast_filestream *),
90                                                 char * (*getcomment)(struct ast_filestream *))
91 {
92         struct ast_format *tmp;
93         if (ast_pthread_mutex_lock(&formatlock)) {
94                 ast_log(LOG_WARNING, "Unable to lock format list\n");
95                 return -1;
96         }
97         tmp = formats;
98         while(tmp) {
99                 if (!strcasecmp(name, tmp->name)) {
100                         ast_pthread_mutex_unlock(&formatlock);
101                         ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", name);
102                         return -1;
103                 }
104                 tmp = tmp->next;
105         }
106         tmp = malloc(sizeof(struct ast_format));
107         if (!tmp) {
108                 ast_log(LOG_WARNING, "Out of memory\n");
109                 ast_pthread_mutex_unlock(&formatlock);
110                 return -1;
111         }
112         strncpy(tmp->name, name, sizeof(tmp->name)-1);
113         strncpy(tmp->exts, exts, sizeof(tmp->exts)-1);
114         tmp->open = open;
115         tmp->rewrite = rewrite;
116         tmp->apply = apply;
117         tmp->play = play;
118         tmp->read = read;
119         tmp->write = write;
120         tmp->seek = seek;
121         tmp->trunc = trunc;
122         tmp->tell = tell;
123         tmp->close = close;
124         tmp->format = format;
125         tmp->getcomment = getcomment;
126         tmp->next = formats;
127         formats = tmp;
128         ast_pthread_mutex_unlock(&formatlock);
129         if (option_verbose > 1)
130                 ast_verbose( VERBOSE_PREFIX_2 "Registered file format %s, extension(s) %s\n", name, exts);
131         return 0;
132 }
133
134 int ast_format_unregister(char *name)
135 {
136         struct ast_format *tmp, *tmpl = NULL;
137         if (ast_pthread_mutex_lock(&formatlock)) {
138                 ast_log(LOG_WARNING, "Unable to lock format list\n");
139                 return -1;
140         }
141         tmp = formats;
142         while(tmp) {
143                 if (!strcasecmp(name, tmp->name)) {
144                         if (tmpl) 
145                                 tmpl->next = tmp->next;
146                         else
147                                 formats = tmp->next;
148                         free(tmp);
149                         ast_pthread_mutex_unlock(&formatlock);
150                         if (option_verbose > 1)
151                                 ast_verbose( VERBOSE_PREFIX_2 "Unregistered format %s\n", name);
152                         return 0;
153                 }
154                 tmp = tmp->next;
155         }
156         ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name);
157         return -1;
158 }
159
160 int ast_stopstream(struct ast_channel *tmp)
161 {
162         /* Stop a running stream if there is one */
163         if (!tmp->stream) 
164                 return 0;
165         tmp->stream->fmt->close(tmp->stream);
166         if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
167                 ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
168         return 0;
169 }
170
171 int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
172 {
173         struct ast_frame *trf;
174         int res = -1;
175         if (f->frametype != AST_FRAME_VOICE) {
176                 ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
177                 return -1;
178         }
179         if ((fs->fmt->format & f->subclass) == f->subclass) {
180                 res =  fs->fmt->write(fs, f);
181                 if (res < 0) 
182                         ast_log(LOG_WARNING, "Natural write failed\n");
183                 if (res > 0)
184                         ast_log(LOG_WARNING, "Huh??\n");
185                 return res;
186         } else {
187                 /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
188                        the one we've setup a translator for, we do the "wrong thing" XXX */
189                 if (!fs->trans) 
190                         fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass);
191                 if (!fs->trans)
192                         ast_log(LOG_WARNING, "Unable to translate to format %s, source format %d\n", fs->fmt->name, f->subclass);
193                 else {
194                         res = 0;
195                         /* Get the translated frame but don't consume the original in case they're using it on another stream */
196                         trf = ast_translate(fs->trans, f, 0);
197                         if (trf) {
198                                 res = fs->fmt->write(fs, trf);
199                                 if (res) 
200                                         ast_log(LOG_WARNING, "Translated frame write failed\n");
201                         } else
202                                 res = 0;
203                 }
204                 return res;
205         }
206 }
207
208 static int copy(char *infile, char *outfile)
209 {
210         int ifd;
211         int ofd;
212         int res;
213         int len;
214         char buf[4096];
215         if ((ifd = open(infile, O_RDONLY)) < 0) {
216                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
217                 return -1;
218         }
219         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
220                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
221                 close(ifd);
222                 return -1;
223         }
224         do {
225                 len = read(ifd, buf, sizeof(buf));
226                 if (len < 0) {
227                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
228                         close(ifd);
229                         close(ofd);
230                         unlink(outfile);
231                 }
232                 if (len) {
233                         res = write(ofd, buf, len);
234                         if (res != len) {
235                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
236                                 close(ifd);
237                                 close(ofd);
238                                 unlink(outfile);
239                         }
240                 }
241         } while(len);
242         close(ifd);
243         close(ofd);
244         return 0;
245 }
246
247 static char *build_filename(char *filename, char *ext)
248 {
249         char *fn;
250         char tmp[AST_CONFIG_MAX_PATH];
251         snprintf((char *)tmp,sizeof(tmp)-1,"%s/%s",(char *)ast_config_AST_VAR_DIR,"sounds");
252         fn = malloc(strlen(tmp) + strlen(filename) + strlen(ext) + 10);
253         if (fn) {
254                 if (filename[0] == '/') 
255                         sprintf(fn, "%s.%s", filename, ext);
256                 else
257                         sprintf(fn, "%s/%s.%s", (char *)tmp, filename, ext);
258         }
259         return fn;
260         
261 }
262
263 #define ACTION_EXISTS 1
264 #define ACTION_DELETE 2
265 #define ACTION_RENAME 3
266 #define ACTION_OPEN   4
267 #define ACTION_COPY   5
268
269 static int ast_filehelper(char *filename, char *filename2, char *fmt, int action)
270 {
271         struct stat st;
272         struct ast_format *f;
273         struct ast_filestream *s;
274         int res=0, ret = 0;
275         char *ext=NULL, *exts, *fn, *nfn;
276         struct ast_channel *chan = (struct ast_channel *)filename2;
277         
278         /* Start with negative response */
279         if (action == ACTION_EXISTS)
280                 res = 0;
281         else
282                 res = -1;
283         if (action == ACTION_OPEN)
284                 ret = -1;
285         /* Check for a specific format */
286         if (ast_pthread_mutex_lock(&formatlock)) {
287                 ast_log(LOG_WARNING, "Unable to lock format list\n");
288                 if (action == ACTION_EXISTS)
289                         return 0;
290                 else
291                         return -1;
292         }
293         f = formats;
294         while(f) {
295                 if (!fmt || !strcasecmp(f->name, fmt)) {
296                         char *stringp=NULL;
297                         exts = strdup(f->exts);
298                         /* Try each kind of extension */
299                         stringp=exts;
300                         ext = strsep(&stringp, "|");
301                         do {
302                                 fn = build_filename(filename, ext);
303                                 if (fn) {
304                                         res = stat(fn, &st);
305                                         if (!res) {
306                                                 switch(action) {
307                                                 case ACTION_EXISTS:
308                                                         ret |= f->format;
309                                                         break;
310                                                 case ACTION_DELETE:
311                                                         res = unlink(fn);
312                                                         if (res)
313                                                                 ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
314                                                         break;
315                                                 case ACTION_RENAME:
316                                                         nfn = build_filename(filename2, ext);
317                                                         if (nfn) {
318                                                                 res = rename(fn, nfn);
319                                                                 if (res)
320                                                                         ast_log(LOG_WARNING, "rename(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
321                                                                 free(nfn);
322                                                         } else
323                                                                 ast_log(LOG_WARNING, "Out of memory\n");
324                                                         break;
325                                                 case ACTION_COPY:
326                                                         nfn = build_filename(filename2, ext);
327                                                         if (nfn) {
328                                                                 res = copy(fn, nfn);
329                                                                 if (res)
330                                                                         ast_log(LOG_WARNING, "copy(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
331                                                                 free(nfn);
332                                                         } else
333                                                                 ast_log(LOG_WARNING, "Out of memory\n");
334                                                         break;
335                                                 case ACTION_OPEN:
336                                                         if ((ret < 0) && ((chan->writeformat & f->format))) {
337                                                                 ret = open(fn, O_RDONLY);
338                                                                 if (ret >= 0) {
339                                                                         s = f->open(ret);
340                                                                         if (s) {
341                                                                                 s->fmt = f;
342                                                                                 s->trans = NULL;
343                                                                                 chan->stream = s;
344                                                                         } else {
345                                                                                 close(ret);
346                                                                                 ast_log(LOG_WARNING, "Unable to open fd on %s\n", filename);
347                                                                         }
348                                                                 } else
349                                                                         ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
350                                                         }
351                                                         break;
352                                                 default:
353                                                         ast_log(LOG_WARNING, "Unknown helper %d\n", action);
354                                                 }
355                                                 /* Conveniently this logic is the same for all */
356                                                 if (res)
357                                                         break;
358                                         }
359                                         free(fn);
360                                 }
361                                 ext = strsep(&stringp, "|");
362                         } while(ext);
363                         free(exts);
364                 }
365                 f = f->next;
366         }
367         ast_pthread_mutex_unlock(&formatlock);
368         if ((action == ACTION_EXISTS) || (action == ACTION_OPEN))
369                 res = ret ? ret : -1;
370         return res;
371 }
372
373 struct ast_filestream *ast_openstream(struct ast_channel *chan, char *filename, char *preflang)
374 {
375         /* This is a fairly complex routine.  Essentially we should do 
376            the following:
377            
378            1) Find which file handlers produce our type of format.
379            2) Look for a filename which it can handle.
380            3) If we find one, then great.  
381            4) If not, see what files are there
382            5) See what we can actually support
383            6) Choose the one with the least costly translator path and
384                set it up.
385                    
386         */
387         int fd = -1;
388         int fmts = -1;
389         char filename2[256];
390         char lang2[MAX_LANGUAGE];
391         int res;
392         ast_stopstream(chan);
393         /* do this first, otherwise we detect the wrong writeformat */
394         if (chan->generator)
395                 ast_deactivate_generator(chan);
396         if (preflang && strlen(preflang)) {
397                 snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
398                 fmts = ast_fileexists(filename2, NULL, NULL);
399                 if (fmts < 1) {
400                         strncpy(lang2, preflang, sizeof(lang2)-1);
401                         snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
402                         fmts = ast_fileexists(filename2, NULL, NULL);
403                 }
404         }
405         if (fmts < 1) {
406                 strncpy(filename2, filename, sizeof(filename2)-1);
407                 fmts = ast_fileexists(filename2, NULL, NULL);
408         }
409         if (fmts < 1) {
410                 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
411                 return NULL;
412         }
413         chan->oldwriteformat = chan->writeformat;
414         /* Set the channel to a format we can work with */
415         res = ast_set_write_format(chan, fmts);
416         
417         fd = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
418         if(fd >= 0)
419                 return chan->stream;
420         return NULL;
421 }
422
423 int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
424 {
425         if(chan->stream->fmt->apply(chan,s)){
426                 chan->stream->fmt->close(s);
427                 chan->stream = NULL;
428                 ast_log(LOG_WARNING, "Unable to apply stream to channel %s\n", chan->name);
429                 return -1;
430         }
431         return 0;
432 }
433
434 int ast_playstream(struct ast_filestream *s)
435 {
436         if(s->fmt->play(s)){
437                 ast_closestream(s);
438                 ast_log(LOG_WARNING, "Unable to start playing stream\n");
439                 return -1;
440         }
441         return 0;
442 }
443
444 int ast_seekstream(struct ast_filestream *fs, long sample_offset, int whence)
445 {
446         return fs->fmt->seek(fs, sample_offset, whence);
447 }
448
449 int ast_truncstream(struct ast_filestream *fs)
450 {
451         return fs->fmt->trunc(fs);
452 }
453
454 long ast_tellstream(struct ast_filestream *fs)
455 {
456         return fs->fmt->tell(fs);
457 }
458
459 int ast_stream_fastforward(struct ast_filestream *fs, long ms)
460 {
461         /* I think this is right, 8000 samples per second, 1000 ms a second so 8
462          * samples per ms  */
463         long samples = ms * 8;
464         return ast_seekstream(fs, samples, SEEK_CUR);
465 }
466
467 int ast_stream_rewind(struct ast_filestream *fs, long ms)
468 {
469         long samples = ms * 8;
470         samples = samples * -1;
471         return ast_seekstream(fs, samples, SEEK_CUR);
472 }
473
474 int ast_closestream(struct ast_filestream *f)
475 {
476         /* Stop a running stream if there is one */
477         f->fmt->close(f);
478         return 0;
479 }
480
481
482 int ast_fileexists(char *filename, char *fmt, char *preflang)
483 {
484         char filename2[256];
485         char tmp[256];
486         char *postfix;
487         char *prefix;
488         char *c;
489         char lang2[MAX_LANGUAGE];
490         int res = -1;
491         if (preflang && strlen(preflang)) {
492                 /* Insert the language between the last two parts of the path */
493                 strncpy(tmp, filename, sizeof(tmp) - 1);
494                 c = strrchr(tmp, '/');
495                 if (c) {
496                         *c = '\0';
497                         postfix = c+1;
498                         prefix = tmp;
499                 } else {
500                         postfix = tmp;
501                         prefix="";
502                 }
503                 snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, preflang, postfix);
504                 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
505                 if (res < 1) {
506                         char *stringp=NULL;
507                         strncpy(lang2, preflang, sizeof(lang2)-1);
508                         stringp=lang2;
509                         strsep(&stringp, "_");
510                         if (strcmp(lang2, preflang)) {
511                                 snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, lang2, postfix);
512                                 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
513                         }
514                 }
515         }
516         if (res < 1) {
517                 res = ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
518         }
519         return res;
520 }
521
522 int ast_filedelete(char *filename, char *fmt)
523 {
524         return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
525 }
526
527 int ast_filerename(char *filename, char *filename2, char *fmt)
528 {
529         return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
530 }
531
532 int ast_filecopy(char *filename, char *filename2, char *fmt)
533 {
534         return ast_filehelper(filename, filename2, fmt, ACTION_COPY);
535 }
536
537 int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang)
538 {
539         struct ast_filestream *fs;
540
541         fs = ast_openstream(chan, filename, preflang);
542         if(fs){
543                 if(ast_applystream(chan, fs))
544                         return -1;
545                 if(ast_playstream(fs))
546                         return -1;
547 #if 1
548                 if (option_verbose > 2)
549                         ast_verbose(VERBOSE_PREFIX_3 "Playing '%s'\n", filename);
550 #endif
551                 return 0;
552         }
553         ast_log(LOG_WARNING, "Unable to open %s (format %d): %s\n", filename, chan->nativeformats, strerror(errno));
554         return -1;
555 }
556
557
558 struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
559 {
560         int fd,myflags;
561         struct ast_format *f;
562         struct ast_filestream *fs=NULL;
563         char *fn;
564         char *ext;
565         if (ast_pthread_mutex_lock(&formatlock)) {
566                 ast_log(LOG_WARNING, "Unable to lock format list\n");
567                 return NULL;
568         }
569         myflags = 0;
570         /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
571         //if (!(flags & O_APPEND)) myflags = O_TRUNC;
572         f = formats;
573         while(f) {
574                 if (!strcasecmp(f->name, type)) {
575                         char *stringp=NULL;
576                         /* XXX Implement check XXX */
577                         ext = strdup(f->exts);
578                         stringp=ext;
579                         ext = strsep(&stringp, "|");
580                         fn = build_filename(filename, ext);
581                         fd = open(fn, flags | myflags | O_WRONLY | O_CREAT, mode);
582                         if (fd >= 0) {
583                                 errno = 0;
584                                 if ((fs = f->rewrite(fd, comment))) {
585                                         fs->trans = NULL;
586                                         fs->fmt = f;
587                                 } else {
588                                         ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
589                                         close(fd);
590                                         unlink(fn);
591                                 }
592                         } else if (errno != EEXIST)
593                                 ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
594                         free(fn);
595                         free(ext);
596                         break;
597                 }
598                 f = f->next;
599         }
600         ast_pthread_mutex_unlock(&formatlock);
601         if (!f) 
602                 ast_log(LOG_WARNING, "No such format '%s'\n", type);
603         return fs;
604 }
605
606 char ast_waitstream(struct ast_channel *c, char *breakon)
607 {
608         /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */
609         int res;
610         struct ast_frame *fr;
611         while(c->stream) {
612                 res = ast_sched_wait(c->sched);
613                 if (res < 0) {
614                         ast_closestream(c->stream);
615                         break;
616                 }
617                 res = ast_waitfor(c, res);
618                 if (res < 0) {
619                         ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
620                         return res;
621                 } else
622                 if (res > 0) {
623                         fr = ast_read(c);
624                         if (!fr) {
625 #if 0
626                                 ast_log(LOG_DEBUG, "Got hung up\n");
627 #endif
628                                 return -1;
629                         }
630                         
631                         switch(fr->frametype) {
632                         case AST_FRAME_DTMF:
633                                 res = fr->subclass;
634                                 if (strchr(breakon, res)) {
635                                         ast_frfree(fr);
636                                         return res;
637                                 }
638                                 break;
639                         case AST_FRAME_CONTROL:
640                                 switch(fr->subclass) {
641                                 case AST_CONTROL_HANGUP:
642                                         ast_frfree(fr);
643                                         return -1;
644                                 case AST_CONTROL_RINGING:
645                                 case AST_CONTROL_ANSWER:
646                                         /* Unimportant */
647                                         break;
648                                 default:
649                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
650                                 }
651                         }
652                         /* Ignore */
653                         ast_frfree(fr);
654                 } else
655                         ast_sched_runq(c->sched);
656         
657                 
658         }
659         return (c->_softhangup ? -1 : 0);
660 }
661
662 char ast_waitstream_fr(struct ast_channel *c, char *breakon, char *forward, char *rewind, int ms)
663 {
664         int res;
665         struct ast_frame *fr;
666         while(c->stream) {
667                 res = ast_sched_wait(c->sched);
668                 if (res < 0) {
669                         ast_closestream(c->stream);
670                         break;
671                 }
672                 res = ast_waitfor(c, res);
673                 if (res < 0) {
674                         ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
675                         return res;
676                 } else
677                 if (res > 0) {
678                         fr = ast_read(c);
679                         if (!fr) {
680 #if 0
681                                 ast_log(LOG_DEBUG, "Got hung up\n");
682 #endif
683                                 return -1;
684                         }
685                         
686                         switch(fr->frametype) {
687                         case AST_FRAME_DTMF:
688                                 res = fr->subclass;
689                                 if (strchr(forward,res)) {
690                                         ast_stream_fastforward(c->stream, ms);
691                                 } else if (strchr(rewind,res)) {
692                                         ast_stream_rewind(c->stream, ms);
693                                 } else if (strchr(breakon, res)) {
694                                         ast_frfree(fr);
695                                         return res;
696                                 }                                       
697                                 break;
698                         case AST_FRAME_CONTROL:
699                                 switch(fr->subclass) {
700                                 case AST_CONTROL_HANGUP:
701                                         ast_frfree(fr);
702                                         return -1;
703                                 case AST_CONTROL_RINGING:
704                                 case AST_CONTROL_ANSWER:
705                                         /* Unimportant */
706                                         break;
707                                 default:
708                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
709                                 }
710                         }
711                         /* Ignore */
712                         ast_frfree(fr);
713                 } else
714                         ast_sched_runq(c->sched);
715         
716                 
717         }
718         return (c->_softhangup ? -1 : 0);
719 }
720
721 char ast_waitstream_full(struct ast_channel *c, char *breakon, int audiofd, int cmdfd)
722 {
723         int res;
724         int ms;
725         int outfd;
726         struct ast_frame *fr;
727         struct ast_channel *rchan;
728         
729         while(c->stream) {
730                 ms = ast_sched_wait(c->sched);
731                 if (ms < 0) {
732                         ast_closestream(c->stream);
733                         break;
734                 }
735                 rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
736                 if (!rchan && (outfd < 0) && (ms)) {
737                         ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
738                         return -1;
739                 } else if (outfd > -1) {
740                         /* The FD we were watching has something waiting */
741                         return 1;
742                 } else if (rchan) {
743                         fr = ast_read(c);
744                         if (!fr) {
745 #if 0
746                                 ast_log(LOG_DEBUG, "Got hung up\n");
747 #endif
748                                 return -1;
749                         }
750                         
751                         switch(fr->frametype) {
752                         case AST_FRAME_DTMF:
753                                 res = fr->subclass;
754                                 if (strchr(breakon, res)) {
755                                         ast_frfree(fr);
756                                         return res;
757                                 }
758                                 break;
759                         case AST_FRAME_CONTROL:
760                                 switch(fr->subclass) {
761                                 case AST_CONTROL_HANGUP:
762                                         ast_frfree(fr);
763                                         return -1;
764                                 case AST_CONTROL_RINGING:
765                                 case AST_CONTROL_ANSWER:
766                                         /* Unimportant */
767                                         break;
768                                 default:
769                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
770                                 }
771                         case AST_FRAME_VOICE:
772                                 /* Write audio if appropriate */
773                                 if (audiofd > -1)
774                                         write(audiofd, fr->data, fr->datalen);
775                         }
776                         /* Ignore */
777                         ast_frfree(fr);
778                 } else
779                         ast_sched_runq(c->sched);
780         
781                 
782         }
783         return (c->_softhangup ? -1 : 0);
784 }