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