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