f7e419d45d1f08eb8a88a7ae926883cda1f6f18e
[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)
328 {
329         return ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
330 }
331
332 int ast_filedelete(char *filename, char *fmt)
333 {
334         return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
335 }
336
337 int ast_filerename(char *filename, char *filename2, char *fmt)
338 {
339         return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
340 }
341
342 int ast_streamfile(struct ast_channel *chan, char *filename)
343 {
344         /* This is a fairly complex routine.  Essentially we should do 
345            the following:
346            
347            1) Find which file handlers produce our type of format.
348            2) Look for a filename which it can handle.
349            3) If we find one, then great.  
350            4) If not, see what files are there
351            5) See what we can actually support
352            6) Choose the one with the least costly translator path and
353                set it up.
354                    
355         */
356         int fd = -1;
357         struct ast_channel *trans;
358         int fmts;
359         ast_stopstream(chan);
360         fmts = ast_fileexists(filename, NULL);
361         if (fmts < 1) {
362                 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
363                 return -1;
364         }
365         if (fmts & chan->format) {
366                 /* No translation necessary -- we have a file in a format our channel can 
367                    handle */
368                 trans = chan;
369         } else {
370                 /* Find the best */
371                 fmts = ast_translator_best_choice(chan->format, fmts);
372                 if (fmts < 1) {
373                         ast_log(LOG_WARNING, "Unable to find a translator method\n");
374                         return -1;
375                 }
376                 trans = ast_translator_create(chan, fmts, AST_DIRECTION_OUT);
377                 if (!trans) {
378                         ast_log(LOG_WARNING, "Unable to create translator\n");
379                         return -1;
380                 }
381         }
382         fd = ast_filehelper(filename, (char *)trans, NULL, ACTION_OPEN);
383         if (fd >= 0) {
384 #if 0
385                 ast_verbose(VERBOSE_PREFIX_3 "Playing '%s'\n", filename);
386 #endif
387                 return 0;
388         }
389         ast_log(LOG_WARNING, "Unable to open %s (format %d): %s\n", filename, chan->format, strerror(errno));
390         if (chan != trans)
391                 ast_translator_destroy(trans);  
392         return -1;
393 }
394
395
396 struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
397 {
398         int fd;
399         struct ast_format *f;
400         struct ast_filestream *fs=NULL;
401         char *fn;
402         char *ext;
403         if (pthread_mutex_lock(&formatlock)) {
404                 ast_log(LOG_WARNING, "Unable to lock format list\n");
405                 return NULL;
406         }
407         f = formats;
408         while(f) {
409                 if (!strcasecmp(f->name, type)) {
410                         /* XXX Implement check XXX */
411                         ext = strdup(f->exts);
412                         ext = strtok(ext, "|");
413                         fn = build_filename(filename, ext);
414                         fd = open(fn, flags | O_WRONLY | O_CREAT, mode);
415                         if (fd >= 0) {
416                                 errno = 0;
417                                 if ((fs = f->rewrite(fd, comment))) {
418                                         fs->trans = NULL;
419                                         fs->fmt = f;
420                                 } else {
421                                         ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
422                                         close(fd);
423                                         unlink(fn);
424                                 }
425                         } else if (errno != EEXIST)
426                                 ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
427                         free(fn);
428                         free(ext);
429                         break;
430                 }
431                 f = f->next;
432         }
433         pthread_mutex_unlock(&formatlock);
434         if (!f) 
435                 ast_log(LOG_WARNING, "No such format '%s'\n", type);
436         return fs;
437 }
438
439 char ast_waitstream(struct ast_channel *c, char *breakon)
440 {
441         int res;
442         struct ast_frame *fr;
443         if (c->trans)
444                 c=c->trans;
445         while(c->stream) {
446                 res = ast_sched_wait(c->sched);
447                 if (res < 0) {
448                         /* Okay, stop :) */
449                         return 0;
450                 }
451                 res = ast_waitfor(c, res);
452                 if (res < 0) {
453                         ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
454                         return res;
455                 } else
456                 if (res > 0) {
457                         fr = ast_read(c);
458                         if (!fr) {
459 #if 0
460                                 ast_log(LOG_DEBUG, "Got hung up\n");
461 #endif
462                                 return -1;
463                         }
464                         
465                         switch(fr->frametype) {
466                         case AST_FRAME_DTMF:
467                                 res = fr->subclass;
468                                 ast_frfree(fr);
469                                 if (strchr(breakon, res))
470                                         return res;
471                                 break;
472                         case AST_FRAME_CONTROL:
473                                 switch(fr->subclass) {
474                                 case AST_CONTROL_HANGUP:
475                                         ast_frfree(fr);
476                                         return -1;
477                                 default:
478                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
479                                 }
480                         default:
481                                 /* Ignore */
482                                 ast_frfree(fr);
483                         }
484                 } else
485                         ast_sched_runq(c->sched);
486         
487                 
488         }
489         return 0;
490 }
491