be82f9152cecf8c9d53495fbd54aca1efe6cc8aa
[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                 return fs->fmt->write(fs, f);
177         else {
178                 /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
179                        the one we've setup a translator for, we do the "wrong thing" XXX */
180                 if (!fs->trans) 
181                         fs->trans = ast_translator_build_path(f->subclass, fs->fmt->format);
182                 if (!fs->trans)
183                         ast_log(LOG_WARNING, "Unable to translate to format %s, source format %d\n", fs->fmt->name, f->subclass);
184                 else {
185                         /* Build a chain of translated frames */
186                         fc = ast_translate(fs->trans, f);
187                         f2 = fc;
188                         while(f2) {
189                                 res = fs->fmt->write(fs, f2->fr);
190                                 if (res) {
191                                         ast_log(LOG_WARNING, "Frame write failed\n");
192                                         break;
193                                 }
194                                 f2 = f2->next;
195                         }
196                         if (fc)
197                                 ast_frchain(fc);
198                 }
199                 return res;
200         }
201 }
202
203 static char *build_filename(char *filename, char *ext)
204 {
205         char *fn;
206         fn = malloc(strlen(AST_SOUNDS) + strlen(filename) + strlen(ext) + 10);
207         if (fn) {
208                 if (filename[0] == '/') 
209                         sprintf(fn, "%s.%s", filename, ext);
210                 else
211                         sprintf(fn, "%s/%s.%s", AST_SOUNDS, filename, ext);
212         }
213         return fn;
214         
215 }
216
217 #define ACTION_EXISTS 1
218 #define ACTION_DELETE 2
219 #define ACTION_RENAME 3
220 #define ACTION_OPEN   4
221
222 static int ast_filehelper(char *filename, char *filename2, char *fmt, int action)
223 {
224         struct stat st;
225         struct ast_format *f;
226         struct ast_filestream *s;
227         int res=0, ret = 0;
228         char *ext=NULL, *exts, *fn, *nfn;
229         struct ast_channel *trans = (struct ast_channel *)filename2;
230         
231         /* Start with negative response */
232         if (action == ACTION_EXISTS)
233                 res = 0;
234         else
235                 res = -1;
236         if (action == ACTION_OPEN)
237                 ret = -1;
238         /* Check for a specific format */
239         if (pthread_mutex_lock(&formatlock)) {
240                 ast_log(LOG_WARNING, "Unable to lock format list\n");
241                 if (action == ACTION_EXISTS)
242                         return 0;
243                 else
244                         return -1;
245         }
246         f = formats;
247         while(f) {
248                 if (!fmt || !strcasecmp(f->name, fmt)) {
249                         exts = strdup(f->exts);
250                         /* Try each kind of extension */
251                         ext = strtok(exts, "|");
252                         do {
253                                 fn = build_filename(filename, ext);
254                                 if (fn) {
255                                         res = stat(fn, &st);
256                                         if (!res) {
257                                                 switch(action) {
258                                                 case ACTION_EXISTS:
259                                                         ret |= f->format;
260                                                         break;
261                                                 case ACTION_DELETE:
262                                                         res = unlink(fn);
263                                                         if (res)
264                                                                 ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
265                                                         break;
266                                                 case ACTION_RENAME:
267                                                         nfn = build_filename(filename2, ext);
268                                                         if (nfn) {
269                                                                 res = rename(fn, nfn);
270                                                                 if (res)
271                                                                         ast_log(LOG_WARNING, "rename(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
272                                                                 free(nfn);
273                                                         } else
274                                                                 ast_log(LOG_WARNING, "Out of memory\n");
275                                                         break;
276                                                 case ACTION_OPEN:
277                                                         if ((ret < 0) && ((trans->format & f->format) /* == trans->format */)) {
278                                                                 ret = open(fn, O_RDONLY);
279                                                                 if (ret >= 0) {
280                                                                         s = f->open(ret);
281                                                                         if (s) {
282                                                                                 s->fmt = f;
283                                                                                 s->trans = NULL;
284                                                                                 trans->stream = s;
285                                                                                 if (f->apply(trans, s)) {
286                                                                                         f->close(s);
287                                                                                         trans->stream = NULL;
288                                                                                         ast_log(LOG_WARNING, "Unable to apply stream to channel %s\n", trans->name);
289                                                                                         close(ret);
290                                                                                         ret = 0;
291                                                                                 }
292                                                                         } else {
293                                                                                 close(ret);
294                                                                                 ast_log(LOG_WARNING, "Unable to open fd on %s\n", filename);
295                                                                         }
296                                                                 } else
297                                                                         ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
298                                                         }
299                                                         break;
300                                                 default:
301                                                         ast_log(LOG_WARNING, "Unknown helper %d\n", action);
302                                                 }
303                                                 /* Conveniently this logic is the same for all */
304                                                 if (res)
305                                                         break;
306                                         }
307                                         free(fn);
308                                 }
309                                 ext = strtok(NULL, "|");
310                         } while(ext);
311                         free(exts);
312                 }
313                 f = f->next;
314         }
315         pthread_mutex_unlock(&formatlock);
316         if ((action == ACTION_EXISTS) || (action == ACTION_OPEN))
317                 res = ret ? ret : -1;
318         return res;
319 }
320
321 int ast_fileexists(char *filename, char *fmt)
322 {
323         return ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
324 }
325
326 int ast_filedelete(char *filename, char *fmt)
327 {
328         return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
329 }
330
331 int ast_filerename(char *filename, char *filename2, char *fmt)
332 {
333         return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
334 }
335
336 int ast_streamfile(struct ast_channel *chan, char *filename)
337 {
338         /* This is a fairly complex routine.  Essentially we should do 
339            the following:
340            
341            1) Find which file handlers produce our type of format.
342            2) Look for a filename which it can handle.
343            3) If we find one, then great.  
344            *4) If not, see what files are there
345            *5) See what we can actually support
346            *6) Choose the one with the least costly translator path and
347                set it up.
348                    
349                 XXX * = unimplemented XXX
350         */
351         int fd = -1;
352         struct ast_channel *trans;
353         int fmts;
354         ast_stopstream(chan);
355         fmts = ast_fileexists(filename, NULL);
356         if (fmts < 1) {
357                 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
358                 return -1;
359         }
360         if (fmts & chan->format) {
361                 /* No translation necessary -- we have a file in a format our channel can 
362                    handle */
363                 trans = chan;
364         } else {
365                 /* Find the best */
366                 fmts = ast_translator_best_choice(chan->format, fmts);
367                 if (fmts < 1) {
368                         ast_log(LOG_WARNING, "Unable to find a translator method\n");
369                         return -1;
370                 }
371                 trans = ast_translator_create(chan, fmts, AST_DIRECTION_OUT);
372                 if (!trans) {
373                         ast_log(LOG_WARNING, "Unable to create translator\n");
374                         return -1;
375                 }
376         }
377         fd = ast_filehelper(filename, (char *)trans, NULL, ACTION_OPEN);
378         if (fd >= 0) {
379 #if 0
380                 ast_verbose(VERBOSE_PREFIX_3 "Playing '%s'\n", filename);
381 #endif
382                 return 0;
383         }
384         ast_log(LOG_WARNING, "Unable to open %s (format %d): %s\n", filename, chan->format, strerror(errno));
385         if (chan != trans)
386                 ast_translator_destroy(trans);  
387         return -1;
388 }
389
390
391 struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
392 {
393         int fd;
394         struct ast_format *f;
395         struct ast_filestream *fs=NULL;
396         char *fn;
397         char *ext;
398         if (pthread_mutex_lock(&formatlock)) {
399                 ast_log(LOG_WARNING, "Unable to lock format list\n");
400                 return NULL;
401         }
402         f = formats;
403         while(f) {
404                 if (!strcasecmp(f->name, type)) {
405                         /* XXX Implement check XXX */
406                         ext = strdup(f->exts);
407                         ext = strtok(ext, "|");
408                         fn = build_filename(filename, ext);
409                         fd = open(fn, flags | O_WRONLY | O_CREAT, mode);
410                         if (fd >= 0) {
411                                 errno = 0;
412                                 if ((fs = f->rewrite(fd, comment))) {
413                                         fs->trans = NULL;
414                                         fs->fmt = f;
415                                 } else {
416                                         ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
417                                         close(fd);
418                                         unlink(fn);
419                                 }
420                         } else if (errno != EEXIST)
421                                 ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
422                         free(fn);
423                         free(ext);
424                         break;
425                 }
426                 f = f->next;
427         }
428         pthread_mutex_unlock(&formatlock);
429         if (!f) 
430                 ast_log(LOG_WARNING, "No such format '%s'\n", type);
431         return fs;
432 }
433
434 char ast_waitstream(struct ast_channel *c, char *breakon)
435 {
436         int res;
437         struct ast_frame *fr;
438         if (c->trans)
439                 c=c->trans;
440         while(c->stream) {
441                 res = ast_sched_wait(c->sched);
442                 if (res < 0) {
443                         /* Okay, stop :) */
444                         return 0;
445                 }
446                 res = ast_waitfor(c, res);
447                 if (res < 0) {
448                         ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
449                         return res;
450                 } else
451                 if (res > 0) {
452                         fr = ast_read(c);
453                         if (!fr) {
454 #if 0
455                                 ast_log(LOG_DEBUG, "Got hung up\n");
456 #endif
457                                 return -1;
458                         }
459                         
460                         switch(fr->frametype) {
461                         case AST_FRAME_DTMF:
462                                 res = fr->subclass;
463                                 ast_frfree(fr);
464                                 if (strchr(breakon, res))
465                                         return res;
466                                 break;
467                         case AST_FRAME_CONTROL:
468                                 switch(fr->subclass) {
469                                 case AST_CONTROL_HANGUP:
470                                         ast_frfree(fr);
471                                         return -1;
472                                 default:
473                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
474                                 }
475                         default:
476                                 /* Ignore */
477                                 ast_frfree(fr);
478                         }
479                 } else
480                         ast_sched_runq(c->sched);
481         
482                 
483         }
484         return 0;
485 }
486