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