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