Version 0.1.10 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 (ast_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                         ast_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                 ast_pthread_mutex_unlock(&formatlock);
97                 return -1;
98         }
99         strncpy(tmp->name, name, sizeof(tmp->name)-1);
100         strncpy(tmp->exts, exts, sizeof(tmp->exts)-1);
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         ast_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 (ast_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                         ast_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 (tmp->oldwriteformat && 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 int copy(char *infile, char *outfile)
196 {
197         int ifd;
198         int ofd;
199         int res;
200         int len;
201         char buf[4096];
202         if ((ifd = open(infile, O_RDONLY)) < 0) {
203                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
204                 return -1;
205         }
206         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
207                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
208                 close(ifd);
209                 return -1;
210         }
211         do {
212                 len = read(ifd, buf, sizeof(buf));
213                 if (len < 0) {
214                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
215                         close(ifd);
216                         close(ofd);
217                         unlink(outfile);
218                 }
219                 if (len) {
220                         res = write(ofd, buf, len);
221                         if (res != len) {
222                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
223                                 close(ifd);
224                                 close(ofd);
225                                 unlink(outfile);
226                         }
227                 }
228         } while(len);
229         close(ifd);
230         close(ofd);
231         return 0;
232 }
233
234 static char *build_filename(char *filename, char *ext)
235 {
236         char *fn;
237         fn = malloc(strlen(AST_SOUNDS) + strlen(filename) + strlen(ext) + 10);
238         if (fn) {
239                 if (filename[0] == '/') 
240                         sprintf(fn, "%s.%s", filename, ext);
241                 else
242                         sprintf(fn, "%s/%s.%s", AST_SOUNDS, filename, ext);
243         }
244         return fn;
245         
246 }
247
248 #define ACTION_EXISTS 1
249 #define ACTION_DELETE 2
250 #define ACTION_RENAME 3
251 #define ACTION_OPEN   4
252 #define ACTION_COPY   5
253
254 static int ast_filehelper(char *filename, char *filename2, char *fmt, int action)
255 {
256         struct stat st;
257         struct ast_format *f;
258         struct ast_filestream *s;
259         int res=0, ret = 0;
260         char *ext=NULL, *exts, *fn, *nfn;
261         struct ast_channel *chan = (struct ast_channel *)filename2;
262         
263         /* Start with negative response */
264         if (action == ACTION_EXISTS)
265                 res = 0;
266         else
267                 res = -1;
268         if (action == ACTION_OPEN)
269                 ret = -1;
270         /* Check for a specific format */
271         if (ast_pthread_mutex_lock(&formatlock)) {
272                 ast_log(LOG_WARNING, "Unable to lock format list\n");
273                 if (action == ACTION_EXISTS)
274                         return 0;
275                 else
276                         return -1;
277         }
278         f = formats;
279         while(f) {
280                 if (!fmt || !strcasecmp(f->name, fmt)) {
281                         exts = strdup(f->exts);
282                         /* Try each kind of extension */
283                         ext = strtok(exts, "|");
284                         do {
285                                 fn = build_filename(filename, ext);
286                                 if (fn) {
287                                         res = stat(fn, &st);
288                                         if (!res) {
289                                                 switch(action) {
290                                                 case ACTION_EXISTS:
291                                                         ret |= f->format;
292                                                         break;
293                                                 case ACTION_DELETE:
294                                                         res = unlink(fn);
295                                                         if (res)
296                                                                 ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
297                                                         break;
298                                                 case ACTION_RENAME:
299                                                         nfn = build_filename(filename2, ext);
300                                                         if (nfn) {
301                                                                 res = rename(fn, nfn);
302                                                                 if (res)
303                                                                         ast_log(LOG_WARNING, "rename(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
304                                                                 free(nfn);
305                                                         } else
306                                                                 ast_log(LOG_WARNING, "Out of memory\n");
307                                                         break;
308                                                 case ACTION_COPY:
309                                                         nfn = build_filename(filename2, ext);
310                                                         if (nfn) {
311                                                                 res = copy(fn, nfn);
312                                                                 if (res)
313                                                                         ast_log(LOG_WARNING, "copy(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
314                                                                 free(nfn);
315                                                         } else
316                                                                 ast_log(LOG_WARNING, "Out of memory\n");
317                                                         break;
318                                                 case ACTION_OPEN:
319                                                         if ((ret < 0) && ((chan->writeformat & f->format))) {
320                                                                 ret = open(fn, O_RDONLY);
321                                                                 if (ret >= 0) {
322                                                                         s = f->open(ret);
323                                                                         if (s) {
324                                                                                 s->fmt = f;
325                                                                                 s->trans = NULL;
326                                                                                 chan->stream = s;
327                                                                                 if (f->apply(chan, s)) {
328                                                                                         f->close(s);
329                                                                                         chan->stream = NULL;
330                                                                                         ast_log(LOG_WARNING, "Unable to apply stream to channel %s\n", chan->name);
331                                                                                         close(ret);
332                                                                                         ret = 0;
333                                                                                 }
334                                                                         } else {
335                                                                                 close(ret);
336                                                                                 ast_log(LOG_WARNING, "Unable to open fd on %s\n", filename);
337                                                                         }
338                                                                 } else
339                                                                         ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
340                                                         }
341                                                         break;
342                                                 default:
343                                                         ast_log(LOG_WARNING, "Unknown helper %d\n", action);
344                                                 }
345                                                 /* Conveniently this logic is the same for all */
346                                                 if (res)
347                                                         break;
348                                         }
349                                         free(fn);
350                                 }
351                                 ext = strtok(NULL, "|");
352                         } while(ext);
353                         free(exts);
354                 }
355                 f = f->next;
356         }
357         ast_pthread_mutex_unlock(&formatlock);
358         if ((action == ACTION_EXISTS) || (action == ACTION_OPEN))
359                 res = ret ? ret : -1;
360         return res;
361 }
362
363 int ast_fileexists(char *filename, char *fmt, char *preflang)
364 {
365         char filename2[256];
366         char lang2[MAX_LANGUAGE];
367         int res = -1;
368         if (preflang && strlen(preflang)) {
369                 snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
370                 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
371                 if (res < 1) {
372                         strncpy(lang2, preflang, sizeof(lang2)-1);
373                         strtok(lang2, "_");
374                         if (strcmp(lang2, preflang)) {
375                                 snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
376                                 res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
377                         }
378                 }
379         }
380         if (res < 1) {
381                 res = ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
382         }
383         return res;
384 }
385
386 int ast_filedelete(char *filename, char *fmt)
387 {
388         return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
389 }
390
391 int ast_filerename(char *filename, char *filename2, char *fmt)
392 {
393         return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
394 }
395
396 int ast_filecopy(char *filename, char *filename2, char *fmt)
397 {
398         return ast_filehelper(filename, filename2, fmt, ACTION_COPY);
399 }
400
401 int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang)
402 {
403         /* This is a fairly complex routine.  Essentially we should do 
404            the following:
405            
406            1) Find which file handlers produce our type of format.
407            2) Look for a filename which it can handle.
408            3) If we find one, then great.  
409            4) If not, see what files are there
410            5) See what we can actually support
411            6) Choose the one with the least costly translator path and
412                set it up.
413                    
414         */
415         int fd = -1;
416         int fmts = -1;
417         char filename2[256];
418         char lang2[MAX_LANGUAGE];
419         int res;
420         ast_stopstream(chan);
421         if (preflang && strlen(preflang)) {
422                 snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
423                 fmts = ast_fileexists(filename2, NULL, NULL);
424                 if (fmts < 1) {
425                         strncpy(lang2, preflang, sizeof(lang2)-1);
426                         snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
427                         fmts = ast_fileexists(filename2, NULL, NULL);
428                 }
429         }
430         if (fmts < 1) {
431                 strncpy(filename2, filename, sizeof(filename2)-1);
432                 fmts = ast_fileexists(filename2, NULL, NULL);
433         }
434         if (fmts < 1) {
435                 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
436                 return -1;
437         }
438         chan->oldwriteformat = chan->writeformat;
439         /* Set the channel to a format we can work with */
440         res = ast_set_write_format(chan, fmts);
441         
442         fd = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
443         if (fd >= 0) {
444 #if 1
445                 if (option_verbose > 2)
446                         ast_verbose(VERBOSE_PREFIX_3 "Playing '%s'\n", filename2);
447 #endif
448                 return 0;
449         }
450         ast_log(LOG_WARNING, "Unable to open %s (format %d): %s\n", filename, chan->nativeformats, strerror(errno));
451         return -1;
452 }
453
454
455 struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
456 {
457         int fd;
458         struct ast_format *f;
459         struct ast_filestream *fs=NULL;
460         char *fn;
461         char *ext;
462         if (ast_pthread_mutex_lock(&formatlock)) {
463                 ast_log(LOG_WARNING, "Unable to lock format list\n");
464                 return NULL;
465         }
466         f = formats;
467         while(f) {
468                 if (!strcasecmp(f->name, type)) {
469                         /* XXX Implement check XXX */
470                         ext = strdup(f->exts);
471                         ext = strtok(ext, "|");
472                         fn = build_filename(filename, ext);
473                         fd = open(fn, flags | O_WRONLY | O_CREAT, mode);
474                         if (fd >= 0) {
475                                 errno = 0;
476                                 if ((fs = f->rewrite(fd, comment))) {
477                                         fs->trans = NULL;
478                                         fs->fmt = f;
479                                 } else {
480                                         ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
481                                         close(fd);
482                                         unlink(fn);
483                                 }
484                         } else if (errno != EEXIST)
485                                 ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
486                         free(fn);
487                         free(ext);
488                         break;
489                 }
490                 f = f->next;
491         }
492         ast_pthread_mutex_unlock(&formatlock);
493         if (!f) 
494                 ast_log(LOG_WARNING, "No such format '%s'\n", type);
495         return fs;
496 }
497
498 char ast_waitstream(struct ast_channel *c, char *breakon)
499 {
500         int res;
501         struct ast_frame *fr;
502         while(c->stream) {
503                 res = ast_sched_wait(c->sched);
504                 if (res < 0) {
505                         ast_closestream(c->stream);
506                         return 0;
507                 }
508                 res = ast_waitfor(c, res);
509                 if (res < 0) {
510                         ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
511                         return res;
512                 } else
513                 if (res > 0) {
514                         fr = ast_read(c);
515                         if (!fr) {
516 #if 0
517                                 ast_log(LOG_DEBUG, "Got hung up\n");
518 #endif
519                                 return -1;
520                         }
521                         
522                         switch(fr->frametype) {
523                         case AST_FRAME_DTMF:
524                                 res = fr->subclass;
525                                 ast_frfree(fr);
526                                 if (strchr(breakon, res))
527                                         return res;
528                                 break;
529                         case AST_FRAME_CONTROL:
530                                 switch(fr->subclass) {
531                                 case AST_CONTROL_HANGUP:
532                                         ast_frfree(fr);
533                                         return -1;
534                                 case AST_CONTROL_RINGING:
535                                 case AST_CONTROL_ANSWER:
536                                         /* Unimportant */
537                                         break;
538                                 default:
539                                         ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
540                                 }
541                         default:
542                                 /* Ignore */
543                                 ast_frfree(fr);
544                         }
545                 } else
546                         ast_sched_runq(c->sched);
547         
548                 
549         }
550         return 0;
551 }
552