Minor cleanups
[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         /* Write a frame to a channel */
47         int (*write)(struct ast_filestream *, struct ast_frame *);
48         /* seek num samples into file, whence(think normal seek) */
49         int (*seek)(struct ast_filestream *, long offset, int whence);
50         /* trunc file to current position */
51         int (*trunc)(struct ast_filestream *fs);
52         /* tell current position */
53         long (*tell)(struct ast_filestream *fs);
54         /* Read the next frame from the filestream (if available) and report when to get next one
55                 (in samples) */
56         struct ast_frame * (*read)(struct ast_filestream *, int *whennext);
57         /* Close file, and destroy filestream structure */
58         void (*close)(struct ast_filestream *);
59         /* Retrieve file comment */
60         char * (*getcomment)(struct ast_filestream *);
61         /* Link */
62         struct ast_format *next;
63 };
64
65 struct ast_filestream {
66         /* Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */
67         struct ast_format *fmt;
68         int flags;
69         mode_t mode;
70         char *filename;
71         /* Video file stream */
72         struct ast_filestream *vfs;
73         /* Transparently translate from another format -- just once */
74         struct ast_trans_pvt *trans;
75         struct ast_tranlator_pvt *tr;
76         int lastwriteformat;
77         int lasttimeout;
78         struct ast_channel *owner;
79 };
80
81 static pthread_mutex_t formatlock = AST_MUTEX_INITIALIZER;
82
83 static struct ast_format *formats = NULL;
84
85 int ast_format_register(char *name, char *exts, int format,
86                                                 struct ast_filestream * (*open)(int fd),
87                                                 struct ast_filestream * (*rewrite)(int fd, char *comment),
88                                                 int (*write)(struct ast_filestream *, struct ast_frame *),
89                                                 int (*seek)(struct ast_filestream *, long sample_offset, int whence),
90                                                 int (*trunc)(struct ast_filestream *),
91                                                 long (*tell)(struct ast_filestream *),
92                                                 struct ast_frame * (*read)(struct ast_filestream *, int *whennext),
93                                                 void (*close)(struct ast_filestream *),
94                                                 char * (*getcomment)(struct ast_filestream *))
95 {
96         struct ast_format *tmp;
97         if (ast_pthread_mutex_lock(&formatlock)) {
98                 ast_log(LOG_WARNING, "Unable to lock format list\n");
99                 return -1;
100         }
101         tmp = formats;
102         while(tmp) {
103                 if (!strcasecmp(name, tmp->name)) {
104                         ast_pthread_mutex_unlock(&formatlock);
105                         ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", name);
106                         return -1;
107                 }
108                 tmp = tmp->next;
109         }
110         tmp = malloc(sizeof(struct ast_format));
111         if (!tmp) {
112                 ast_log(LOG_WARNING, "Out of memory\n");
113                 ast_pthread_mutex_unlock(&formatlock);
114                 return -1;
115         }
116         strncpy(tmp->name, name, sizeof(tmp->name)-1);
117         strncpy(tmp->exts, exts, sizeof(tmp->exts)-1);
118         tmp->open = open;
119         tmp->rewrite = rewrite;
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->vstream)
166                 ast_closestream(tmp->vstream);
167         if (tmp->stream) {
168                 ast_closestream(tmp->stream);
169                 if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
170                         ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
171         }
172         return 0;
173 }
174
175 int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
176 {
177         struct ast_frame *trf;
178         int res = -1;
179         int alt=0;
180         if (f->frametype == AST_FRAME_VIDEO) {
181                 if (fs->fmt->format < AST_FORMAT_MAX_AUDIO) {
182                         /* This is the audio portion.  Call the video one... */
183                         if (!fs->vfs && fs->filename) {
184                                 /* XXX Support other video formats XXX */
185                                 char *type = "h263";
186                                 fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode);
187                                 ast_log(LOG_DEBUG, "Opened video output file\n");
188                         }
189                         if (fs->vfs)
190                                 return ast_writestream(fs->vfs, f);
191                         /* Ignore */
192                         return 0;                               
193                 } else {
194                         /* Might / might not have mark set */
195                         alt = 1;
196                 }
197         } else if (f->frametype != AST_FRAME_VOICE) {
198                 ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
199                 return -1;
200         }
201         if (((fs->fmt->format | alt) & f->subclass) == f->subclass) {
202                 res =  fs->fmt->write(fs, f);
203                 if (res < 0) 
204                         ast_log(LOG_WARNING, "Natural write failed\n");
205                 if (res > 0)
206                         ast_log(LOG_WARNING, "Huh??\n");
207                 return res;
208         } else {
209                 /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
210                        the one we've setup a translator for, we do the "wrong thing" XXX */
211                 if (fs->trans && (f->subclass != fs->lastwriteformat)) {
212                         ast_translator_free_path(fs->trans);
213                         fs->trans = NULL;
214                 }
215                 if (!fs->trans) 
216                         fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass);
217                 if (!fs->trans)
218                         ast_log(LOG_WARNING, "Unable to translate to format %s, source format %d\n", fs->fmt->name, f->subclass);
219                 else {
220                         fs->lastwriteformat = f->subclass;
221                         res = 0;
222                         /* Get the translated frame but don't consume the original in case they're using it on another stream */
223                         trf = ast_translate(fs->trans, f, 0);
224                         if (trf) {
225                                 res = fs->fmt->write(fs, trf);
226                                 if (res) 
227                                         ast_log(LOG_WARNING, "Translated frame write failed\n");
228                         } else
229                                 res = 0;
230                 }
231                 return res;
232         }
233 }
234
235 static int copy(char *infile, char *outfile)
236 {
237         int ifd;
238         int ofd;
239         int res;
240         int len;
241         char buf[4096];
242         if ((ifd = open(infile, O_RDONLY)) < 0) {
243                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
244                 return -1;
245         }
246         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
247                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
248                 close(ifd);
249                 return -1;
250         }
251         do {
252                 len = read(ifd, buf, sizeof(buf));
253                 if (len < 0) {
254                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
255                         close(ifd);
256                         close(ofd);
257                         unlink(outfile);
258                 }
259                 if (len) {
260                         res = write(ofd, buf, len);
261                         if (res != len) {
262                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
263                                 close(ifd);
264                                 close(ofd);
265                                 unlink(outfile);
266                         }
267                 }
268         } while(len);
269         close(ifd);
270         close(ofd);
271         return 0;
272 }
273
274 static char *build_filename(char *filename, char *ext)
275 {
276         char *fn;
277         char tmp[AST_CONFIG_MAX_PATH];
278         snprintf(tmp,sizeof(tmp)-1,"%s/%s",(char *)ast_config_AST_VAR_DIR,"sounds");
279         fn = malloc(strlen(tmp) + strlen(filename) + strlen(ext) + 10);
280         if (fn) {
281                 if (filename[0] == '/') 
282                         sprintf(fn, "%s.%s", filename, ext);
283                 else
284                         sprintf(fn, "%s/%s.%s", (char *)tmp, filename, ext);
285         }
286         return fn;
287         
288 }
289
290 #define ACTION_EXISTS 1
291 #define ACTION_DELETE 2
292 #define ACTION_RENAME 3
293 #define ACTION_OPEN   4
294 #define ACTION_COPY   5
295
296 static int ast_filehelper(char *filename, char *filename2, char *fmt, int action)
297 {
298         struct stat st;
299         struct ast_format *f;
300         struct ast_filestream *s;
301         int res=0, ret = 0;
302         char *ext=NULL, *exts, *fn, *nfn;
303         struct ast_channel *chan = (struct ast_channel *)filename2;
304         
305         /* Start with negative response */
306         if (action == ACTION_EXISTS)
307                 res = 0;
308         else
309                 res = -1;
310         if (action == ACTION_OPEN)
311                 ret = -1;
312         /* Check for a specific format */
313         if (ast_pthread_mutex_lock(&formatlock)) {
314                 ast_log(LOG_WARNING, "Unable to lock format list\n");
315                 if (action == ACTION_EXISTS)
316                         return 0;
317                 else
318                         return -1;
319         }
320         f = formats;
321         while(f) {
322                 if (!fmt || !strcasecmp(f->name, fmt)) {
323                         char *stringp=NULL;
324                         exts = strdup(f->exts);
325                         /* Try each kind of extension */
326                         stringp=exts;
327                         ext = strsep(&stringp, "|");
328                         do {
329                                 fn = build_filename(filename, ext);
330                                 if (fn) {
331                                         res = stat(fn, &st);
332                                         if (!res) {
333                                                 switch(action) {
334                                                 case ACTION_EXISTS:
335                                                         ret |= f->format;
336                                                         break;
337                                                 case ACTION_DELETE:
338                                                         res = unlink(fn);
339                                                         if (res)
340                                                                 ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
341                                                         break;
342                                                 case ACTION_RENAME:
343                                                         nfn = build_filename(filename2, ext);
344                                                         if (nfn) {
345                                                                 res = rename(fn, nfn);
346                                                                 if (res)
347                                                                         ast_log(LOG_WARNING, "rename(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
348                                                                 free(nfn);
349                                                         } else
350                                                                 ast_log(LOG_WARNING, "Out of memory\n");
351                                                         break;
352                                                 case ACTION_COPY:
353                                                         nfn = build_filename(filename2, ext);
354                                                         if (nfn) {
355                                                                 res = copy(fn, nfn);
356                                                                 if (res)
357                                                                         ast_log(LOG_WARNING, "copy(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
358                                                                 free(nfn);
359                                                         } else
360                                                                 ast_log(LOG_WARNING, "Out of memory\n");
361                                                         break;
362                                                 case ACTION_OPEN:
363                                                         if ((ret < 0) && ((chan->writeformat & f->format) ||
364                                                                                 ((f->format >= AST_FORMAT_MAX_AUDIO) && fmt))) {
365                                                                 ret = open(fn, O_RDONLY);
366                                                                 if (ret >= 0) {
367                                                                         s = f->open(ret);
368                                                                         if (s) {
369                                                                                 s->lasttimeout = -1;
370                                                                                 s->fmt = f;
371                                                                                 s->trans = NULL;
372                                                                                 s->filename = NULL;
373                                                                                 if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
374                                                                                         chan->stream = s;
375                                                                                 else
376                                                                                         chan->vstream = s;
377                                                                         } else {
378                                                                                 close(ret);
379                                                                                 ast_log(LOG_WARNING, "Unable to open fd on %s\n", filename);
380                                                                         }
381                                                                 } else
382                                                                         ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
383                                                         }
384                                                         break;
385                                                 default:
386                                                         ast_log(LOG_WARNING, "Unknown helper %d\n", action);
387                                                 }
388                                                 /* Conveniently this logic is the same for all */
389                                                 if (res)
390                                                         break;
391                                         }
392                                         free(fn);
393                                 }
394                                 ext = strsep(&stringp, "|");
395                         } while(ext);
396                         free(exts);
397                 }
398                 f = f->next;
399         }
400         ast_pthread_mutex_unlock(&formatlock);
401         if ((action == ACTION_EXISTS) || (action == ACTION_OPEN))
402                 res = ret ? ret : -1;
403         return res;
404 }
405
406 struct ast_filestream *ast_openstream(struct ast_channel *chan, char *filename, char *preflang)
407 {
408         /* This is a fairly complex routine.  Essentially we should do 
409            the following:
410            
411            1) Find which file handlers produce our type of format.
412            2) Look for a filename which it can handle.
413            3) If we find one, then great.  
414            4) If not, see what files are there
415            5) See what we can actually support
416            6) Choose the one with the least costly translator path and
417                set it up.
418                    
419         */
420         int fd = -1;
421         int fmts = -1;
422         char filename2[256];
423         char lang2[MAX_LANGUAGE];
424         int res;
425         ast_stopstream(chan);
426         /* do this first, otherwise we detect the wrong writeformat */
427         if (chan->generator)
428                 ast_deactivate_generator(chan);
429         if (preflang && strlen(preflang)) {
430                 snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
431                 fmts = ast_fileexists(filename2, NULL, NULL);
432                 if (fmts < 1) {
433                         strncpy(lang2, preflang, sizeof(lang2)-1);
434                         snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename);
435                         fmts = ast_fileexists(filename2, NULL, NULL);
436                 }
437         }
438         if (fmts < 1) {
439                 strncpy(filename2, filename, sizeof(filename2)-1);
440                 fmts = ast_fileexists(filename2, NULL, NULL);
441         }
442         if (fmts < 1) {
443                 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
444                 return NULL;
445         }
446         chan->oldwriteformat = chan->writeformat;
447         /* Set the channel to a format we can work with */
448         res = ast_set_write_format(chan, fmts);
449         
450         fd = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
451         if(fd >= 0)
452                 return chan->stream;
453         return NULL;
454 }
455
456 struct ast_filestream *ast_openvstream(struct ast_channel *chan, char *filename, char *preflang)
457 {
458         /* This is a fairly complex routine.  Essentially we should do 
459            the following:
460            
461            1) Find which file handlers produce our type of format.
462            2) Look for a filename which it can handle.
463            3) If we find one, then great.  
464            4) If not, see what files are there
465            5) See what we can actually support
466            6) Choose the one with the least costly translator path and
467                set it up.
468                    
469         */
470         int fd = -1;
471         int fmts = -1;
472         char filename2[256];
473         char lang2[MAX_LANGUAGE];
474         /* XXX H.263 only XXX */
475         char *fmt = "h263";
476         if (preflang && strlen(preflang)) {
477                 snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
478                 fmts = ast_fileexists(filename2, fmt, NULL);
479                 if (fmts < 1) {
480                         strncpy(lang2, preflang, sizeof(lang2)-1);
481                         snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename);
482                         fmts = ast_fileexists(filename2, fmt, NULL);
483                 }
484         }
485         if (fmts < 1) {
486                 strncpy(filename2, filename, sizeof(filename2)-1);
487                 fmts = ast_fileexists(filename2, fmt, NULL);
488         }
489         if (fmts < 1) {
490                 return NULL;
491         }
492         fd = ast_filehelper(filename2, (char *)chan, fmt, ACTION_OPEN);
493         if(fd >= 0)
494                 return chan->vstream;
495         ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename);
496         return NULL;
497 }
498
499 static int ast_readaudio_callback(void *data)
500 {
501         struct ast_filestream *s = data;
502         struct ast_frame *fr;
503         int whennext = 0;
504
505         while(!whennext) {
506                 fr = s->fmt->read(s, &whennext);
507                 if (fr) {
508                         if (ast_write(s->owner, fr)) {
509                                 ast_log(LOG_WARNING, "Failed to write frame\n");
510                                 s->owner->streamid = -1;
511 #ifdef ZAPTEL_OPTIMIZATIONS
512                                 ast_settimeout(s->owner, 0, NULL, NULL);
513 #endif                  
514                                 return 0;
515                         }
516                 } else {
517                         /* Stream has finished */
518                         s->owner->streamid = -1;
519 #ifdef ZAPTEL_OPTIMIZATIONS
520                         ast_settimeout(s->owner, 0, NULL, NULL);
521 #endif                  
522                         return 0;
523                 }
524         }
525         if (whennext != s->lasttimeout) {
526 #ifdef ZAPTEL_OPTIMIZATIONS
527                 ast_settimeout(s->owner, whennext, ast_readaudio_callback, s);
528 #else
529                 s->owner->streamid = ast_sched_add(s->owner->sched, whennext/8, ast_readaudio_callback, s);
530 #endif          
531                 s->lasttimeout = whennext;
532                 return 0;
533         }
534         return 1;
535 }
536
537 static int ast_readvideo_callback(void *data)
538 {
539         struct ast_filestream *s = data;
540         struct ast_frame *fr;
541         int whennext = 0;
542
543         while(!whennext) {
544                 fr = s->fmt->read(s, &whennext);
545                 if (fr) {
546                         if (ast_write(s->owner, fr)) {
547                                 ast_log(LOG_WARNING, "Failed to write frame\n");
548                                 s->owner->vstreamid = -1;
549                                 return 0;
550                         }
551                 } else {
552                         /* Stream has finished */
553                         s->owner->vstreamid = -1;
554                         return 0;
555                 }
556         }
557         if (whennext != s->lasttimeout) {
558                 s->owner->vstreamid = ast_sched_add(s->owner->sched, whennext/8, ast_readvideo_callback, s);
559                 s->lasttimeout = whennext;
560                 return 0;
561         }
562         return 1;
563 }
564
565 int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
566 {
567         s->owner = chan;
568         return 0;
569 }
570
571 int ast_playstream(struct ast_filestream *s)
572 {
573         if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
574                 ast_readaudio_callback(s);
575         else
576                 ast_readvideo_callback(s);
577         return 0;
578 }
579
580 int ast_seekstream(struct ast_filestream *fs, long sample_offset, int whence)
581 {
582         return fs->fmt->seek(fs, sample_offset, whence);
583 }
584
585 int ast_truncstream(struct ast_filestream *fs)
586 {
587         return fs->fmt->trunc(fs);
588 }
589
590 long ast_tellstream(struct ast_filestream *fs)
591 {
592         return fs->fmt->tell(fs);
593 }
594
595 int ast_stream_fastforward(struct ast_filestream *fs, long ms)
596 {
597         /* I think this is right, 8000 samples per second, 1000 ms a second so 8
598          * samples per ms  */
599         long samples = ms * 8;
600         return ast_seekstream(fs, samples, SEEK_CUR);
601 }
602
603 int ast_stream_rewind(struct ast_filestream *fs, long ms)
604 {
605         long samples = ms * 8;
606         samples = samples * -1;
607         return ast_seekstream(fs, samples, SEEK_CUR);
608 }
609
610 int ast_closestream(struct ast_filestream *f)
611 {
612         /* Stop a running stream if there is one */
613         if (f->owner) {
614                 if (f->fmt->format < AST_FORMAT_MAX_AUDIO) {
615                         f->owner->stream = NULL;
616                         if (f->owner->streamid > -1)
617                                 ast_sched_del(f->owner->sched, f->owner->streamid);
618                         f->owner->streamid = -1;
619 #ifdef ZAPTEL_OPTIMIZATIONS
620                         ast_settimeout(f->owner, 0, NULL, NULL);
621 #endif                  
622                 } else {
623                         f->owner->vstream = NULL;
624                         if (f->owner->vstreamid > -1)
625                                 ast_sched_del(f->owner->sched, f->owner->vstreamid);
626                         f->owner->vstreamid = -1;
627                 }
628         }
629         if (f->filename)
630                 free(f->filename);
631         f->filename = NULL;
632         f->fmt->close(f);
633         return 0;
634 }
635
636
637 int ast_fileexists(char *filename, char *fmt, char *preflang)
638 {
639         char filename2[256];
640         char tmp[256];
641         char *postfix;
642         char *prefix;
643         char *c;
644         char lang2[MAX_LANGUAGE];
645         int res = -1;
646         if (preflang && strlen(preflang)) {
647                 /* Insert the language between the last two parts of the path */
648                 strncpy(tmp, filename, sizeof(tmp) - 1);
649                 c = strrchr(tmp, '/');
650                 if (c) {
651                         *c = '\0';
652                         postfix = c+1;
653                         prefix = tmp;
654                 } else {
655                         postfix = tmp;
656                         prefix="";
657                 }
658                 snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, preflang, postfix);
659                 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
660                 if (res < 1) {
661                         char *stringp=NULL;
662                         strncpy(lang2, preflang, sizeof(lang2)-1);
663                         stringp=lang2;
664                         strsep(&stringp, "_");
665                         if (strcmp(lang2, preflang)) {
666                                 snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, lang2, postfix);
667                                 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
668                         }
669                 }
670         }
671         if (res < 1) {
672                 res = ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
673         }
674         return res;
675 }
676
677 int ast_filedelete(char *filename, char *fmt)
678 {
679         return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
680 }
681
682 int ast_filerename(char *filename, char *filename2, char *fmt)
683 {
684         return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
685 }
686
687 int ast_filecopy(char *filename, char *filename2, char *fmt)
688 {
689         return ast_filehelper(filename, filename2, fmt, ACTION_COPY);
690 }
691
692 int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang)
693 {
694         struct ast_filestream *fs;
695         struct ast_filestream *vfs;
696
697         fs = ast_openstream(chan, filename, preflang);
698         vfs = ast_openvstream(chan, filename, preflang);
699         if (vfs)
700                 ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n");
701         else
702                 ast_log(LOG_DEBUG, "Waaah, '%s' has no video stream :(\n", filename);
703         if(fs){
704                 if(ast_applystream(chan, fs))
705                         return -1;
706                 if(vfs && ast_applystream(chan, vfs))
707                         return -1;
708                 if(ast_playstream(fs))
709                         return -1;
710                 if(vfs && ast_playstream(vfs))
711                         return -1;
712 #if 1
713                 if (option_verbose > 2)
714                         ast_verbose(VERBOSE_PREFIX_3 "Playing '%s'\n", filename);
715 #endif
716                 return 0;
717         }
718         ast_log(LOG_WARNING, "Unable to open %s (format %d): %s\n", filename, chan->nativeformats, strerror(errno));
719         return -1;
720 }
721
722
723 struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
724 {
725         int fd,myflags;
726         struct ast_format *f;
727         struct ast_filestream *fs=NULL;
728         char *fn;
729         char *ext;
730         if (ast_pthread_mutex_lock(&formatlock)) {
731                 ast_log(LOG_WARNING, "Unable to lock format list\n");
732                 return NULL;
733         }
734         myflags = 0;
735         /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
736         if (!(flags & O_APPEND)) myflags = O_TRUNC;
737         f = formats;
738         while(f) {
739                 if (!strcasecmp(f->name, type)) {
740                         char *stringp=NULL;
741                         /* XXX Implement check XXX */
742                         ext = strdup(f->exts);
743                         stringp=ext;
744                         ext = strsep(&stringp, "|");
745                         fn = build_filename(filename, ext);
746                         fd = open(fn, flags | myflags | O_WRONLY | O_CREAT, mode);
747                         if (fd >= 0) {
748                                 errno = 0;
749                                 if ((fs = f->rewrite(fd, comment))) {
750                                         fs->trans = NULL;
751                                         fs->fmt = f;
752                                         fs->flags = flags;
753                                         fs->mode = mode;
754                                         fs->filename = strdup(filename);
755                                         fs->vfs = NULL;
756                                 } else {
757                                         ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
758                                         close(fd);
759                                         unlink(fn);
760                                 }
761                         } else if (errno != EEXIST)
762                                 ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
763                         free(fn);
764                         free(ext);
765                         break;
766                 }
767                 f = f->next;
768         }
769         ast_pthread_mutex_unlock(&formatlock);
770         if (!f) 
771                 ast_log(LOG_WARNING, "No such format '%s'\n", type);
772         return fs;
773 }
774
775 char ast_waitstream(struct ast_channel *c, char *breakon)
776 {
777         /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */
778         int res;
779         struct ast_frame *fr;
780         while(c->stream) {
781                 res = ast_sched_wait(c->sched);
782                 if ((res < 0) && !c->timingfunc) {
783                         if (c->stream)
784                                 ast_closestream(c->stream);
785                         if (c->vstream)
786                                 ast_closestream(c->vstream);
787                         break;
788                 }
789                 if (res < 0)
790                         res = 1000;
791                 res = ast_waitfor(c, res);
792                 if (res < 0) {
793                         ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
794                         return res;
795                 } else if (res > 0) {
796                         fr = ast_read(c);
797                         if (!fr) {
798 #if 0
799                                 ast_log(LOG_DEBUG, "Got hung up\n");
800 #endif
801                                 return -1;
802                         }
803                         
804                         switch(fr->frametype) {
805                         case AST_FRAME_DTMF:
806                                 res = fr->subclass;
807                                 if (strchr(breakon, res)) {
808                                         ast_frfree(fr);
809                                         return res;
810                                 }
811                                 break;
812                         case AST_FRAME_CONTROL:
813                                 switch(fr->subclass) {
814                                 case AST_CONTROL_HANGUP:
815                                         ast_frfree(fr);
816                                         return -1;
817                                 case AST_CONTROL_RINGING:
818                                 case AST_CONTROL_ANSWER:
819                                         /* Unimportant */
820                                         break;
821                                 default:
822                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
823                                 }
824                         }
825                         /* Ignore */
826                         ast_frfree(fr);
827                 }
828                 ast_sched_runq(c->sched);
829         }
830         return (c->_softhangup ? -1 : 0);
831 }
832
833 char ast_waitstream_fr(struct ast_channel *c, char *breakon, char *forward, char *rewind, int ms)
834 {
835         int res;
836         struct ast_frame *fr;
837         while(c->stream) {
838                 res = ast_sched_wait(c->sched);
839                 if ((res < 0) && !c->timingfunc) {
840                         if (c->stream)
841                                 ast_closestream(c->stream);
842                         if (c->vstream)
843                                 ast_closestream(c->vstream);
844                         break;
845                 }
846                 if (res < 0)
847                         res = 1000;
848                 res = ast_waitfor(c, res);
849                 if (res < 0) {
850                         ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
851                         return res;
852                 } else
853                 if (res > 0) {
854                         fr = ast_read(c);
855                         if (!fr) {
856 #if 0
857                                 ast_log(LOG_DEBUG, "Got hung up\n");
858 #endif
859                                 return -1;
860                         }
861                         
862                         switch(fr->frametype) {
863                         case AST_FRAME_DTMF:
864                                 res = fr->subclass;
865                                 if (strchr(forward,res)) {
866                                         ast_stream_fastforward(c->stream, ms);
867                                 } else if (strchr(rewind,res)) {
868                                         ast_stream_rewind(c->stream, ms);
869                                 } else if (strchr(breakon, res)) {
870                                         ast_frfree(fr);
871                                         return res;
872                                 }                                       
873                                 break;
874                         case AST_FRAME_CONTROL:
875                                 switch(fr->subclass) {
876                                 case AST_CONTROL_HANGUP:
877                                         ast_frfree(fr);
878                                         return -1;
879                                 case AST_CONTROL_RINGING:
880                                 case AST_CONTROL_ANSWER:
881                                         /* Unimportant */
882                                         break;
883                                 default:
884                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
885                                 }
886                         }
887                         /* Ignore */
888                         ast_frfree(fr);
889                 } else
890                         ast_sched_runq(c->sched);
891         
892                 
893         }
894         return (c->_softhangup ? -1 : 0);
895 }
896
897 char ast_waitstream_full(struct ast_channel *c, char *breakon, int audiofd, int cmdfd)
898 {
899         int res;
900         int ms;
901         int outfd;
902         struct ast_frame *fr;
903         struct ast_channel *rchan;
904         
905         while(c->stream) {
906                 ms = ast_sched_wait(c->sched);
907                 if ((ms < 0) && !c->timingfunc) {
908                         if (c->stream)
909                                 ast_closestream(c->stream);
910                         if (c->vstream)
911                                 ast_closestream(c->vstream);
912                         break;
913                 }
914                 if (ms < 0)
915                         ms = 1000;
916                 rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
917                 if (!rchan && (outfd < 0) && (ms)) {
918                         ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
919                         return -1;
920                 } else if (outfd > -1) {
921                         /* The FD we were watching has something waiting */
922                         return 1;
923                 } else if (rchan) {
924                         fr = ast_read(c);
925                         if (!fr) {
926 #if 0
927                                 ast_log(LOG_DEBUG, "Got hung up\n");
928 #endif
929                                 return -1;
930                         }
931                         
932                         switch(fr->frametype) {
933                         case AST_FRAME_DTMF:
934                                 res = fr->subclass;
935                                 if (strchr(breakon, res)) {
936                                         ast_frfree(fr);
937                                         return res;
938                                 }
939                                 break;
940                         case AST_FRAME_CONTROL:
941                                 switch(fr->subclass) {
942                                 case AST_CONTROL_HANGUP:
943                                         ast_frfree(fr);
944                                         return -1;
945                                 case AST_CONTROL_RINGING:
946                                 case AST_CONTROL_ANSWER:
947                                         /* Unimportant */
948                                         break;
949                                 default:
950                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
951                                 }
952                         case AST_FRAME_VOICE:
953                                 /* Write audio if appropriate */
954                                 if (audiofd > -1)
955                                         write(audiofd, fr->data, fr->datalen);
956                         }
957                         /* Ignore */
958                         ast_frfree(fr);
959                 }
960                 ast_sched_runq(c->sched);
961         
962                 
963         }
964         return (c->_softhangup ? -1 : 0);
965 }