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