![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
file.c
Go to the documentation of this file.
00001 /* 00002 * Asterisk -- An open source telephony toolkit. 00003 * 00004 * Copyright (C) 1999 - 2006, Digium, Inc. 00005 * 00006 * Mark Spencer <markster@digium.com> 00007 * 00008 * See http://www.asterisk.org for more information about 00009 * the Asterisk project. Please do not directly contact 00010 * any of the maintainers of this project for assistance; 00011 * the project provides a web site, mailing lists and IRC 00012 * channels for your use. 00013 * 00014 * This program is free software, distributed under the terms of 00015 * the GNU General Public License Version 2. See the LICENSE file 00016 * at the top of the source tree. 00017 */ 00018 00019 /*! \file 00020 * 00021 * \brief Generic File Format Support. 00022 * 00023 * \author Mark Spencer <markster@digium.com> 00024 */ 00025 00026 #include "asterisk.h" 00027 00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 52352 $") 00029 00030 #include <sys/types.h> 00031 #include <errno.h> 00032 #include <unistd.h> 00033 #include <stdlib.h> 00034 #include <string.h> 00035 #include <stdio.h> 00036 #include <fcntl.h> 00037 #include <dirent.h> 00038 #include <sys/types.h> 00039 #include <sys/stat.h> 00040 00041 #include "asterisk/frame.h" 00042 #include "asterisk/file.h" 00043 #include "asterisk/cli.h" 00044 #include "asterisk/logger.h" 00045 #include "asterisk/channel.h" 00046 #include "asterisk/sched.h" 00047 #include "asterisk/options.h" 00048 #include "asterisk/translate.h" 00049 #include "asterisk/utils.h" 00050 #include "asterisk/lock.h" 00051 #include "asterisk/app.h" 00052 #include "asterisk/pbx.h" 00053 #include "asterisk/linkedlists.h" 00054 #include "asterisk/module.h" 00055 00056 /* 00057 * The following variable controls the layout of localized sound files. 00058 * If 0, use the historical layout with prefix just before the filename 00059 * (i.e. digits/en/1.gsm , digits/it/1.gsm or default to digits/1.gsm), 00060 * if 1 put the prefix at the beginning of the filename 00061 * (i.e. en/digits/1.gsm, it/digits/1.gsm or default to digits/1.gsm). 00062 * The latter permits a language to be entirely in one directory. 00063 */ 00064 int ast_language_is_prefix = 1; 00065 00066 static AST_RWLIST_HEAD_STATIC(formats, ast_format); 00067 00068 int __ast_format_register(const struct ast_format *f, struct ast_module *mod) 00069 { 00070 struct ast_format *tmp; 00071 00072 AST_RWLIST_WRLOCK(&formats); 00073 AST_RWLIST_TRAVERSE(&formats, tmp, list) { 00074 if (!strcasecmp(f->name, tmp->name)) { 00075 AST_RWLIST_UNLOCK(&formats); 00076 ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", f->name); 00077 return -1; 00078 } 00079 } 00080 if (!(tmp = ast_calloc(1, sizeof(*tmp)))) { 00081 AST_RWLIST_UNLOCK(&formats); 00082 return -1; 00083 } 00084 *tmp = *f; 00085 tmp->module = mod; 00086 if (tmp->buf_size) { 00087 /* 00088 * Align buf_size properly, rounding up to the machine-specific 00089 * alignment for pointers. 00090 */ 00091 struct _test_align { void *a, *b; } p; 00092 int align = (char *)&p.b - (char *)&p.a; 00093 tmp->buf_size = ((f->buf_size + align - 1)/align)*align; 00094 } 00095 00096 memset(&tmp->list, 0, sizeof(tmp->list)); 00097 00098 AST_RWLIST_INSERT_HEAD(&formats, tmp, list); 00099 AST_RWLIST_UNLOCK(&formats); 00100 if (option_verbose > 1) 00101 ast_verbose( VERBOSE_PREFIX_2 "Registered file format %s, extension(s) %s\n", f->name, f->exts); 00102 00103 return 0; 00104 } 00105 00106 int ast_format_unregister(const char *name) 00107 { 00108 struct ast_format *tmp; 00109 int res = -1; 00110 00111 AST_RWLIST_WRLOCK(&formats); 00112 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&formats, tmp, list) { 00113 if (!strcasecmp(name, tmp->name)) { 00114 AST_RWLIST_REMOVE_CURRENT(&formats, list); 00115 free(tmp); 00116 res = 0; 00117 } 00118 } 00119 AST_RWLIST_TRAVERSE_SAFE_END 00120 AST_RWLIST_UNLOCK(&formats); 00121 00122 if (!res) { 00123 if (option_verbose > 1) 00124 ast_verbose( VERBOSE_PREFIX_2 "Unregistered format %s\n", name); 00125 } else 00126 ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name); 00127 00128 return res; 00129 } 00130 00131 int ast_stopstream(struct ast_channel *tmp) 00132 { 00133 /* Stop a running stream if there is one */ 00134 if (tmp->stream) { 00135 ast_closestream(tmp->stream); 00136 tmp->stream = NULL; 00137 if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat)) 00138 ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat); 00139 } 00140 return 0; 00141 } 00142 00143 int ast_writestream(struct ast_filestream *fs, struct ast_frame *f) 00144 { 00145 int res = -1; 00146 int alt = 0; 00147 if (f->frametype == AST_FRAME_VIDEO) { 00148 if (fs->fmt->format < AST_FORMAT_MAX_AUDIO) { 00149 /* This is the audio portion. Call the video one... */ 00150 if (!fs->vfs && fs->filename) { 00151 const char *type = ast_getformatname(f->subclass & ~0x1); 00152 fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode); 00153 if (option_debug) 00154 ast_log(LOG_DEBUG, "Opened video output file\n"); 00155 } 00156 if (fs->vfs) 00157 return ast_writestream(fs->vfs, f); 00158 /* else ignore */ 00159 return 0; 00160 } else { 00161 /* Might / might not have mark set */ 00162 alt = 1; 00163 } 00164 } else if (f->frametype != AST_FRAME_VOICE) { 00165 ast_log(LOG_WARNING, "Tried to write non-voice frame\n"); 00166 return -1; 00167 } 00168 if (((fs->fmt->format | alt) & f->subclass) == f->subclass) { 00169 res = fs->fmt->write(fs, f); 00170 if (res < 0) 00171 ast_log(LOG_WARNING, "Natural write failed\n"); 00172 else if (res > 0) 00173 ast_log(LOG_WARNING, "Huh??\n"); 00174 } else { 00175 /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't 00176 the one we've setup a translator for, we do the "wrong thing" XXX */ 00177 if (fs->trans && f->subclass != fs->lastwriteformat) { 00178 ast_translator_free_path(fs->trans); 00179 fs->trans = NULL; 00180 } 00181 if (!fs->trans) 00182 fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass); 00183 if (!fs->trans) 00184 ast_log(LOG_WARNING, "Unable to translate to format %s, source format %s\n", 00185 fs->fmt->name, ast_getformatname(f->subclass)); 00186 else { 00187 struct ast_frame *trf; 00188 fs->lastwriteformat = f->subclass; 00189 /* Get the translated frame but don't consume the original in case they're using it on another stream */ 00190 trf = ast_translate(fs->trans, f, 0); 00191 if (trf) { 00192 res = fs->fmt->write(fs, trf); 00193 if (res) 00194 ast_log(LOG_WARNING, "Translated frame write failed\n"); 00195 } else 00196 res = 0; 00197 } 00198 } 00199 return res; 00200 } 00201 00202 static int copy(const char *infile, const char *outfile) 00203 { 00204 int ifd, ofd, len; 00205 char buf[4096]; /* XXX make it lerger. */ 00206 00207 if ((ifd = open(infile, O_RDONLY)) < 0) { 00208 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile); 00209 return -1; 00210 } 00211 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, AST_FILE_MODE)) < 0) { 00212 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile); 00213 close(ifd); 00214 return -1; 00215 } 00216 while ( (len = read(ifd, buf, sizeof(buf)) ) ) { 00217 int res; 00218 if (len < 0) { 00219 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno)); 00220 break; 00221 } 00222 /* XXX handle partial writes */ 00223 res = write(ofd, buf, len); 00224 if (res != len) { 00225 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno)); 00226 len = -1; /* error marker */ 00227 break; 00228 } 00229 } 00230 close(ifd); 00231 close(ofd); 00232 if (len < 0) { 00233 unlink(outfile); 00234 return -1; /* error */ 00235 } 00236 return 0; /* success */ 00237 } 00238 00239 /*! 00240 * \brief construct a filename. Absolute pathnames are preserved, 00241 * relative names are prefixed by the sounds/ directory. 00242 * The wav49 suffix is replaced by 'WAV'. 00243 * Returns a malloc'ed string to be freed by the caller. 00244 */ 00245 static char *build_filename(const char *filename, const char *ext) 00246 { 00247 char *fn = NULL; 00248 00249 if (!strcmp(ext, "wav49")) 00250 ext = "WAV"; 00251 00252 if (filename[0] == '/') 00253 asprintf(&fn, "%s.%s", filename, ext); 00254 else 00255 asprintf(&fn, "%s/sounds/%s.%s", 00256 ast_config_AST_DATA_DIR, filename, ext); 00257 return fn; 00258 } 00259 00260 /* compare type against the list 'exts' */ 00261 /* XXX need a better algorithm */ 00262 static int exts_compare(const char *exts, const char *type) 00263 { 00264 char tmp[256]; 00265 char *stringp = tmp, *ext; 00266 00267 ast_copy_string(tmp, exts, sizeof(tmp)); 00268 while ((ext = strsep(&stringp, "|"))) { 00269 if (!strcmp(ext, type)) 00270 return 1; 00271 } 00272 00273 return 0; 00274 } 00275 00276 static struct ast_filestream *get_filestream(struct ast_format *fmt, FILE *bfile) 00277 { 00278 struct ast_filestream *s; 00279 00280 int l = sizeof(*s) + fmt->buf_size + fmt->desc_size; /* total allocation size */ 00281 if ( (s = ast_calloc(1, l)) == NULL) 00282 return NULL; 00283 s->fmt = fmt; 00284 s->f = bfile; 00285 00286 if (fmt->desc_size) 00287 s->private = ((char *)(s+1)) + fmt->buf_size; 00288 if (fmt->buf_size) 00289 s->buf = (char *)(s+1); 00290 s->fr.src = fmt->name; 00291 return s; 00292 } 00293 00294 /* 00295 * Default implementations of open and rewrite. 00296 * Only use them if you don't have expensive stuff to do. 00297 */ 00298 enum wrap_fn { WRAP_OPEN, WRAP_REWRITE }; 00299 00300 static int fn_wrapper(struct ast_filestream *s, const char *comment, enum wrap_fn mode) 00301 { 00302 struct ast_format *f = s->fmt; 00303 int ret = -1; 00304 00305 if (mode == WRAP_OPEN && f->open && f->open(s)) 00306 ast_log(LOG_WARNING, "Unable to open format %s\n", f->name); 00307 else if (mode == WRAP_REWRITE && f->rewrite && f->rewrite(s, comment)) 00308 ast_log(LOG_WARNING, "Unable to rewrite format %s\n", f->name); 00309 else { 00310 /* preliminary checks succeed. update usecount */ 00311 ast_module_ref(f->module); 00312 ret = 0; 00313 } 00314 return ret; 00315 } 00316 00317 static int rewrite_wrapper(struct ast_filestream *s, const char *comment) 00318 { 00319 return fn_wrapper(s, comment, WRAP_REWRITE); 00320 } 00321 00322 static int open_wrapper(struct ast_filestream *s) 00323 { 00324 return fn_wrapper(s, NULL, WRAP_OPEN); 00325 } 00326 00327 enum file_action { 00328 ACTION_EXISTS = 1, /* return matching format if file exists, 0 otherwise */ 00329 ACTION_DELETE, /* delete file, return 0 on success, -1 on error */ 00330 ACTION_RENAME, /* rename file. return 0 on success, -1 on error */ 00331 ACTION_OPEN, 00332 ACTION_COPY /* copy file. return 0 on success, -1 on error */ 00333 }; 00334 00335 /*! 00336 * \brief perform various actions on a file. Second argument 00337 * arg2 depends on the command: 00338 * unused for EXISTS and DELETE 00339 * destination file name (const char *) for COPY and RENAME 00340 * struct ast_channel * for OPEN 00341 * if fmt is NULL, OPEN will return the first matching entry, 00342 * whereas other functions will run on all matching entries. 00343 */ 00344 static int ast_filehelper(const char *filename, const void *arg2, const char *fmt, const enum file_action action) 00345 { 00346 struct ast_format *f; 00347 int res = (action == ACTION_EXISTS) ? 0 : -1; 00348 00349 AST_RWLIST_RDLOCK(&formats); 00350 /* Check for a specific format */ 00351 AST_RWLIST_TRAVERSE(&formats, f, list) { 00352 char *stringp, *ext = NULL; 00353 00354 if (fmt && !exts_compare(f->exts, fmt)) 00355 continue; 00356 00357 /* Look for a file matching the supported extensions. 00358 * The file must exist, and for OPEN, must match 00359 * one of the formats supported by the channel. 00360 */ 00361 stringp = ast_strdupa(f->exts); /* this is in the stack so does not need to be freed */ 00362 while ( (ext = strsep(&stringp, "|")) ) { 00363 struct stat st; 00364 char *fn = build_filename(filename, ext); 00365 00366 if (fn == NULL) 00367 continue; 00368 00369 if ( stat(fn, &st) ) { /* file not existent */ 00370 free(fn); 00371 continue; 00372 } 00373 /* for 'OPEN' we need to be sure that the format matches 00374 * what the channel can process 00375 */ 00376 if (action == ACTION_OPEN) { 00377 struct ast_channel *chan = (struct ast_channel *)arg2; 00378 FILE *bfile; 00379 struct ast_filestream *s; 00380 00381 if ( !(chan->writeformat & f->format) && 00382 !(f->format >= AST_FORMAT_MAX_AUDIO && fmt)) { 00383 free(fn); 00384 continue; /* not a supported format */ 00385 } 00386 if ( (bfile = fopen(fn, "r")) == NULL) { 00387 free(fn); 00388 continue; /* cannot open file */ 00389 } 00390 s = get_filestream(f, bfile); 00391 if (!s) { 00392 fclose(bfile); 00393 free(fn); /* cannot allocate descriptor */ 00394 continue; 00395 } 00396 if (open_wrapper(s)) { 00397 fclose(bfile); 00398 free(fn); 00399 free(s); 00400 continue; /* cannot run open on file */ 00401 } 00402 /* ok this is good for OPEN */ 00403 res = 1; /* found */ 00404 s->lasttimeout = -1; 00405 s->fmt = f; 00406 s->trans = NULL; 00407 s->filename = NULL; 00408 if (s->fmt->format < AST_FORMAT_MAX_AUDIO) 00409 chan->stream = s; 00410 else 00411 chan->vstream = s; 00412 free(fn); 00413 break; 00414 } 00415 switch (action) { 00416 case ACTION_OPEN: 00417 break; /* will never get here */ 00418 00419 case ACTION_EXISTS: /* return the matching format */ 00420 res |= f->format; 00421 break; 00422 00423 case ACTION_DELETE: 00424 if ( (res = unlink(fn)) ) 00425 ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno)); 00426 break; 00427 00428 case ACTION_RENAME: 00429 case ACTION_COPY: { 00430 char *nfn = build_filename((const char *)arg2, ext); 00431 if (!nfn) 00432 ast_log(LOG_WARNING, "Out of memory\n"); 00433 else { 00434 res = action == ACTION_COPY ? copy(fn, nfn) : rename(fn, nfn); 00435 if (res) 00436 ast_log(LOG_WARNING, "%s(%s,%s) failed: %s\n", 00437 action == ACTION_COPY ? "copy" : "rename", 00438 fn, nfn, strerror(errno)); 00439 free(nfn); 00440 } 00441 } 00442 break; 00443 00444 default: 00445 ast_log(LOG_WARNING, "Unknown helper %d\n", action); 00446 } 00447 free(fn); 00448 } 00449 } 00450 AST_RWLIST_UNLOCK(&formats); 00451 return res; 00452 } 00453 00454 /*! 00455 * \brief helper routine to locate a file with a given format 00456 * and language preference. 00457 * Try preflang, preflang with stripped '_' suffix, or NULL. 00458 * In the standard asterisk, language goes just before the last component. 00459 * In an alternative configuration, the language should be a prefix 00460 * to the actual filename. 00461 * 00462 * The last parameter(s) point to a buffer of sufficient size, 00463 * which on success is filled with the matching filename. 00464 */ 00465 static int fileexists_core(const char *filename, const char *fmt, const char *preflang, 00466 char *buf, int buflen) 00467 { 00468 int res = -1; 00469 int langlen; /* length of language string */ 00470 const char *c = strrchr(filename, '/'); 00471 int offset = c ? c - filename + 1 : 0; /* points right after the last '/' */ 00472 00473 if (preflang == NULL) 00474 preflang = ""; 00475 langlen = strlen(preflang); 00476 00477 if (buflen < langlen + strlen(filename) + 2) { 00478 ast_log(LOG_WARNING, "buffer too small\n"); 00479 buf[0] = '\0'; /* set to empty */ 00480 buf = alloca(langlen + strlen(filename) + 2); /* room for everything */ 00481 } 00482 if (buf == NULL) 00483 return 0; 00484 buf[0] = '\0'; 00485 for (;;) { 00486 if (ast_language_is_prefix) { /* new layout */ 00487 if (langlen) { 00488 strcpy(buf, preflang); 00489 buf[langlen] = '/'; 00490 strcpy(buf + langlen + 1, filename); 00491 } else 00492 strcpy(buf, filename); /* first copy the full string */ 00493 } else { /* old layout */ 00494 strcpy(buf, filename); /* first copy the full string */ 00495 if (langlen) { 00496 /* insert the language and suffix if needed */ 00497 strcpy(buf + offset, preflang); 00498 sprintf(buf + offset + langlen, "/%s", filename + offset); 00499 } 00500 } 00501 res = ast_filehelper(buf, NULL, fmt, ACTION_EXISTS); 00502 if (res > 0) /* found format */ 00503 break; 00504 if (langlen == 0) /* no more formats */ 00505 break; 00506 if (preflang[langlen] == '_') /* we are on the local suffix */ 00507 langlen = 0; /* try again with no language */ 00508 else 00509 langlen = (c = strchr(preflang, '_')) ? c - preflang : 0; 00510 } 00511 return res; 00512 } 00513 00514 struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *filename, const char *preflang) 00515 { 00516 return ast_openstream_full(chan, filename, preflang, 0); 00517 } 00518 00519 struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis) 00520 { 00521 /* 00522 * Use fileexists_core() to find a file in a compatible 00523 * language and format, set up a suitable translator, 00524 * and open the stream. 00525 */ 00526 int fmts, res, buflen; 00527 char *buf; 00528 00529 if (!asis) { 00530 /* do this first, otherwise we detect the wrong writeformat */ 00531 ast_stopstream(chan); 00532 if (chan->generator) 00533 ast_deactivate_generator(chan); 00534 } 00535 if (preflang == NULL) 00536 preflang = ""; 00537 buflen = strlen(preflang) + strlen(filename) + 2; 00538 buf = alloca(buflen); 00539 if (buf == NULL) 00540 return NULL; 00541 fmts = fileexists_core(filename, NULL, preflang, buf, buflen); 00542 if (fmts > 0) 00543 fmts &= AST_FORMAT_AUDIO_MASK; 00544 if (fmts < 1) { 00545 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename); 00546 return NULL; 00547 } 00548 chan->oldwriteformat = chan->writeformat; 00549 /* Set the channel to a format we can work with */ 00550 res = ast_set_write_format(chan, fmts); 00551 res = ast_filehelper(buf, chan, NULL, ACTION_OPEN); 00552 if (res >= 0) 00553 return chan->stream; 00554 return NULL; 00555 } 00556 00557 struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *filename, const char *preflang) 00558 { 00559 /* As above, but for video. But here we don't have translators 00560 * so we must enforce a format. 00561 */ 00562 unsigned int format; 00563 char *buf; 00564 int buflen; 00565 00566 if (preflang == NULL) 00567 preflang = ""; 00568 buflen = strlen(preflang) + strlen(filename) + 2; 00569 buf = alloca(buflen); 00570 if (buf == NULL) 00571 return NULL; 00572 00573 for (format = AST_FORMAT_MAX_AUDIO << 1; format <= AST_FORMAT_MAX_VIDEO; format = format << 1) { 00574 int fd; 00575 const char *fmt; 00576 00577 if (!(chan->nativeformats & format)) 00578 continue; 00579 fmt = ast_getformatname(format); 00580 if ( fileexists_core(filename, fmt, preflang, buf, buflen) < 1) /* no valid format */ 00581 continue; 00582 fd = ast_filehelper(buf, chan, fmt, ACTION_OPEN); 00583 if (fd >= 0) 00584 return chan->vstream; 00585 ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename); 00586 } 00587 return NULL; 00588 } 00589 00590 struct ast_frame *ast_readframe(struct ast_filestream *s) 00591 { 00592 struct ast_frame *f = NULL; 00593 int whennext = 0; 00594 if (s && s->fmt) 00595 f = s->fmt->read(s, &whennext); 00596 return f; 00597 } 00598 00599 static int ast_readaudio_callback(void *data) 00600 { 00601 struct ast_filestream *s = data; 00602 int whennext = 0; 00603 00604 while (!whennext) { 00605 struct ast_frame *fr = s->fmt->read(s, &whennext); 00606 if (!fr /* stream complete */ || ast_write(s->owner, fr) /* error writing */) { 00607 if (fr) 00608 ast_log(LOG_WARNING, "Failed to write frame\n"); 00609 s->owner->streamid = -1; 00610 #ifdef HAVE_ZAPTEL 00611 ast_settimeout(s->owner, 0, NULL, NULL); 00612 #endif 00613 return 0; 00614 } 00615 } 00616 if (whennext != s->lasttimeout) { 00617 #ifdef HAVE_ZAPTEL 00618 if (s->owner->timingfd > -1) 00619 ast_settimeout(s->owner, whennext, ast_readaudio_callback, s); 00620 else 00621 #endif 00622 s->owner->streamid = ast_sched_add(s->owner->sched, whennext/8, ast_readaudio_callback, s); 00623 s->lasttimeout = whennext; 00624 return 0; 00625 } 00626 return 1; 00627 } 00628 00629 static int ast_readvideo_callback(void *data) 00630 { 00631 struct ast_filestream *s = data; 00632 int whennext = 0; 00633 00634 while (!whennext) { 00635 struct ast_frame *fr = s->fmt->read(s, &whennext); 00636 if (!fr || ast_write(s->owner, fr)) { /* no stream or error, as above */ 00637 if (fr) 00638 ast_log(LOG_WARNING, "Failed to write frame\n"); 00639 s->owner->vstreamid = -1; 00640 return 0; 00641 } 00642 } 00643 if (whennext != s->lasttimeout) { 00644 s->owner->vstreamid = ast_sched_add(s->owner->sched, whennext/8, ast_readvideo_callback, s); 00645 s->lasttimeout = whennext; 00646 return 0; 00647 } 00648 return 1; 00649 } 00650 00651 int ast_applystream(struct ast_channel *chan, struct ast_filestream *s) 00652 { 00653 s->owner = chan; 00654 return 0; 00655 } 00656 00657 int ast_playstream(struct ast_filestream *s) 00658 { 00659 if (s->fmt->format < AST_FORMAT_MAX_AUDIO) 00660 ast_readaudio_callback(s); 00661 else 00662 ast_readvideo_callback(s); 00663 return 0; 00664 } 00665 00666 int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence) 00667 { 00668 return fs->fmt->seek(fs, sample_offset, whence); 00669 } 00670 00671 int ast_truncstream(struct ast_filestream *fs) 00672 { 00673 return fs->fmt->trunc(fs); 00674 } 00675 00676 off_t ast_tellstream(struct ast_filestream *fs) 00677 { 00678 return fs->fmt->tell(fs); 00679 } 00680 00681 int ast_stream_fastforward(struct ast_filestream *fs, off_t ms) 00682 { 00683 return ast_seekstream(fs, ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR); 00684 } 00685 00686 int ast_stream_rewind(struct ast_filestream *fs, off_t ms) 00687 { 00688 return ast_seekstream(fs, -ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR); 00689 } 00690 00691 int ast_closestream(struct ast_filestream *f) 00692 { 00693 char *cmd = NULL; 00694 size_t size = 0; 00695 /* Stop a running stream if there is one */ 00696 if (f->owner) { 00697 if (f->fmt->format < AST_FORMAT_MAX_AUDIO) { 00698 f->owner->stream = NULL; 00699 if (f->owner->streamid > -1) 00700 ast_sched_del(f->owner->sched, f->owner->streamid); 00701 f->owner->streamid = -1; 00702 #ifdef HAVE_ZAPTEL 00703 ast_settimeout(f->owner, 0, NULL, NULL); 00704 #endif 00705 } else { 00706 f->owner->vstream = NULL; 00707 if (f->owner->vstreamid > -1) 00708 ast_sched_del(f->owner->sched, f->owner->vstreamid); 00709 f->owner->vstreamid = -1; 00710 } 00711 } 00712 /* destroy the translator on exit */ 00713 if (f->trans) 00714 ast_translator_free_path(f->trans); 00715 00716 if (f->realfilename && f->filename) { 00717 size = strlen(f->filename) + strlen(f->realfilename) + 15; 00718 cmd = alloca(size); 00719 memset(cmd,0,size); 00720 snprintf(cmd,size,"/bin/mv -f %s %s",f->filename,f->realfilename); 00721 ast_safe_system(cmd); 00722 } 00723 00724 if (f->filename) 00725 free(f->filename); 00726 if (f->realfilename) 00727 free(f->realfilename); 00728 if (f->fmt->close) 00729 f->fmt->close(f); 00730 fclose(f->f); 00731 if (f->vfs) 00732 ast_closestream(f->vfs); 00733 ast_module_unref(f->fmt->module); 00734 free(f); 00735 return 0; 00736 } 00737 00738 00739 /* 00740 * Look the various language-specific places where a file could exist. 00741 */ 00742 int ast_fileexists(const char *filename, const char *fmt, const char *preflang) 00743 { 00744 char *buf; 00745 int buflen; 00746 00747 if (preflang == NULL) 00748 preflang = ""; 00749 buflen = strlen(preflang) + strlen(filename) + 2; /* room for everything */ 00750 buf = alloca(buflen); 00751 if (buf == NULL) 00752 return 0; 00753 return fileexists_core(filename, fmt, preflang, buf, buflen); 00754 } 00755 00756 int ast_filedelete(const char *filename, const char *fmt) 00757 { 00758 return ast_filehelper(filename, NULL, fmt, ACTION_DELETE); 00759 } 00760 00761 int ast_filerename(const char *filename, const char *filename2, const char *fmt) 00762 { 00763 return ast_filehelper(filename, filename2, fmt, ACTION_RENAME); 00764 } 00765 00766 int ast_filecopy(const char *filename, const char *filename2, const char *fmt) 00767 { 00768 return ast_filehelper(filename, filename2, fmt, ACTION_COPY); 00769 } 00770 00771 int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang) 00772 { 00773 struct ast_filestream *fs; 00774 struct ast_filestream *vfs=NULL; 00775 char fmt[256]; 00776 00777 fs = ast_openstream(chan, filename, preflang); 00778 if (fs) 00779 vfs = ast_openvstream(chan, filename, preflang); 00780 if (vfs) { 00781 if (option_debug) 00782 ast_log(LOG_DEBUG, "Ooh, found a video stream, too, format %s\n", ast_getformatname(vfs->fmt->format)); 00783 } 00784 if (fs){ 00785 if (ast_applystream(chan, fs)) 00786 return -1; 00787 if (vfs && ast_applystream(chan, vfs)) 00788 return -1; 00789 if (ast_playstream(fs)) 00790 return -1; 00791 if (vfs && ast_playstream(vfs)) 00792 return -1; 00793 if (option_verbose > 2) 00794 ast_verbose(VERBOSE_PREFIX_3 "<%s> Playing '%s' (language '%s')\n", chan->name, filename, preflang ? preflang : "default"); 00795 00796 return 0; 00797 } 00798 ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", filename, ast_getformatname_multiple(fmt, sizeof(fmt), chan->nativeformats), strerror(errno)); 00799 return -1; 00800 } 00801 00802 struct ast_filestream *ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode) 00803 { 00804 FILE *bfile; 00805 struct ast_format *f; 00806 struct ast_filestream *fs = NULL; 00807 char *fn; 00808 00809 AST_RWLIST_RDLOCK(&formats); 00810 00811 AST_RWLIST_TRAVERSE(&formats, f, list) { 00812 fs = NULL; 00813 if (!exts_compare(f->exts, type)) 00814 continue; 00815 00816 fn = build_filename(filename, type); 00817 errno = 0; 00818 bfile = fopen(fn, "r"); 00819 if (!bfile || (fs = get_filestream(f, bfile)) == NULL || 00820 open_wrapper(fs) ) { 00821 ast_log(LOG_WARNING, "Unable to open %s\n", fn); 00822 if (fs) 00823 free(fs); 00824 if (bfile) 00825 fclose(bfile); 00826 free(fn); 00827 continue; 00828 } 00829 /* found it */ 00830 fs->trans = NULL; 00831 fs->fmt = f; 00832 fs->flags = flags; 00833 fs->mode = mode; 00834 fs->filename = strdup(filename); 00835 fs->vfs = NULL; 00836 break; 00837 } 00838 00839 AST_RWLIST_UNLOCK(&formats); 00840 if (!fs) 00841 ast_log(LOG_WARNING, "No such format '%s'\n", type); 00842 00843 return fs; 00844 } 00845 00846 struct ast_filestream *ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode) 00847 { 00848 int fd, myflags = 0; 00849 /* compiler claims this variable can be used before initialization... */ 00850 FILE *bfile = NULL; 00851 struct ast_format *f; 00852 struct ast_filestream *fs = NULL; 00853 char *buf = NULL; 00854 size_t size = 0; 00855 int format_found = 0; 00856 00857 AST_RWLIST_RDLOCK(&formats); 00858 00859 /* set the O_TRUNC flag if and only if there is no O_APPEND specified */ 00860 /* We really can't use O_APPEND as it will break WAV header updates */ 00861 if (flags & O_APPEND) { 00862 flags &= ~O_APPEND; 00863 } else { 00864 myflags = O_TRUNC; 00865 } 00866 00867 myflags |= O_WRONLY | O_CREAT; 00868 00869 /* XXX need to fix this - we should just do the fopen, 00870 * not open followed by fdopen() 00871 */ 00872 AST_RWLIST_TRAVERSE(&formats, f, list) { 00873 char *fn, *orig_fn = NULL; 00874 if (fs) 00875 break; 00876 00877 if (!exts_compare(f->exts, type)) 00878 continue; 00879 else 00880 format_found = 1; 00881 00882 fn = build_filename(filename, type); 00883 fd = open(fn, flags | myflags, mode); 00884 if (fd > -1) { 00885 /* fdopen() the resulting file stream */ 00886 bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w"); 00887 if (!bfile) { 00888 ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno)); 00889 close(fd); 00890 fd = -1; 00891 } 00892 } 00893 00894 if (ast_opt_cache_record_files && (fd > -1)) { 00895 char *c; 00896 00897 fclose(bfile); /* this also closes fd */ 00898 /* 00899 We touch orig_fn just as a place-holder so other things (like vmail) see the file is there. 00900 What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place. 00901 */ 00902 orig_fn = ast_strdupa(fn); 00903 for (c = fn; *c; c++) 00904 if (*c == '/') 00905 *c = '_'; 00906 00907 size = strlen(fn) + strlen(record_cache_dir) + 2; 00908 buf = alloca(size); 00909 strcpy(buf, record_cache_dir); 00910 strcat(buf, "/"); 00911 strcat(buf, fn); 00912 free(fn); 00913 fn = buf; 00914 fd = open(fn, flags | myflags, mode); 00915 if (fd > -1) { 00916 /* fdopen() the resulting file stream */ 00917 bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w"); 00918 if (!bfile) { 00919 ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno)); 00920 close(fd); 00921 fd = -1; 00922 } 00923 } 00924 } 00925 if (fd > -1) { 00926 errno = 0; 00927 fs = get_filestream(f, bfile); 00928 if (!fs || rewrite_wrapper(fs, comment)) { 00929 ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn); 00930 close(fd); 00931 if (orig_fn) { 00932 unlink(fn); 00933 unlink(orig_fn); 00934 } 00935 if (fs) 00936 free(fs); 00937 } 00938 fs->trans = NULL; 00939 fs->fmt = f; 00940 fs->flags = flags; 00941 fs->mode = mode; 00942 if (orig_fn) { 00943 fs->realfilename = strdup(orig_fn); 00944 fs->filename = strdup(fn); 00945 } else { 00946 fs->realfilename = NULL; 00947 fs->filename = strdup(filename); 00948 } 00949 fs->vfs = NULL; 00950 /* If truncated, we'll be at the beginning; if not truncated, then append */ 00951 f->seek(fs, 0, SEEK_END); 00952 } else if (errno != EEXIST) { 00953 ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno)); 00954 if (orig_fn) 00955 unlink(orig_fn); 00956 } 00957 /* if buf != NULL then fn is already free and pointing to it */ 00958 if (!buf) 00959 free(fn); 00960 } 00961 00962 AST_RWLIST_UNLOCK(&formats); 00963 00964 if (!format_found) 00965 ast_log(LOG_WARNING, "No such format '%s'\n", type); 00966 00967 return fs; 00968 } 00969 00970 /*! 00971 * \brief the core of all waitstream() functions 00972 */ 00973 static int waitstream_core(struct ast_channel *c, const char *breakon, 00974 const char *forward, const char *rewind, int skip_ms, 00975 int audiofd, int cmdfd, const char *context) 00976 { 00977 if (!breakon) 00978 breakon = ""; 00979 if (!forward) 00980 forward = ""; 00981 if (!rewind) 00982 rewind = ""; 00983 00984 while (c->stream) { 00985 int res; 00986 int ms = ast_sched_wait(c->sched); 00987 if (ms < 0 && !c->timingfunc) { 00988 ast_stopstream(c); 00989 break; 00990 } 00991 if (ms < 0) 00992 ms = 1000; 00993 if (cmdfd < 0) { 00994 res = ast_waitfor(c, ms); 00995 if (res < 0) { 00996 ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno)); 00997 return res; 00998 } 00999 } else { 01000 int outfd; 01001 struct ast_channel *rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms); 01002 if (!rchan && (outfd < 0) && (ms)) { 01003 /* Continue */ 01004 if (errno == EINTR) 01005 continue; 01006 ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno)); 01007 return -1; 01008 } else if (outfd > -1) { /* this requires cmdfd set */ 01009 /* The FD we were watching has something waiting */ 01010 return 1; 01011 } 01012 /* if rchan is set, it is 'c' */ 01013 res = rchan ? 1 : 0; /* map into 'res' values */ 01014 } 01015 if (res > 0) { 01016 struct ast_frame *fr = ast_read(c); 01017 if (!fr) 01018 return -1; 01019 switch (fr->frametype) { 01020 case AST_FRAME_DTMF_END: 01021 if (context) { 01022 const char exten[2] = { fr->subclass, '\0' }; 01023 if (ast_exists_extension(c, context, exten, 1, c->cid.cid_num)) { 01024 ast_frfree(fr); 01025 return res; 01026 } 01027 } else { 01028 res = fr->subclass; 01029 if (strchr(forward,res)) { 01030 ast_stream_fastforward(c->stream, skip_ms); 01031 } else if (strchr(rewind,res)) { 01032 ast_stream_rewind(c->stream, skip_ms); 01033 } else if (strchr(breakon, res)) { 01034 ast_frfree(fr); 01035 return res; 01036 } 01037 } 01038 break; 01039 case AST_FRAME_CONTROL: 01040 switch (fr->subclass) { 01041 case AST_CONTROL_HANGUP: 01042 case AST_CONTROL_BUSY: 01043 case AST_CONTROL_CONGESTION: 01044 ast_frfree(fr); 01045 return -1; 01046 case AST_CONTROL_RINGING: 01047 case AST_CONTROL_ANSWER: 01048 case AST_CONTROL_VIDUPDATE: 01049 case AST_CONTROL_HOLD: 01050 case AST_CONTROL_UNHOLD: 01051 /* Unimportant */ 01052 break; 01053 default: 01054 ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass); 01055 } 01056 break; 01057 case AST_FRAME_VOICE: 01058 /* Write audio if appropriate */ 01059 if (audiofd > -1) 01060 write(audiofd, fr->data, fr->datalen); 01061 default: 01062 /* Ignore all others */ 01063 break; 01064 } 01065 ast_frfree(fr); 01066 } 01067 ast_sched_runq(c->sched); 01068 } 01069 return (c->_softhangup ? -1 : 0); 01070 } 01071 01072 int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *rewind, int ms) 01073 { 01074 return waitstream_core(c, breakon, forward, rewind, ms, 01075 -1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */); 01076 } 01077 01078 int ast_waitstream(struct ast_channel *c, const char *breakon) 01079 { 01080 return waitstream_core(c, breakon, NULL, NULL, 0, -1, -1, NULL); 01081 } 01082 01083 int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd) 01084 { 01085 return waitstream_core(c, breakon, NULL, NULL, 0, 01086 audiofd, cmdfd, NULL /* no context */); 01087 } 01088 01089 int ast_waitstream_exten(struct ast_channel *c, const char *context) 01090 { 01091 /* Waitstream, with return in the case of a valid 1 digit extension */ 01092 /* in the current or specified context being pressed */ 01093 01094 if (!context) 01095 context = c->context; 01096 return waitstream_core(c, NULL, NULL, NULL, 0, 01097 -1, -1, context); 01098 } 01099 01100 /* 01101 * if the file name is non-empty, try to play it. 01102 * Return 0 if success, -1 if error, digit if interrupted by a digit. 01103 * If digits == "" then we can simply check for non-zero. 01104 */ 01105 int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits) 01106 { 01107 int res = 0; 01108 if (!ast_strlen_zero(file)) { 01109 res = ast_streamfile(chan, file, chan->language); 01110 if (!res) 01111 res = ast_waitstream(chan, digits); 01112 } 01113 return res; 01114 } 01115 01116 static int show_file_formats(int fd, int argc, char *argv[]) 01117 { 01118 #define FORMAT "%-10s %-10s %-20s\n" 01119 #define FORMAT2 "%-10s %-10s %-20s\n" 01120 struct ast_format *f; 01121 int count_fmt = 0; 01122 01123 if (argc != 4) 01124 return RESULT_SHOWUSAGE; 01125 ast_cli(fd, FORMAT, "Format", "Name", "Extensions"); 01126 01127 AST_RWLIST_RDLOCK(&formats); 01128 AST_RWLIST_TRAVERSE(&formats, f, list) { 01129 ast_cli(fd, FORMAT2, ast_getformatname(f->format), f->name, f->exts); 01130 count_fmt++; 01131 } 01132 AST_RWLIST_UNLOCK(&formats); 01133 ast_cli(fd, "%d file formats registered.\n", count_fmt); 01134 return RESULT_SUCCESS; 01135 #undef FORMAT 01136 #undef FORMAT2 01137 } 01138 01139 static const char show_file_formats_usage[] = 01140 "Usage: core show file formats\n" 01141 " Displays currently registered file formats (if any)\n"; 01142 01143 struct ast_cli_entry cli_file[] = { 01144 { { "core", "show", "file", "formats" }, 01145 show_file_formats, "Displays file formats", 01146 show_file_formats_usage }, 01147 }; 01148 01149 int ast_file_init(void) 01150 { 01151 ast_cli_register_multiple(cli_file, sizeof(cli_file) / sizeof(struct ast_cli_entry)); 01152 return 0; 01153 }