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