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