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