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