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

Generated on Fri Sep 25 19:28:11 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.5