Version 0.1.3 from FTP
[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 <asterisk/frame.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/sched.h>
19 #include <asterisk/options.h>
20 #include <asterisk/translate.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <pthread.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <dirent.h>
29 #include <sys/stat.h>
30 #include "asterisk.h"
31
32 struct ast_format {
33         /* Name of format */
34         char name[80];
35         /* Extensions (separated by | if more than one) 
36            this format can read.  First is assumed for writing (e.g. .mp3) */
37         char exts[80];
38         /* Format of frames it uses/provides (one only) */
39         int format;
40         /* Open an input stream, and start playback */
41         struct ast_filestream * (*open)(int fd);
42         /* Open an output stream, of a given file descriptor and comment it appropriately if applicable */
43         struct ast_filestream * (*rewrite)(int fd, char *comment);
44         /* Apply a reading filestream to a channel */
45         int (*apply)(struct ast_channel *, struct ast_filestream *);
46         /* Write a frame to a channel */
47         int (*write)(struct ast_filestream *, struct ast_frame *);
48         /* Read the next frame from the filestream (if available) */
49         struct ast_frame * (*read)(struct ast_filestream *);
50         /* Close file, and destroy filestream structure */
51         void (*close)(struct ast_filestream *);
52         /* Retrieve file comment */
53         char * (*getcomment)(struct ast_filestream *);
54         /* Link */
55         struct ast_format *next;
56 };
57
58 struct ast_filestream {
59         /* Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */
60         struct ast_format *fmt;
61         /* Transparently translate from another format -- just once */
62         struct ast_trans_pvt *trans;
63         struct ast_tranlator_pvt *tr;
64 };
65
66 static pthread_mutex_t formatlock = PTHREAD_MUTEX_INITIALIZER;
67
68 static struct ast_format *formats = NULL;
69
70 int ast_format_register(char *name, char *exts, int format,
71                                                 struct ast_filestream * (*open)(int fd),
72                                                 struct ast_filestream * (*rewrite)(int fd, char *comment),
73                                                 int (*apply)(struct ast_channel *, struct ast_filestream *),
74                                                 int (*write)(struct ast_filestream *, struct ast_frame *),
75                                                 struct ast_frame * (*read)(struct ast_filestream *),
76                                                 void (*close)(struct ast_filestream *),
77                                                 char * (*getcomment)(struct ast_filestream *))
78 {
79         struct ast_format *tmp;
80         if (pthread_mutex_lock(&formatlock)) {
81                 ast_log(LOG_WARNING, "Unable to lock format list\n");
82                 return -1;
83         }
84         tmp = formats;
85         while(tmp) {
86                 if (!strcasecmp(name, tmp->name)) {
87                         pthread_mutex_unlock(&formatlock);
88                         ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", name);
89                         return -1;
90                 }
91                 tmp = tmp->next;
92         }
93         tmp = malloc(sizeof(struct ast_format));
94         if (!tmp) {
95                 ast_log(LOG_WARNING, "Out of memory\n");
96                 pthread_mutex_unlock(&formatlock);
97                 return -1;
98         }
99         strncpy(tmp->name, name, sizeof(tmp->name));
100         strncpy(tmp->exts, exts, sizeof(tmp->exts));
101         tmp->open = open;
102         tmp->rewrite = rewrite;
103         tmp->apply = apply;
104         tmp->read = read;
105         tmp->write = write;
106         tmp->close = close;
107         tmp->format = format;
108         tmp->getcomment = getcomment;
109         tmp->next = formats;
110         formats = tmp;
111         pthread_mutex_unlock(&formatlock);
112         if (option_verbose > 1)
113                 ast_verbose( VERBOSE_PREFIX_2 "Registered file format %s, extension(s) %s\n", name, exts);
114         return 0;
115 }
116
117 int ast_format_unregister(char *name)
118 {
119         struct ast_format *tmp, *tmpl = NULL;
120         if (pthread_mutex_lock(&formatlock)) {
121                 ast_log(LOG_WARNING, "Unable to lock format list\n");
122                 return -1;
123         }
124         tmp = formats;
125         while(tmp) {
126                 if (!strcasecmp(name, tmp->name)) {
127                         if (tmpl) 
128                                 tmpl->next = tmp->next;
129                         else
130                                 formats = tmp->next;
131                         free(tmp);
132                         pthread_mutex_unlock(&formatlock);
133                         if (option_verbose > 1)
134                                 ast_verbose( VERBOSE_PREFIX_2 "Unregistered format %s\n", name);
135                         return 0;
136                 }
137                 tmp = tmp->next;
138         }
139         ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name);
140         return -1;
141 }
142
143 int ast_stopstream(struct ast_channel *tmp)
144 {
145         if (tmp->trans)
146                 tmp = tmp->trans;
147         /* Stop a running stream if there is one */
148         if (!tmp->stream) 
149                 return 0;
150         tmp->stream->fmt->close(tmp->stream);
151         if (tmp->master) {
152                 ast_translator_destroy(tmp);
153         }
154         return 0;
155 }
156
157 int ast_closestream(struct ast_filestream *f)
158 {
159         if (f->trans) {
160                 ast_translator_free_path(f->trans);
161         }
162         /* Stop a running stream if there is one */
163         f->fmt->close(f);
164         return 0;
165 }
166
167 int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
168 {
169         struct ast_frame_chain *fc, *f2;
170         int res = -1;
171         if (f->frametype != AST_FRAME_VOICE) {
172                 ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
173                 return -1;
174         }
175         if ((fs->fmt->format & f->subclass) == f->subclass) {
176                 res =  fs->fmt->write(fs, f);
177                 if (res < 0) 
178                         ast_log(LOG_WARNING, "Natural write failed\n");
179                 if (res > 0)
180                         ast_log(LOG_WARNING, "Huh??\n");
181                 return res;
182         } else {
183                 /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
184                        the one we've setup a translator for, we do the "wrong thing" XXX */
185                 if (!fs->trans) 
186                         fs->trans = ast_translator_build_path(f->subclass, fs->fmt->format);
187                 if (!fs->trans)
188                         ast_log(LOG_WARNING, "Unable to translate to format %s, source format %d\n", fs->fmt->name, f->subclass);
189                 else {
190                         res = 0;
191                         /* Build a chain of translated frames */
192                         fc = ast_translate(fs->trans, f);
193                         f2 = fc;
194                         while(f2) {
195                                 res = fs->fmt->write(fs, f2->fr);
196                                 if (res) {
197                                         ast_log(LOG_WARNING, "Translated frame write failed\n");
198                                         break;
199                                 }
200                                 f2 = f2->next;
201                         }
202                         if (fc)
203                                 ast_frchain(fc);
204                 }
205                 return res;
206         }
207 }
208
209 static char *build_filename(char *filename, char *ext)
210 {
211         char *fn;
212         fn = malloc(strlen(AST_SOUNDS) + strlen(filename) + strlen(ext) + 10);
213         if (fn) {
214                 if (filename[0] == '/') 
215                         sprintf(fn, "%s.%s", filename, ext);
216                 else
217                         sprintf(fn, "%s/%s.%s", AST_SOUNDS, filename, ext);
218         }
219         return fn;
220         
221 }
222
223 #define ACTION_EXISTS 1
224 #define ACTION_DELETE 2
225 #define ACTION_RENAME 3
226 #define ACTION_OPEN   4
227
228 static int ast_filehelper(char *filename, char *filename2, char *fmt, int action)
229 {
230         struct stat st;
231         struct ast_format *f;
232         struct ast_filestream *s;
233         int res=0, ret = 0;
234         char *ext=NULL, *exts, *fn, *nfn;
235         struct ast_channel *trans = (struct ast_channel *)filename2;
236         
237         /* Start with negative response */
238         if (action == ACTION_EXISTS)
239                 res = 0;
240         else
241                 res = -1;
242         if (action == ACTION_OPEN)
243                 ret = -1;
244         /* Check for a specific format */
245         if (pthread_mutex_lock(&formatlock)) {
246                 ast_log(LOG_WARNING, "Unable to lock format list\n");
247                 if (action == ACTION_EXISTS)
248                         return 0;
249                 else
250                         return -1;
251         }
252         f = formats;
253         while(f) {
254                 if (!fmt || !strcasecmp(f->name, fmt)) {
255                         exts = strdup(f->exts);
256                         /* Try each kind of extension */
257                         ext = strtok(exts, "|");
258                         do {
259                                 fn = build_filename(filename, ext);
260                                 if (fn) {
261                                         res = stat(fn, &st);
262                                         if (!res) {
263                                                 switch(action) {
264                                                 case ACTION_EXISTS:
265                                                         ret |= f->format;
266                                                         break;
267                                                 case ACTION_DELETE:
268                                                         res = unlink(fn);
269                                                         if (res)
270                                                                 ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
271                                                         break;
272                                                 case ACTION_RENAME:
273                                                         nfn = build_filename(filename2, ext);
274                                                         if (nfn) {
275                                                                 res = rename(fn, nfn);
276                                                                 if (res)
277                                                                         ast_log(LOG_WARNING, "rename(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
278                                                                 free(nfn);
279                                                         } else
280                                                                 ast_log(LOG_WARNING, "Out of memory\n");
281                                                         break;
282                                                 case ACTION_OPEN:
283                                                         if ((ret < 0) && ((trans->format & f->format) /* == trans->format */)) {
284                                                                 ret = open(fn, O_RDONLY);
285                                                                 if (ret >= 0) {
286                                                                         s = f->open(ret);
287                                                                         if (s) {
288                                                                                 s->fmt = f;
289                                                                                 s->trans = NULL;
290                                                                                 trans->stream = s;
291                                                                                 if (f->apply(trans, s)) {
292                                                                                         f->close(s);
293                                                                                         trans->stream = NULL;
294                                                                                         ast_log(LOG_WARNING, "Unable to apply stream to channel %s\n", trans->name);
295                                                                                         close(ret);
296                                                                                         ret = 0;
297                                                                                 }
298                                                                         } else {
299                                                                                 close(ret);
300                                                                                 ast_log(LOG_WARNING, "Unable to open fd on %s\n", filename);
301                                                                         }
302                                                                 } else
303                                                                         ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
304                                                         }
305                                                         break;
306                                                 default:
307                                                         ast_log(LOG_WARNING, "Unknown helper %d\n", action);
308                                                 }
309                                                 /* Conveniently this logic is the same for all */
310                                                 if (res)
311                                                         break;
312                                         }
313                                         free(fn);
314                                 }
315                                 ext = strtok(NULL, "|");
316                         } while(ext);
317                         free(exts);
318                 }
319                 f = f->next;
320         }
321         pthread_mutex_unlock(&formatlock);
322         if ((action == ACTION_EXISTS) || (action == ACTION_OPEN))
323                 res = ret ? ret : -1;
324         return res;
325 }
326
327 int ast_fileexists(char *filename, char *fmt, char *preflang)
328 {
329         char filename2[256];
330         char lang2[MAX_LANGUAGE];
331         int res = -1;
332         if (preflang && strlen(preflang)) {
333                 snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
334                 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
335                 if (res < 1) {
336                         strncpy(lang2, preflang, sizeof(lang2));
337                         strtok(lang2, "_");
338                         if (strcmp(lang2, preflang)) {
339                                 snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
340                                 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
341                         }
342                 }
343         }
344         if (res < 1) {
345                 res = ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
346         }
347         return res;
348 }
349
350 int ast_filedelete(char *filename, char *fmt)
351 {
352         return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
353 }
354
355 int ast_filerename(char *filename, char *filename2, char *fmt)
356 {
357         return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
358 }
359
360 int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang)
361 {
362         /* This is a fairly complex routine.  Essentially we should do 
363            the following:
364            
365            1) Find which file handlers produce our type of format.
366            2) Look for a filename which it can handle.
367            3) If we find one, then great.  
368            4) If not, see what files are there
369            5) See what we can actually support
370            6) Choose the one with the least costly translator path and
371                set it up.
372                    
373         */
374         int fd = -1;
375         struct ast_channel *trans;
376         int fmts = -1;
377         char filename2[256];
378         char lang2[MAX_LANGUAGE];
379         ast_stopstream(chan);
380         if (preflang && strlen(preflang)) {
381                 snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
382                 fmts = ast_fileexists(filename2, NULL, NULL);
383                 if (fmts < 1) {
384                         strncpy(lang2, preflang, sizeof(lang2));
385                         snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
386                         fmts = ast_fileexists(filename2, NULL, NULL);
387                 }
388         }
389         if (fmts < 1) {
390                 strncpy(filename2, filename, sizeof(filename2));
391                 fmts = ast_fileexists(filename2, NULL, NULL);
392         }
393         if (fmts < 1) {
394                 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
395                 return -1;
396         }
397         if (fmts & chan->format) {
398                 /* No translation necessary -- we have a file in a format our channel can 
399                    handle */
400                 trans = chan;
401         } else {
402                 /* Find the best */
403                 fmts = ast_translator_best_choice(chan->format, fmts);
404                 if (fmts < 1) {
405                         ast_log(LOG_WARNING, "Unable to find a translator method\n");
406                         return -1;
407                 }
408                 trans = ast_translator_create(chan, fmts, AST_DIRECTION_OUT);
409                 if (!trans) {
410                         ast_log(LOG_WARNING, "Unable to create translator\n");
411                         return -1;
412                 }
413         }
414         fd = ast_filehelper(filename2, (char *)trans, NULL, ACTION_OPEN);
415         if (fd >= 0) {
416 #if 1
417                 if (option_verbose > 2)
418                         ast_verbose(VERBOSE_PREFIX_3 "Playing '%s'\n", filename2);
419 #endif
420                 return 0;
421         }
422         ast_log(LOG_WARNING, "Unable to open %s (format %d): %s\n", filename, chan->format, strerror(errno));
423         if (chan != trans)
424                 ast_translator_destroy(trans);  
425         return -1;
426 }
427
428
429 struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
430 {
431         int fd;
432         struct ast_format *f;
433         struct ast_filestream *fs=NULL;
434         char *fn;
435         char *ext;
436         if (pthread_mutex_lock(&formatlock)) {
437                 ast_log(LOG_WARNING, "Unable to lock format list\n");
438                 return NULL;
439         }
440         f = formats;
441         while(f) {
442                 if (!strcasecmp(f->name, type)) {
443                         /* XXX Implement check XXX */
444                         ext = strdup(f->exts);
445                         ext = strtok(ext, "|");
446                         fn = build_filename(filename, ext);
447                         fd = open(fn, flags | O_WRONLY | O_CREAT, mode);
448                         if (fd >= 0) {
449                                 errno = 0;
450                                 if ((fs = f->rewrite(fd, comment))) {
451                                         fs->trans = NULL;
452                                         fs->fmt = f;
453                                 } else {
454                                         ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
455                                         close(fd);
456                                         unlink(fn);
457                                 }
458                         } else if (errno != EEXIST)
459                                 ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
460                         free(fn);
461                         free(ext);
462                         break;
463                 }
464                 f = f->next;
465         }
466         pthread_mutex_unlock(&formatlock);
467         if (!f) 
468                 ast_log(LOG_WARNING, "No such format '%s'\n", type);
469         return fs;
470 }
471
472 char ast_waitstream(struct ast_channel *c, char *breakon)
473 {
474         int res;
475         struct ast_frame *fr;
476         if (c->trans)
477                 c=c->trans;
478         while(c->stream) {
479                 res = ast_sched_wait(c->sched);
480                 if (res < 0) {
481                         /* Okay, stop :) */
482                         return 0;
483                 }
484                 res = ast_waitfor(c, res);
485                 if (res < 0) {
486                         ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
487                         return res;
488                 } else
489                 if (res > 0) {
490                         fr = ast_read(c);
491                         if (!fr) {
492 #if 0
493                                 ast_log(LOG_DEBUG, "Got hung up\n");
494 #endif
495                                 return -1;
496                         }
497                         
498                         switch(fr->frametype) {
499                         case AST_FRAME_DTMF:
500                                 res = fr->subclass;
501                                 ast_frfree(fr);
502                                 if (strchr(breakon, res))
503                                         return res;
504                                 break;
505                         case AST_FRAME_CONTROL:
506                                 switch(fr->subclass) {
507                                 case AST_CONTROL_HANGUP:
508                                         ast_frfree(fr);
509                                         return -1;
510                                 default:
511                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
512                                 }
513                         default:
514                                 /* Ignore */
515                                 ast_frfree(fr);
516                         }
517                 } else
518                         ast_sched_runq(c->sched);
519         
520                 
521         }
522         return 0;
523 }
524