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