Sat Feb 11 06:33:15 2012

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

Generated on Sat Feb 11 06:33:15 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6