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