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