Fri Jun 19 12:09:44 2009

Asterisk developer's documentation


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: 188596 $")
00029 
00030 #include <dirent.h>
00031 #include <sys/stat.h>
00032 #include <math.h>
00033 
00034 #include "asterisk/_private.h"   /* declare ast_file_init() */
00035 #include "asterisk/paths.h"   /* use ast_config_AST_DATA_DIR */
00036 #include "asterisk/mod_format.h"
00037 #include "asterisk/cli.h"
00038 #include "asterisk/channel.h"
00039 #include "asterisk/sched.h"
00040 #include "asterisk/translate.h"
00041 #include "asterisk/utils.h"
00042 #include "asterisk/lock.h"
00043 #include "asterisk/app.h"
00044 #include "asterisk/pbx.h"
00045 #include "asterisk/linkedlists.h"
00046 #include "asterisk/module.h"
00047 #include "asterisk/astobj2.h"
00048 
00049 /*
00050  * The following variable controls the layout of localized sound files.
00051  * If 0, use the historical layout with prefix just before the filename
00052  * (i.e. digits/en/1.gsm , digits/it/1.gsm or default to digits/1.gsm),
00053  * if 1 put the prefix at the beginning of the filename
00054  * (i.e. en/digits/1.gsm, it/digits/1.gsm or default to digits/1.gsm).
00055  * The latter permits a language to be entirely in one directory.
00056  */
00057 int ast_language_is_prefix = 1;
00058 
00059 static AST_RWLIST_HEAD_STATIC(formats, ast_format);
00060 
00061 int __ast_format_register(const struct ast_format *f, struct ast_module *mod)
00062 {
00063    struct ast_format *tmp;
00064 
00065    AST_RWLIST_WRLOCK(&formats);
00066    AST_RWLIST_TRAVERSE(&formats, tmp, list) {
00067       if (!strcasecmp(f->name, tmp->name)) {
00068          AST_RWLIST_UNLOCK(&formats);
00069          ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", f->name);
00070          return -1;
00071       }
00072    }
00073    if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
00074       AST_RWLIST_UNLOCK(&formats);
00075       return -1;
00076    }
00077    *tmp = *f;
00078    tmp->module = mod;
00079    if (tmp->buf_size) {
00080       /*
00081        * Align buf_size properly, rounding up to the machine-specific
00082        * alignment for pointers.
00083        */
00084       struct _test_align { void *a, *b; } p;
00085       int align = (char *)&p.b - (char *)&p.a;
00086       tmp->buf_size = ((f->buf_size + align - 1) / align) * align;
00087    }
00088    
00089    memset(&tmp->list, 0, sizeof(tmp->list));
00090 
00091    AST_RWLIST_INSERT_HEAD(&formats, tmp, list);
00092    AST_RWLIST_UNLOCK(&formats);
00093    ast_verb(2, "Registered file format %s, extension(s) %s\n", f->name, f->exts);
00094 
00095    return 0;
00096 }
00097 
00098 int ast_format_unregister(const char *name)
00099 {
00100    struct ast_format *tmp;
00101    int res = -1;
00102 
00103    AST_RWLIST_WRLOCK(&formats);
00104    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&formats, tmp, list) {
00105       if (!strcasecmp(name, tmp->name)) {
00106          AST_RWLIST_REMOVE_CURRENT(list);
00107          ast_free(tmp);
00108          res = 0;
00109       }
00110    }
00111    AST_RWLIST_TRAVERSE_SAFE_END;
00112    AST_RWLIST_UNLOCK(&formats);
00113 
00114    if (!res)
00115       ast_verb(2, "Unregistered format %s\n", name);
00116    else
00117       ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name);
00118 
00119    return res;
00120 }
00121 
00122 int ast_stopstream(struct ast_channel *tmp)
00123 {
00124    ast_channel_lock(tmp);
00125 
00126    /* Stop a running stream if there is one */
00127    if (tmp->stream) {
00128       ast_closestream(tmp->stream);
00129       tmp->stream = NULL;
00130       if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
00131          ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
00132    }
00133    /* Stop the video stream too */
00134    if (tmp->vstream != NULL) {
00135       ast_closestream(tmp->vstream);
00136       tmp->vstream = NULL;
00137    }
00138 
00139    ast_channel_unlock(tmp);
00140 
00141    return 0;
00142 }
00143 
00144 int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
00145 {
00146    int res = -1;
00147    int alt = 0;
00148    if (f->frametype == AST_FRAME_VIDEO) {
00149       if (fs->fmt->format & AST_FORMAT_AUDIO_MASK) {
00150          /* This is the audio portion.  Call the video one... */
00151          if (!fs->vfs && fs->filename) {
00152             const char *type = ast_getformatname(f->subclass & ~0x1);
00153             fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode);
00154             ast_debug(1, "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             ast_frfree(trf);
00194             if (res) 
00195                ast_log(LOG_WARNING, "Translated frame write failed\n");
00196          } else
00197             res = 0;
00198       }
00199    }
00200    return res;
00201 }
00202 
00203 static int copy(const char *infile, const char *outfile)
00204 {
00205    int ifd, ofd, len;
00206    char buf[4096];   /* XXX make it lerger. */
00207 
00208    if ((ifd = open(infile, O_RDONLY)) < 0) {
00209       ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
00210       return -1;
00211    }
00212    if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, AST_FILE_MODE)) < 0) {
00213       ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
00214       close(ifd);
00215       return -1;
00216    }
00217    while ( (len = read(ifd, buf, sizeof(buf)) ) ) {
00218       int res;
00219       if (len < 0) {
00220          ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
00221          break;
00222       }
00223       /* XXX handle partial writes */
00224       res = write(ofd, buf, len);
00225       if (res != len) {
00226          ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
00227          len = -1; /* error marker */
00228          break;
00229       }
00230    }
00231    close(ifd);
00232    close(ofd);
00233    if (len < 0) {
00234       unlink(outfile);
00235       return -1; /* error */
00236    }
00237    return 0;   /* success */
00238 }
00239 
00240 /*!
00241  * \brief construct a filename. Absolute pathnames are preserved,
00242  * relative names are prefixed by the sounds/ directory.
00243  * The wav49 suffix is replaced by 'WAV'.
00244  * Returns a malloc'ed string to be freed by the caller.
00245  */
00246 static char *build_filename(const char *filename, const char *ext)
00247 {
00248    char *fn = NULL;
00249 
00250    if (!strcmp(ext, "wav49"))
00251       ext = "WAV";
00252 
00253    if (filename[0] == '/') {
00254       if (asprintf(&fn, "%s.%s", filename, ext) < 0) {
00255          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00256          fn = NULL;
00257       }
00258    } else {
00259       if (asprintf(&fn, "%s/sounds/%s.%s",
00260               ast_config_AST_DATA_DIR, filename, ext) < 0) {
00261          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00262          fn = NULL;
00263       }
00264    }
00265    return fn;
00266 }
00267 
00268 /* compare type against the list 'exts' */
00269 /* XXX need a better algorithm */
00270 static int exts_compare(const char *exts, const char *type)
00271 {
00272    char tmp[256];
00273    char *stringp = tmp, *ext;
00274 
00275    ast_copy_string(tmp, exts, sizeof(tmp));
00276    while ((ext = strsep(&stringp, "|"))) {
00277       if (!strcmp(ext, type))
00278          return 1;
00279    }
00280 
00281    return 0;
00282 }
00283 
00284 static void filestream_destructor(void *arg)
00285 {
00286    char *cmd = NULL;
00287    size_t size = 0;
00288    struct ast_filestream *f = arg;
00289 
00290    /* Stop a running stream if there is one */
00291    if (f->owner) {
00292       if (f->fmt->format < AST_FORMAT_AUDIO_MASK) {
00293          f->owner->stream = NULL;
00294          AST_SCHED_DEL(f->owner->sched, f->owner->streamid);
00295          ast_settimeout(f->owner, 0, NULL, NULL);
00296       } else {
00297          f->owner->vstream = NULL;
00298          AST_SCHED_DEL(f->owner->sched, f->owner->vstreamid);
00299       }
00300    }
00301    /* destroy the translator on exit */
00302    if (f->trans)
00303       ast_translator_free_path(f->trans);
00304 
00305    if (f->realfilename && f->filename) {
00306          size = strlen(f->filename) + strlen(f->realfilename) + 15;
00307          cmd = alloca(size);
00308          memset(cmd,0,size);
00309          snprintf(cmd,size,"/bin/mv -f %s %s",f->filename,f->realfilename);
00310          ast_safe_system(cmd);
00311    }
00312 
00313    if (f->filename)
00314       free(f->filename);
00315    if (f->realfilename)
00316       free(f->realfilename);
00317    if (f->fmt->close) {
00318       void (*closefn)(struct ast_filestream *) = f->fmt->close;
00319       closefn(f);
00320    }
00321    if (f->f)
00322       fclose(f->f);
00323    if (f->vfs)
00324       ast_closestream(f->vfs);
00325    if (f->orig_chan_name)
00326       free((void *) f->orig_chan_name);
00327    ast_module_unref(f->fmt->module);
00328 }
00329 
00330 static struct ast_filestream *get_filestream(struct ast_format *fmt, FILE *bfile)
00331 {
00332    struct ast_filestream *s;
00333 
00334    int l = sizeof(*s) + fmt->buf_size + fmt->desc_size;  /* total allocation size */
00335    if ( (s = ao2_alloc(l, filestream_destructor)) == NULL)
00336       return NULL;
00337    s->fmt = fmt;
00338    s->f = bfile;
00339 
00340    if (fmt->desc_size)
00341       s->_private = ((char *)(s + 1)) + fmt->buf_size;
00342    if (fmt->buf_size)
00343       s->buf = (char *)(s + 1);
00344    s->fr.src = fmt->name;
00345    return s;
00346 }
00347 
00348 /*
00349  * Default implementations of open and rewrite.
00350  * Only use them if you don't have expensive stuff to do.
00351  */
00352 enum wrap_fn { WRAP_OPEN, WRAP_REWRITE };
00353 
00354 static int fn_wrapper(struct ast_filestream *s, const char *comment, enum wrap_fn mode)
00355 {
00356    struct ast_format *f = s->fmt;
00357    int ret = -1;
00358    int (*openfn)(struct ast_filestream *s);
00359 
00360    if (mode == WRAP_OPEN && (openfn = f->open) && openfn(s))
00361       ast_log(LOG_WARNING, "Unable to open format %s\n", f->name);
00362    else if (mode == WRAP_REWRITE && f->rewrite && f->rewrite(s, comment))
00363       ast_log(LOG_WARNING, "Unable to rewrite format %s\n", f->name);
00364    else {
00365       /* preliminary checks succeed. update usecount */
00366       ast_module_ref(f->module);
00367       ret = 0;
00368    }
00369    return ret;
00370 }
00371 
00372 static int rewrite_wrapper(struct ast_filestream *s, const char *comment)
00373 {
00374    return fn_wrapper(s, comment, WRAP_REWRITE);
00375 }
00376 
00377 static int open_wrapper(struct ast_filestream *s)
00378 {
00379    return fn_wrapper(s, NULL, WRAP_OPEN);
00380 }
00381 
00382 enum file_action {
00383    ACTION_EXISTS = 1, /* return matching format if file exists, 0 otherwise */
00384    ACTION_DELETE, /* delete file, return 0 on success, -1 on error */
00385    ACTION_RENAME, /* rename file. return 0 on success, -1 on error */
00386    ACTION_OPEN,
00387    ACTION_COPY /* copy file. return 0 on success, -1 on error */
00388 };
00389 
00390 /*!
00391  * \brief perform various actions on a file. Second argument
00392  * arg2 depends on the command:
00393  * unused for EXISTS and DELETE
00394  * destination file name (const char *) for COPY and RENAME
00395  *    struct ast_channel * for OPEN
00396  * if fmt is NULL, OPEN will return the first matching entry,
00397  * whereas other functions will run on all matching entries.
00398  */
00399 static int ast_filehelper(const char *filename, const void *arg2, const char *fmt, const enum file_action action)
00400 {
00401    struct ast_format *f;
00402    int res = (action == ACTION_EXISTS) ? 0 : -1;
00403 
00404    AST_RWLIST_RDLOCK(&formats);
00405    /* Check for a specific format */
00406    AST_RWLIST_TRAVERSE(&formats, f, list) {
00407       char *stringp, *ext = NULL;
00408 
00409       if (fmt && !exts_compare(f->exts, fmt))
00410          continue;
00411 
00412       /* Look for a file matching the supported extensions.
00413        * The file must exist, and for OPEN, must match
00414        * one of the formats supported by the channel.
00415        */
00416       stringp = ast_strdupa(f->exts);  /* this is in the stack so does not need to be freed */
00417       while ( (ext = strsep(&stringp, "|")) ) {
00418          struct stat st;
00419          char *fn = build_filename(filename, ext);
00420 
00421          if (fn == NULL)
00422             continue;
00423 
00424          if ( stat(fn, &st) ) { /* file not existent */
00425             ast_free(fn);
00426             continue;
00427          }
00428          /* for 'OPEN' we need to be sure that the format matches
00429           * what the channel can process
00430           */
00431          if (action == ACTION_OPEN) {
00432             struct ast_channel *chan = (struct ast_channel *)arg2;
00433             FILE *bfile;
00434             struct ast_filestream *s;
00435 
00436             if ( !(chan->writeformat & f->format) &&
00437                  !((f->format & AST_FORMAT_AUDIO_MASK && fmt) ||
00438                  (f->format & AST_FORMAT_VIDEO_MASK && fmt))) {
00439                ast_free(fn);
00440                continue;   /* not a supported format */
00441             }
00442             if ( (bfile = fopen(fn, "r")) == NULL) {
00443                ast_free(fn);
00444                continue;   /* cannot open file */
00445             }
00446             s = get_filestream(f, bfile);
00447             if (!s) {
00448                fclose(bfile);
00449                ast_free(fn);  /* cannot allocate descriptor */
00450                continue;
00451             }
00452             if (open_wrapper(s)) {
00453                ast_free(fn);
00454                ast_closestream(s);
00455                continue;   /* cannot run open on file */
00456             }
00457             if (st.st_size == 0) {
00458                ast_log(LOG_WARNING, "File %s detected to have zero size.\n", fn);
00459             }
00460             /* ok this is good for OPEN */
00461             res = 1; /* found */
00462             s->lasttimeout = -1;
00463             s->fmt = f;
00464             s->trans = NULL;
00465             s->filename = NULL;
00466             if (s->fmt->format & AST_FORMAT_AUDIO_MASK) {
00467                if (chan->stream)
00468                   ast_closestream(chan->stream);
00469                chan->stream = s;
00470             } else {
00471                if (chan->vstream)
00472                   ast_closestream(chan->vstream);
00473                chan->vstream = s;
00474             }
00475             ast_free(fn);
00476             break;
00477          }
00478          switch (action) {
00479          case ACTION_OPEN:
00480             break;   /* will never get here */
00481 
00482          case ACTION_EXISTS:  /* return the matching format */
00483             res |= f->format;
00484             break;
00485 
00486          case ACTION_DELETE:
00487             if ( (res = unlink(fn)) )
00488                ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
00489             break;
00490 
00491          case ACTION_RENAME:
00492          case ACTION_COPY: {
00493             char *nfn = build_filename((const char *)arg2, ext);
00494             if (!nfn)
00495                ast_log(LOG_WARNING, "Out of memory\n");
00496             else {
00497                res = action == ACTION_COPY ? copy(fn, nfn) : rename(fn, nfn);
00498                if (res)
00499                   ast_log(LOG_WARNING, "%s(%s,%s) failed: %s\n",
00500                      action == ACTION_COPY ? "copy" : "rename",
00501                       fn, nfn, strerror(errno));
00502                ast_free(nfn);
00503             }
00504              }
00505             break;
00506 
00507          default:
00508             ast_log(LOG_WARNING, "Unknown helper %d\n", action);
00509          }
00510          ast_free(fn);
00511       }
00512    }
00513    AST_RWLIST_UNLOCK(&formats);
00514    return res;
00515 }
00516 
00517 static int is_absolute_path(const char *filename)
00518 {
00519    return filename[0] == '/';
00520 }
00521 
00522 static int fileexists_test(const char *filename, const char *fmt, const char *lang,
00523             char *buf, int buflen)
00524 {
00525    if (buf == NULL) {
00526       return -1;
00527    }
00528 
00529    if (ast_language_is_prefix && !is_absolute_path(filename)) { /* new layout */
00530       if (lang) {
00531          snprintf(buf, buflen, "%s/%s", lang, filename);
00532       } else {
00533          snprintf(buf, buflen, "%s", filename);
00534       }
00535    } else { /* old layout */
00536       strcpy(buf, filename);  /* first copy the full string */
00537       if (lang) {
00538          /* insert the language and suffix if needed */
00539          const char *c = strrchr(filename, '/');
00540          int offset = c ? c - filename + 1 : 0; /* points right after the last '/' */
00541          snprintf(buf + offset, buflen - offset, "%s/%s", lang, filename + offset);
00542       }
00543    }
00544 
00545    return ast_filehelper(buf, NULL, fmt, ACTION_EXISTS);
00546 }
00547 
00548 /*!
00549  * \brief helper routine to locate a file with a given format
00550  * and language preference.
00551  * Try preflang, preflang with stripped '_' suffix, or NULL.
00552  * In the standard asterisk, language goes just before the last component.
00553  * In an alternative configuration, the language should be a prefix
00554  * to the actual filename.
00555  *
00556  * The last parameter(s) point to a buffer of sufficient size,
00557  * which on success is filled with the matching filename.
00558  */
00559 static int fileexists_core(const char *filename, const char *fmt, const char *preflang,
00560             char *buf, int buflen)
00561 {
00562    int res = -1;
00563    char *lang = NULL;
00564 
00565    if (buf == NULL) {
00566       return -1;
00567    }
00568 
00569    /* We try languages in the following order:
00570     *    preflang (may include dialect)
00571     *    lang (preflang without dialect - if any)
00572     *    <none>
00573     *    default (unless the same as preflang or lang without dialect)
00574     */
00575 
00576    /* Try preferred language */
00577    if (!ast_strlen_zero(preflang)) {
00578       /* try the preflang exactly as it was requested */
00579       if ((res = fileexists_test(filename, fmt, preflang, buf, buflen)) > 0) {
00580          return res;
00581       } else {
00582          /* try without a dialect */
00583          char *postfix = NULL;
00584          postfix = lang = ast_strdupa(preflang);
00585 
00586          strsep(&postfix, "_");
00587          if (postfix) {
00588             if ((res = fileexists_test(filename, fmt, lang, buf, buflen)) > 0) {
00589                return res;
00590             }
00591          }
00592       }
00593    }
00594 
00595    /* Try without any language */
00596    if ((res = fileexists_test(filename, fmt, NULL, buf, buflen)) > 0) {
00597       return res;
00598    }
00599 
00600    /* Finally try the default language unless it was already tried before */
00601    if ((ast_strlen_zero(preflang) || strcmp(preflang, DEFAULT_LANGUAGE)) && (ast_strlen_zero(lang) || strcmp(lang, DEFAULT_LANGUAGE))) {
00602       if ((res = fileexists_test(filename, fmt, DEFAULT_LANGUAGE, buf, buflen)) > 0) {
00603          return res;
00604       }
00605    }
00606 
00607    return 0;
00608 }
00609 
00610 struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *filename, const char *preflang)
00611 {
00612    return ast_openstream_full(chan, filename, preflang, 0);
00613 }
00614 
00615 struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis)
00616 {
00617    /* 
00618     * Use fileexists_core() to find a file in a compatible
00619     * language and format, set up a suitable translator,
00620     * and open the stream.
00621     */
00622    int fmts, res, buflen;
00623    char *buf;
00624 
00625    if (!asis) {
00626       /* do this first, otherwise we detect the wrong writeformat */
00627       ast_stopstream(chan);
00628       if (chan->generator)
00629          ast_deactivate_generator(chan);
00630    }
00631    if (preflang == NULL)
00632       preflang = "";
00633    buflen = strlen(preflang) + strlen(filename) + 4;
00634    buf = alloca(buflen);
00635    if (buf == NULL)
00636       return NULL;
00637    fmts = fileexists_core(filename, NULL, preflang, buf, buflen);
00638    if (fmts > 0)
00639       fmts &= AST_FORMAT_AUDIO_MASK;
00640    if (fmts < 1) {
00641       ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
00642       return NULL;
00643    }
00644    chan->oldwriteformat = chan->writeformat;
00645    /* Set the channel to a format we can work with */
00646    res = ast_set_write_format(chan, fmts);
00647    res = ast_filehelper(buf, chan, NULL, ACTION_OPEN);
00648    if (res >= 0)
00649       return chan->stream;
00650    return NULL;
00651 }
00652 
00653 struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *filename, const char *preflang)
00654 {
00655    /* As above, but for video. But here we don't have translators
00656     * so we must enforce a format.
00657     */
00658    unsigned int format;
00659    char *buf;
00660    int buflen;
00661 
00662    if (preflang == NULL)
00663       preflang = "";
00664    buflen = strlen(preflang) + strlen(filename) + 4;
00665    buf = alloca(buflen);
00666    if (buf == NULL)
00667       return NULL;
00668 
00669    for (format = AST_FORMAT_AUDIO_MASK + 1; format <= AST_FORMAT_VIDEO_MASK; format = format << 1) {
00670       int fd;
00671       const char *fmt;
00672 
00673       if (!(chan->nativeformats & format))
00674          continue;
00675       fmt = ast_getformatname(format);
00676       if ( fileexists_core(filename, fmt, preflang, buf, buflen) < 1)   /* no valid format */
00677          continue;
00678       fd = ast_filehelper(buf, chan, fmt, ACTION_OPEN);
00679       if (fd >= 0)
00680          return chan->vstream;
00681       ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename);
00682    }
00683    return NULL;
00684 }
00685 
00686 struct ast_frame *ast_readframe(struct ast_filestream *s)
00687 {
00688    struct ast_frame *f = NULL;
00689    int whennext = 0; 
00690    if (s && s->fmt)
00691       f = s->fmt->read(s, &whennext);
00692    if (f) {
00693       ast_set_flag(f, AST_FRFLAG_FROM_FILESTREAM);
00694       ao2_ref(s, +1);
00695    }
00696    return f;
00697 }
00698 
00699 enum fsread_res {
00700    FSREAD_FAILURE,
00701    FSREAD_SUCCESS_SCHED,
00702    FSREAD_SUCCESS_NOSCHED,
00703 };
00704 
00705 static int ast_fsread_audio(const void *data);
00706 
00707 static enum fsread_res ast_readaudio_callback(struct ast_filestream *s)
00708 {
00709    int whennext = 0;
00710 
00711    while (!whennext) {
00712       struct ast_frame *fr;
00713       
00714       if (s->orig_chan_name && strcasecmp(s->owner->name, s->orig_chan_name))
00715          goto return_failure;
00716       
00717       fr = s->fmt->read(s, &whennext);
00718       if (fr) {
00719          ast_set_flag(fr, AST_FRFLAG_FROM_FILESTREAM);
00720          ao2_ref(s, +1);
00721       }
00722       if (!fr /* stream complete */ || ast_write(s->owner, fr) /* error writing */) {
00723          if (fr) {
00724             ast_log(LOG_WARNING, "Failed to write frame\n");
00725             ast_frfree(fr);
00726          }
00727          goto return_failure;
00728       } 
00729       if (fr) {
00730          ast_frfree(fr);
00731       }
00732    }
00733    if (whennext != s->lasttimeout) {
00734       if (s->owner->timingfd > -1) {
00735          float samp_rate = (float) ast_format_rate(s->fmt->format);
00736          unsigned int rate;
00737 
00738          rate = (unsigned int) roundf(samp_rate / ((float) whennext));
00739 
00740          ast_settimeout(s->owner, rate, ast_fsread_audio, s);
00741       } else {
00742          s->owner->streamid = ast_sched_add(s->owner->sched, 
00743             whennext / (ast_format_rate(s->fmt->format) / 1000), ast_fsread_audio, s);
00744       }
00745       s->lasttimeout = whennext;
00746       return FSREAD_SUCCESS_NOSCHED;
00747    }
00748    return FSREAD_SUCCESS_SCHED;
00749 
00750 return_failure:
00751    s->owner->streamid = -1;
00752    ast_settimeout(s->owner, 0, NULL, NULL);
00753    return FSREAD_FAILURE;
00754 }
00755 
00756 static int ast_fsread_audio(const void *data)
00757 {
00758    struct ast_filestream *fs = (struct ast_filestream *)data;
00759    enum fsread_res res;
00760 
00761    res = ast_readaudio_callback(fs);
00762 
00763    if (res == FSREAD_SUCCESS_SCHED)
00764       return 1;
00765    
00766    return 0;
00767 }
00768 
00769 static int ast_fsread_video(const void *data);
00770 
00771 static enum fsread_res ast_readvideo_callback(struct ast_filestream *s)
00772 {
00773    int whennext = 0;
00774 
00775    while (!whennext) {
00776       struct ast_frame *fr = s->fmt->read(s, &whennext);
00777       if (fr) {
00778          ast_set_flag(fr, AST_FRFLAG_FROM_FILESTREAM);
00779          ao2_ref(s, +1);
00780       }
00781       if (!fr /* stream complete */ || ast_write(s->owner, fr) /* error writing */) {
00782          if (fr) {
00783             ast_log(LOG_WARNING, "Failed to write frame\n");
00784             ast_frfree(fr);
00785          }
00786          s->owner->vstreamid = -1;
00787          return FSREAD_FAILURE;
00788       }
00789       if (fr) {
00790          ast_frfree(fr);
00791       }
00792    }
00793 
00794    if (whennext != s->lasttimeout) {
00795       s->owner->vstreamid = ast_sched_add(s->owner->sched, 
00796          whennext / (ast_format_rate(s->fmt->format) / 1000), 
00797          ast_fsread_video, s);
00798       s->lasttimeout = whennext;
00799       return FSREAD_SUCCESS_NOSCHED;
00800    }
00801 
00802    return FSREAD_SUCCESS_SCHED;
00803 }
00804 
00805 static int ast_fsread_video(const void *data)
00806 {
00807    struct ast_filestream *fs = (struct ast_filestream *)data;
00808    enum fsread_res res;
00809 
00810    res = ast_readvideo_callback(fs);
00811 
00812    if (res == FSREAD_SUCCESS_SCHED)
00813       return 1;
00814    
00815    return 0;
00816 }
00817 
00818 int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
00819 {
00820    s->owner = chan;
00821    return 0;
00822 }
00823 
00824 int ast_playstream(struct ast_filestream *s)
00825 {
00826    enum fsread_res res;
00827 
00828    if (s->fmt->format & AST_FORMAT_AUDIO_MASK)
00829       res = ast_readaudio_callback(s);
00830    else
00831       res = ast_readvideo_callback(s);
00832 
00833    return (res == FSREAD_FAILURE) ? -1 : 0;
00834 }
00835 
00836 int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
00837 {
00838    return fs->fmt->seek(fs, sample_offset, whence);
00839 }
00840 
00841 int ast_truncstream(struct ast_filestream *fs)
00842 {
00843    return fs->fmt->trunc(fs);
00844 }
00845 
00846 off_t ast_tellstream(struct ast_filestream *fs)
00847 {
00848    return fs->fmt->tell(fs);
00849 }
00850 
00851 int ast_stream_fastforward(struct ast_filestream *fs, off_t ms)
00852 {
00853    return ast_seekstream(fs, ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR);
00854 }
00855 
00856 int ast_stream_rewind(struct ast_filestream *fs, off_t ms)
00857 {
00858    return ast_seekstream(fs, -ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR);
00859 }
00860 
00861 int ast_closestream(struct ast_filestream *f)
00862 {
00863    if (ast_test_flag(&f->fr, AST_FRFLAG_FROM_FILESTREAM)) {
00864       /* If this flag is still set, it essentially means that the reference
00865        * count of f is non-zero. We can't destroy this filestream until
00866        * whatever is using the filestream's frame has finished.
00867        *
00868        * Since this was called, however, we need to remove the reference from
00869        * when this filestream was first allocated. That way, when the embedded
00870        * frame is freed, the refcount will reach 0 and we can finish destroying
00871        * this filestream properly.
00872        */
00873       ao2_ref(f, -1);
00874       return 0;
00875    }
00876    
00877    ao2_ref(f, -1);
00878    return 0;
00879 }
00880 
00881 
00882 /*
00883  * Look the various language-specific places where a file could exist.
00884  */
00885 int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
00886 {
00887    char *buf;
00888    int buflen;
00889 
00890    if (preflang == NULL)
00891       preflang = "";
00892    buflen = strlen(preflang) + strlen(filename) + 4;  /* room for everything */
00893    buf = alloca(buflen);
00894    if (buf == NULL)
00895       return 0;
00896    return fileexists_core(filename, fmt, preflang, buf, buflen);
00897 }
00898 
00899 int ast_filedelete(const char *filename, const char *fmt)
00900 {
00901    return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
00902 }
00903 
00904 int ast_filerename(const char *filename, const char *filename2, const char *fmt)
00905 {
00906    return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
00907 }
00908 
00909 int ast_filecopy(const char *filename, const char *filename2, const char *fmt)
00910 {
00911    return ast_filehelper(filename, filename2, fmt, ACTION_COPY);
00912 }
00913 
00914 int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
00915 {
00916    struct ast_filestream *fs;
00917    struct ast_filestream *vfs=NULL;
00918    char fmt[256];
00919    int seekattempt;
00920    int res;
00921 
00922    fs = ast_openstream(chan, filename, preflang);
00923    if (!fs) {
00924       ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", filename, ast_getformatname_multiple(fmt, sizeof(fmt), chan->nativeformats), strerror(errno));
00925       return -1;
00926    }
00927 
00928    /* check to see if there is any data present (not a zero length file),
00929     * done this way because there is no where for ast_openstream_full to
00930     * return the file had no data. */
00931    seekattempt = fseek(fs->f, -1, SEEK_END);
00932    if (!seekattempt)
00933       ast_seekstream(fs, 0, SEEK_SET);
00934    else
00935       return 0;
00936 
00937    vfs = ast_openvstream(chan, filename, preflang);
00938    if (vfs) {
00939       ast_debug(1, "Ooh, found a video stream, too, format %s\n", ast_getformatname(vfs->fmt->format));
00940    }
00941 
00942    if (ast_test_flag(chan, AST_FLAG_MASQ_NOSTREAM))
00943       fs->orig_chan_name = ast_strdup(chan->name);
00944    if (ast_applystream(chan, fs))
00945       return -1;
00946    if (vfs && ast_applystream(chan, vfs))
00947       return -1;
00948    res = ast_playstream(fs);
00949    if (!res && vfs)
00950       res = ast_playstream(vfs);
00951    ast_verb(3, "<%s> Playing '%s.%s' (language '%s')\n", chan->name, filename, ast_getformatname(chan->writeformat), preflang ? preflang : "default");
00952 
00953    return res;
00954 }
00955 
00956 struct ast_filestream *ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
00957 {
00958    FILE *bfile;
00959    struct ast_format *f;
00960    struct ast_filestream *fs = NULL;
00961    char *fn;
00962    int format_found = 0;   
00963 
00964    AST_RWLIST_RDLOCK(&formats);
00965 
00966    AST_RWLIST_TRAVERSE(&formats, f, list) {
00967       fs = NULL;
00968       if (!exts_compare(f->exts, type))
00969          continue;
00970       else 
00971          format_found = 1;
00972 
00973       fn = build_filename(filename, type);
00974       errno = 0;
00975       bfile = fopen(fn, "r");
00976 
00977       if (!bfile || (fs = get_filestream(f, bfile)) == NULL || open_wrapper(fs) ) {
00978          ast_log(LOG_WARNING, "Unable to open %s\n", fn);
00979          if (fs) {
00980             ast_closestream(fs);
00981          }
00982          fs = NULL;
00983          bfile = NULL;
00984          ast_free(fn);
00985          break;            
00986       }
00987       /* found it */
00988       fs->trans = NULL;
00989       fs->fmt = f;
00990       fs->flags = flags;
00991       fs->mode = mode;
00992       fs->filename = ast_strdup(filename);
00993       fs->vfs = NULL;
00994       break;
00995    }
00996 
00997    AST_RWLIST_UNLOCK(&formats);
00998    if (!format_found)
00999       ast_log(LOG_WARNING, "No such format '%s'\n", type);
01000 
01001    return fs;
01002 }
01003 
01004 struct ast_filestream *ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
01005 {
01006    int fd, myflags = 0;
01007    /* compiler claims this variable can be used before initialization... */
01008    FILE *bfile = NULL;
01009    struct ast_format *f;
01010    struct ast_filestream *fs = NULL;
01011    char *buf = NULL;
01012    size_t size = 0;
01013    int format_found = 0;
01014 
01015    AST_RWLIST_RDLOCK(&formats);
01016 
01017    /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
01018    /* We really can't use O_APPEND as it will break WAV header updates */
01019    if (flags & O_APPEND) { 
01020       flags &= ~O_APPEND;
01021    } else {
01022       myflags = O_TRUNC;
01023    }
01024    
01025    myflags |= O_WRONLY | O_CREAT;
01026 
01027    /* XXX need to fix this - we should just do the fopen,
01028     * not open followed by fdopen()
01029     */
01030    AST_RWLIST_TRAVERSE(&formats, f, list) {
01031       char *fn, *orig_fn = NULL;
01032       if (fs)
01033          break;
01034 
01035       if (!exts_compare(f->exts, type))
01036          continue;
01037       else
01038          format_found = 1;
01039 
01040       fn = build_filename(filename, type);
01041       fd = open(fn, flags | myflags, mode);
01042       if (fd > -1) {
01043          /* fdopen() the resulting file stream */
01044          bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
01045          if (!bfile) {
01046             ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
01047             close(fd);
01048             fd = -1;
01049          }
01050       }
01051       
01052       if (ast_opt_cache_record_files && (fd > -1)) {
01053          char *c;
01054 
01055          fclose(bfile); /* this also closes fd */
01056          /*
01057            We touch orig_fn just as a place-holder so other things (like vmail) see the file is there.
01058            What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place.
01059          */
01060          orig_fn = ast_strdupa(fn);
01061          for (c = fn; *c; c++)
01062             if (*c == '/')
01063                *c = '_';
01064 
01065          size = strlen(fn) + strlen(record_cache_dir) + 2;
01066          buf = alloca(size);
01067          strcpy(buf, record_cache_dir);
01068          strcat(buf, "/");
01069          strcat(buf, fn);
01070          ast_free(fn);
01071          fn = buf;
01072          fd = open(fn, flags | myflags, mode);
01073          if (fd > -1) {
01074             /* fdopen() the resulting file stream */
01075             bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
01076             if (!bfile) {
01077                ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
01078                close(fd);
01079                fd = -1;
01080             }
01081          }
01082       }
01083       if (fd > -1) {
01084          errno = 0;
01085          fs = get_filestream(f, bfile);
01086          if (!fs || rewrite_wrapper(fs, comment)) {
01087             ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
01088             close(fd);
01089             if (orig_fn) {
01090                unlink(fn);
01091                unlink(orig_fn);
01092             }
01093             if (fs) {
01094                ast_closestream(fs);
01095                fs = NULL;
01096             }
01097             continue;
01098          }
01099          fs->trans = NULL;
01100          fs->fmt = f;
01101          fs->flags = flags;
01102          fs->mode = mode;
01103          if (orig_fn) {
01104             fs->realfilename = ast_strdup(orig_fn);
01105             fs->filename = ast_strdup(fn);
01106          } else {
01107             fs->realfilename = NULL;
01108             fs->filename = ast_strdup(filename);
01109          }
01110          fs->vfs = NULL;
01111          /* If truncated, we'll be at the beginning; if not truncated, then append */
01112 
01113          if ((fs->write_buffer = ast_malloc(32768))){
01114             setvbuf(fs->f, fs->write_buffer, _IOFBF, 32768);
01115          }
01116 
01117          f->seek(fs, 0, SEEK_END);
01118       } else if (errno != EEXIST) {
01119          ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
01120          if (orig_fn)
01121             unlink(orig_fn);
01122       }
01123       /* if buf != NULL then fn is already free and pointing to it */
01124       if (!buf)
01125          ast_free(fn);
01126    }
01127 
01128    AST_RWLIST_UNLOCK(&formats);
01129 
01130    if (!format_found)
01131       ast_log(LOG_WARNING, "No such format '%s'\n", type);
01132 
01133    return fs;
01134 }
01135 
01136 /*!
01137  * \brief the core of all waitstream() functions
01138  */
01139 static int waitstream_core(struct ast_channel *c, const char *breakon,
01140    const char *forward, const char *reverse, int skip_ms,
01141    int audiofd, int cmdfd,  const char *context)
01142 {
01143    const char *orig_chan_name = NULL;
01144    int err = 0;
01145 
01146    if (!breakon)
01147       breakon = "";
01148    if (!forward)
01149       forward = "";
01150    if (!reverse)
01151       reverse = "";
01152 
01153    /* Switch the channel to end DTMF frame only. waitstream_core doesn't care about the start of DTMF. */
01154    ast_set_flag(c, AST_FLAG_END_DTMF_ONLY);
01155 
01156    if (ast_test_flag(c, AST_FLAG_MASQ_NOSTREAM))
01157       orig_chan_name = ast_strdupa(c->name);
01158 
01159    while (c->stream) {
01160       int res;
01161       int ms;
01162 
01163       if (orig_chan_name && strcasecmp(orig_chan_name, c->name)) {
01164          ast_stopstream(c);
01165          err = 1;
01166          break;
01167       }
01168 
01169       ms = ast_sched_wait(c->sched);
01170 
01171       if (ms < 0 && !c->timingfunc) {
01172          ast_stopstream(c);
01173          break;
01174       }
01175       if (ms < 0)
01176          ms = 1000;
01177       if (cmdfd < 0) {
01178          res = ast_waitfor(c, ms);
01179          if (res < 0) {
01180             ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
01181             ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
01182             return res;
01183          }
01184       } else {
01185          int outfd;
01186          struct ast_channel *rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
01187          if (!rchan && (outfd < 0) && (ms)) {
01188             /* Continue */
01189             if (errno == EINTR)
01190                continue;
01191             ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
01192             ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
01193             return -1;
01194          } else if (outfd > -1) { /* this requires cmdfd set */
01195             /* The FD we were watching has something waiting */
01196             ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
01197             return 1;
01198          }
01199          /* if rchan is set, it is 'c' */
01200          res = rchan ? 1 : 0; /* map into 'res' values */
01201       }
01202       if (res > 0) {
01203          struct ast_frame *fr = ast_read(c);
01204          if (!fr) {
01205             ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
01206             return -1;
01207          }
01208          switch (fr->frametype) {
01209          case AST_FRAME_DTMF_END:
01210             if (context) {
01211                const char exten[2] = { fr->subclass, '\0' };
01212                if (ast_exists_extension(c, context, exten, 1, c->cid.cid_num)) {
01213                   res = fr->subclass;
01214                   ast_frfree(fr);
01215                   ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
01216                   return res;
01217                }
01218             } else {
01219                res = fr->subclass;
01220                if (strchr(forward, res)) {
01221                   ast_stream_fastforward(c->stream, skip_ms);
01222                } else if (strchr(reverse, res)) {
01223                   ast_stream_rewind(c->stream, skip_ms);
01224                } else if (strchr(breakon, res)) {
01225                   ast_frfree(fr);
01226                   ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
01227                   return res;
01228                }              
01229             }
01230             break;
01231          case AST_FRAME_CONTROL:
01232             switch (fr->subclass) {
01233             case AST_CONTROL_HANGUP:
01234             case AST_CONTROL_BUSY:
01235             case AST_CONTROL_CONGESTION:
01236                ast_frfree(fr);
01237                ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
01238                return -1;
01239             case AST_CONTROL_RINGING:
01240             case AST_CONTROL_ANSWER:
01241             case AST_CONTROL_VIDUPDATE:
01242             case AST_CONTROL_SRCUPDATE:
01243             case AST_CONTROL_HOLD:
01244             case AST_CONTROL_UNHOLD:
01245             case -1:
01246                /* Unimportant */
01247                break;
01248             default:
01249                ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
01250             }
01251             break;
01252          case AST_FRAME_VOICE:
01253             /* Write audio if appropriate */
01254             if (audiofd > -1) {
01255                if (write(audiofd, fr->data.ptr, fr->datalen) < 0) {
01256                   ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
01257                }
01258             }
01259          default:
01260             /* Ignore all others */
01261             break;
01262          }
01263          ast_frfree(fr);
01264       }
01265       ast_sched_runq(c->sched);
01266    }
01267 
01268    ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
01269 
01270    return (err || c->_softhangup) ? -1 : 0;
01271 }
01272 
01273 int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *reverse, int ms)
01274 {
01275    return waitstream_core(c, breakon, forward, reverse, ms,
01276       -1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */);
01277 }
01278 
01279 int ast_waitstream(struct ast_channel *c, const char *breakon)
01280 {
01281    return waitstream_core(c, breakon, NULL, NULL, 0, -1, -1, NULL);
01282 }
01283 
01284 int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd)
01285 {
01286    return waitstream_core(c, breakon, NULL, NULL, 0,
01287       audiofd, cmdfd, NULL /* no context */);
01288 }
01289 
01290 int ast_waitstream_exten(struct ast_channel *c, const char *context)
01291 {
01292    /* Waitstream, with return in the case of a valid 1 digit extension */
01293    /* in the current or specified context being pressed */
01294 
01295    if (!context)
01296       context = c->context;
01297    return waitstream_core(c, NULL, NULL, NULL, 0,
01298       -1, -1, context);
01299 }
01300 
01301 void ast_filestream_frame_freed(struct ast_frame *fr)
01302 {
01303    struct ast_filestream *fs;
01304 
01305    ast_clear_flag(fr, AST_FRFLAG_FROM_FILESTREAM);
01306 
01307    fs = (struct ast_filestream *) (((char *) fr) - offsetof(struct ast_filestream, fr));
01308 
01309    ao2_ref(fs, -1);
01310 }
01311 
01312 /*
01313  * if the file name is non-empty, try to play it.
01314  * Return 0 if success, -1 if error, digit if interrupted by a digit.
01315  * If digits == "" then we can simply check for non-zero.
01316  */
01317 int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
01318 {
01319    int res = 0;
01320    if (!ast_strlen_zero(file)) {
01321       res = ast_streamfile(chan, file, chan->language);
01322       if (!res) {
01323          res = ast_waitstream(chan, digits);
01324       }
01325    }
01326    return res;
01327 } 
01328 
01329 static char *handle_cli_core_show_file_formats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01330 {
01331 #define FORMAT "%-10s %-10s %-20s\n"
01332 #define FORMAT2 "%-10s %-10s %-20s\n"
01333    struct ast_format *f;
01334    int count_fmt = 0;
01335 
01336    switch (cmd) {
01337    case CLI_INIT:
01338       e->command = "core show file formats";
01339       e->usage =
01340          "Usage: core show file formats\n"
01341          "       Displays currently registered file formats (if any).\n";
01342       return NULL;
01343    case CLI_GENERATE:
01344       return NULL;
01345    }
01346 
01347    if (a->argc != 4)
01348       return CLI_SHOWUSAGE;
01349 
01350    ast_cli(a->fd, FORMAT, "Format", "Name", "Extensions");
01351    ast_cli(a->fd, FORMAT, "------", "----", "----------");
01352 
01353    AST_RWLIST_RDLOCK(&formats);
01354    AST_RWLIST_TRAVERSE(&formats, f, list) {
01355       ast_cli(a->fd, FORMAT2, ast_getformatname(f->format), f->name, f->exts);
01356       count_fmt++;
01357    }
01358    AST_RWLIST_UNLOCK(&formats);
01359    ast_cli(a->fd, "%d file formats registered.\n", count_fmt);
01360    return CLI_SUCCESS;
01361 #undef FORMAT
01362 #undef FORMAT2
01363 }
01364 
01365 struct ast_cli_entry cli_file[] = {
01366    AST_CLI_DEFINE(handle_cli_core_show_file_formats, "Displays file formats")
01367 };
01368 
01369 int ast_file_init(void)
01370 {
01371    ast_cli_register_multiple(cli_file, sizeof(cli_file) / sizeof(struct ast_cli_entry));
01372    return 0;
01373 }

Generated on Fri Jun 19 12:09:44 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7