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