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