Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

file.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Generic File Format Support.
00005  * 
00006  * Copyright (C) 1999, Mark Spencer
00007  *
00008  * Mark Spencer <markster@linux-support.net>
00009  *
00010  * This program is free software, distributed under the terms of
00011  * the GNU General Public License
00012  */
00013 
00014 #include <sys/types.h>
00015 #include <asterisk/frame.h>
00016 #include <asterisk/file.h>
00017 #include <asterisk/cli.h>
00018 #include <asterisk/logger.h>
00019 #include <asterisk/channel.h>
00020 #include <asterisk/sched.h>
00021 #include <asterisk/options.h>
00022 #include <asterisk/translate.h>
00023 #include <asterisk/utils.h>
00024 #include <asterisk/lock.h>
00025 #include <asterisk/app.h>
00026 #include <errno.h>
00027 #include <unistd.h>
00028 #include <stdlib.h>
00029 #include <string.h>
00030 #include <stdio.h>
00031 #include <fcntl.h>
00032 #include <dirent.h>
00033 #include <sys/types.h>
00034 #include <sys/stat.h>
00035 #include "asterisk.h"
00036 #include "astconf.h"
00037 
00038 struct ast_format {
00039    /* Name of format */
00040    char name[80];
00041    /* Extensions (separated by | if more than one) 
00042       this format can read.  First is assumed for writing (e.g. .mp3) */
00043    char exts[80];
00044    /* Format of frames it uses/provides (one only) */
00045    int format;
00046    /* Open an input stream, and start playback */
00047    struct ast_filestream * (*open)(int fd);
00048    /* Open an output stream, of a given file descriptor and comment it appropriately if applicable */
00049    struct ast_filestream * (*rewrite)(int fd, char *comment);
00050    /* Write a frame to a channel */
00051    int (*write)(struct ast_filestream *, struct ast_frame *);
00052    /* seek num samples into file, whence(think normal seek) */
00053    int (*seek)(struct ast_filestream *, long offset, int whence);
00054    /* trunc file to current position */
00055    int (*trunc)(struct ast_filestream *fs);
00056    /* tell current position */
00057    long (*tell)(struct ast_filestream *fs);
00058    /* Read the next frame from the filestream (if available) and report when to get next one
00059       (in samples) */
00060    struct ast_frame * (*read)(struct ast_filestream *, int *whennext);
00061    /* Close file, and destroy filestream structure */
00062    void (*close)(struct ast_filestream *);
00063    /* Retrieve file comment */
00064    char * (*getcomment)(struct ast_filestream *);
00065    /* Link */
00066    struct ast_format *next;
00067 };
00068 
00069 struct ast_filestream {
00070    /* Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */
00071    struct ast_format *fmt;
00072    int flags;
00073    mode_t mode;
00074    char *filename;
00075    char *realfilename;
00076    /* Video file stream */
00077    struct ast_filestream *vfs;
00078    /* Transparently translate from another format -- just once */
00079    struct ast_trans_pvt *trans;
00080    struct ast_tranlator_pvt *tr;
00081    int lastwriteformat;
00082    int lasttimeout;
00083    struct ast_channel *owner;
00084 };
00085 
00086 AST_MUTEX_DEFINE_STATIC(formatlock);
00087 
00088 static struct ast_format *formats = NULL;
00089 
00090 int ast_format_register(char *name, char *exts, int format,
00091                   struct ast_filestream * (*open)(int fd),
00092                   struct ast_filestream * (*rewrite)(int fd, char *comment),
00093                   int (*write)(struct ast_filestream *, struct ast_frame *),
00094                   int (*seek)(struct ast_filestream *, long sample_offset, int whence),
00095                   int (*trunc)(struct ast_filestream *),
00096                   long (*tell)(struct ast_filestream *),
00097                   struct ast_frame * (*read)(struct ast_filestream *, int *whennext),
00098                   void (*close)(struct ast_filestream *),
00099                   char * (*getcomment)(struct ast_filestream *))
00100 {
00101    struct ast_format *tmp;
00102    if (ast_mutex_lock(&formatlock)) {
00103       ast_log(LOG_WARNING, "Unable to lock format list\n");
00104       return -1;
00105    }
00106    tmp = formats;
00107    while(tmp) {
00108       if (!strcasecmp(name, tmp->name)) {
00109          ast_mutex_unlock(&formatlock);
00110          ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", name);
00111          return -1;
00112       }
00113       tmp = tmp->next;
00114    }
00115    tmp = malloc(sizeof(struct ast_format));
00116    if (!tmp) {
00117       ast_log(LOG_WARNING, "Out of memory\n");
00118       ast_mutex_unlock(&formatlock);
00119       return -1;
00120    }
00121    strncpy(tmp->name, name, sizeof(tmp->name)-1);
00122    strncpy(tmp->exts, exts, sizeof(tmp->exts)-1);
00123    tmp->open = open;
00124    tmp->rewrite = rewrite;
00125    tmp->read = read;
00126    tmp->write = write;
00127    tmp->seek = seek;
00128    tmp->trunc = trunc;
00129    tmp->tell = tell;
00130    tmp->close = close;
00131    tmp->format = format;
00132    tmp->getcomment = getcomment;
00133    tmp->next = formats;
00134    formats = tmp;
00135    ast_mutex_unlock(&formatlock);
00136    if (option_verbose > 1)
00137       ast_verbose( VERBOSE_PREFIX_2 "Registered file format %s, extension(s) %s\n", name, exts);
00138    return 0;
00139 }
00140 
00141 int ast_format_unregister(char *name)
00142 {
00143    struct ast_format *tmp, *tmpl = NULL;
00144    if (ast_mutex_lock(&formatlock)) {
00145       ast_log(LOG_WARNING, "Unable to lock format list\n");
00146       return -1;
00147    }
00148    tmp = formats;
00149    while(tmp) {
00150       if (!strcasecmp(name, tmp->name)) {
00151          if (tmpl) 
00152             tmpl->next = tmp->next;
00153          else
00154             formats = tmp->next;
00155          free(tmp);
00156          ast_mutex_unlock(&formatlock);
00157          if (option_verbose > 1)
00158             ast_verbose( VERBOSE_PREFIX_2 "Unregistered format %s\n", name);
00159          return 0;
00160       }
00161       tmpl = tmp;
00162       tmp = tmp->next;
00163    }
00164    ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name);
00165    return -1;
00166 }
00167 
00168 int ast_stopstream(struct ast_channel *tmp)
00169 {
00170    /* Stop a running stream if there is one */
00171    if (tmp->vstream)
00172       ast_closestream(tmp->vstream);
00173    if (tmp->stream) {
00174       ast_closestream(tmp->stream);
00175       if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
00176          ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
00177    }
00178    return 0;
00179 }
00180 
00181 int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
00182 {
00183    struct ast_frame *trf;
00184    int res = -1;
00185    int alt=0;
00186    if (f->frametype == AST_FRAME_VIDEO) {
00187       if (fs->fmt->format < AST_FORMAT_MAX_AUDIO) {
00188          /* This is the audio portion.  Call the video one... */
00189          if (!fs->vfs && fs->filename) {
00190             /* XXX Support other video formats XXX */
00191             char *type = "h263";
00192             fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode);
00193             ast_log(LOG_DEBUG, "Opened video output file\n");
00194          }
00195          if (fs->vfs)
00196             return ast_writestream(fs->vfs, f);
00197          /* Ignore */
00198          return 0;            
00199       } else {
00200          /* Might / might not have mark set */
00201          alt = 1;
00202       }
00203    } else if (f->frametype != AST_FRAME_VOICE) {
00204       ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
00205       return -1;
00206    }
00207    if (((fs->fmt->format | alt) & f->subclass) == f->subclass) {
00208       res =  fs->fmt->write(fs, f);
00209       if (res < 0) 
00210          ast_log(LOG_WARNING, "Natural write failed\n");
00211       if (res > 0)
00212          ast_log(LOG_WARNING, "Huh??\n");
00213       return res;
00214    } else {
00215       /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
00216              the one we've setup a translator for, we do the "wrong thing" XXX */
00217       if (fs->trans && (f->subclass != fs->lastwriteformat)) {
00218          ast_translator_free_path(fs->trans);
00219          fs->trans = NULL;
00220       }
00221       if (!fs->trans) 
00222          fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass);
00223       if (!fs->trans)
00224          ast_log(LOG_WARNING, "Unable to translate to format %s, source format %s\n", fs->fmt->name, ast_getformatname(f->subclass));
00225       else {
00226          fs->lastwriteformat = f->subclass;
00227          res = 0;
00228          /* Get the translated frame but don't consume the original in case they're using it on another stream */
00229          trf = ast_translate(fs->trans, f, 0);
00230          if (trf) {
00231             res = fs->fmt->write(fs, trf);
00232             if (res) 
00233                ast_log(LOG_WARNING, "Translated frame write failed\n");
00234          } else
00235             res = 0;
00236       }
00237       return res;
00238    }
00239 }
00240 
00241 static int copy(char *infile, char *outfile)
00242 {
00243    int ifd;
00244    int ofd;
00245    int res;
00246    int len;
00247    char buf[4096];
00248 
00249    if ((ifd = open(infile, O_RDONLY)) < 0) {
00250       ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
00251       return -1;
00252    }
00253    if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
00254       ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
00255       close(ifd);
00256       return -1;
00257    }
00258    do {
00259       len = read(ifd, buf, sizeof(buf));
00260       if (len < 0) {
00261          ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
00262          close(ifd);
00263          close(ofd);
00264          unlink(outfile);
00265       }
00266       if (len) {
00267          res = write(ofd, buf, len);
00268          if (res != len) {
00269             ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
00270             close(ifd);
00271             close(ofd);
00272             unlink(outfile);
00273          }
00274       }
00275    } while(len);
00276    close(ifd);
00277    close(ofd);
00278    return 0;
00279 }
00280 
00281 static char *build_filename(char *filename, char *ext)
00282 {
00283    char *fn;
00284    int fnsize = 0;
00285    char tmp[AST_CONFIG_MAX_PATH]="";
00286 
00287    snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_DATA_DIR, "sounds");
00288    fnsize = strlen(tmp) + strlen(filename) + strlen(ext) + 10;
00289    fn = malloc(fnsize);
00290    if (fn) {
00291       if (filename[0] == '/') 
00292          snprintf(fn, fnsize, "%s.%s", filename, ext);
00293       else
00294          snprintf(fn, fnsize, "%s/%s.%s", tmp, filename, ext);
00295    }
00296    return fn;
00297    
00298 }
00299 
00300 static int exts_compare(char *exts, char *type)
00301 {
00302    char *stringp = NULL, *ext;
00303    char tmp[256];
00304 
00305    strncpy(tmp, exts, sizeof(tmp) - 1);
00306    stringp = tmp;
00307    while ((ext = strsep(&stringp, "|"))) {
00308       if (!strcmp(ext, type)) {
00309          return 1;
00310       }
00311    }
00312 
00313    return 0;
00314 }
00315 
00316 #define ACTION_EXISTS 1
00317 #define ACTION_DELETE 2
00318 #define ACTION_RENAME 3
00319 #define ACTION_OPEN   4
00320 #define ACTION_COPY   5
00321 
00322 static int ast_filehelper(char *filename, char *filename2, char *fmt, int action)
00323 {
00324    struct stat st;
00325    struct ast_format *f;
00326    struct ast_filestream *s;
00327    int res=0, ret = 0;
00328    char *ext=NULL, *exts, *fn, *nfn;
00329    struct ast_channel *chan = (struct ast_channel *)filename2;
00330    
00331    /* Start with negative response */
00332    if (action == ACTION_EXISTS)
00333       res = 0;
00334    else
00335       res = -1;
00336    if (action == ACTION_OPEN)
00337       ret = -1;
00338    /* Check for a specific format */
00339    if (ast_mutex_lock(&formatlock)) {
00340       ast_log(LOG_WARNING, "Unable to lock format list\n");
00341       if (action == ACTION_EXISTS)
00342          return 0;
00343       else
00344          return -1;
00345    }
00346    f = formats;
00347    while(f) {
00348       if (!fmt || exts_compare(f->exts, fmt)) {
00349          char *stringp=NULL;
00350          exts = strdup(f->exts);
00351          /* Try each kind of extension */
00352          stringp=exts;
00353          ext = strsep(&stringp, "|");
00354          if (!strcmp(ext,"wav49")) {
00355             ext = "WAV";
00356          }
00357          do {
00358             fn = build_filename(filename, ext);
00359             if (fn) {
00360                res = stat(fn, &st);
00361                if (!res) {
00362                   switch(action) {
00363                   case ACTION_EXISTS:
00364                      ret |= f->format;
00365                      break;
00366                   case ACTION_DELETE:
00367                      res = unlink(fn);
00368                      if (res)
00369                         ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
00370                      break;
00371                   case ACTION_RENAME:
00372                      nfn = build_filename(filename2, ext);
00373                      if (nfn) {
00374                         res = rename(fn, nfn);
00375                         if (res)
00376                            ast_log(LOG_WARNING, "rename(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
00377                         free(nfn);
00378                      } else
00379                         ast_log(LOG_WARNING, "Out of memory\n");
00380                      break;
00381                   case ACTION_COPY:
00382                      nfn = build_filename(filename2, ext);
00383                      if (nfn) {
00384                         res = copy(fn, nfn);
00385                         if (res)
00386                            ast_log(LOG_WARNING, "copy(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
00387                         free(nfn);
00388                      } else
00389                         ast_log(LOG_WARNING, "Out of memory\n");
00390                      break;
00391                   case ACTION_OPEN:
00392                      if ((ret < 0) && ((chan->writeformat & f->format) ||
00393                               ((f->format >= AST_FORMAT_MAX_AUDIO) && fmt))) {
00394                         ret = open(fn, O_RDONLY);
00395                         if (ret >= 0) {
00396                            s = f->open(ret);
00397                            if (s) {
00398                               s->lasttimeout = -1;
00399                               s->fmt = f;
00400                               s->trans = NULL;
00401                               s->filename = NULL;
00402                               if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
00403                                  chan->stream = s;
00404                               else
00405                                  chan->vstream = s;
00406                            } else {
00407                               close(ret);
00408                               ast_log(LOG_WARNING, "Unable to open fd on %s\n", fn);
00409                            }
00410                         } else
00411                            ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
00412                      }
00413                      break;
00414                   default:
00415                      ast_log(LOG_WARNING, "Unknown helper %d\n", action);
00416                   }
00417                   /* Conveniently this logic is the same for all */
00418                   if (res)
00419                      break;
00420                }
00421                free(fn);
00422             }
00423             ext = strsep(&stringp, "|");
00424          } while(ext);
00425          free(exts);
00426       }
00427       f = f->next;
00428    }
00429    ast_mutex_unlock(&formatlock);
00430    if ((action == ACTION_EXISTS) || (action == ACTION_OPEN))
00431       res = ret ? ret : -1;
00432    return res;
00433 }
00434 
00435 struct ast_filestream *ast_openstream(struct ast_channel *chan, char *filename, char *preflang)
00436 {
00437    /* This is a fairly complex routine.  Essentially we should do 
00438       the following:
00439       
00440       1) Find which file handlers produce our type of format.
00441       2) Look for a filename which it can handle.
00442       3) If we find one, then great.  
00443       4) If not, see what files are there
00444       5) See what we can actually support
00445       6) Choose the one with the least costly translator path and
00446           set it up.
00447          
00448    */
00449    int fd = -1;
00450    int fmts = -1;
00451    char filename2[256]="";
00452    char filename3[256]="";
00453    char *endpart;
00454    int res;
00455    ast_stopstream(chan);
00456    /* do this first, otherwise we detect the wrong writeformat */
00457    if (chan->generator)
00458       ast_deactivate_generator(chan);
00459    if (preflang && !ast_strlen_zero(preflang)) {
00460       strncpy(filename3, filename, sizeof(filename3) - 1);
00461       endpart = strrchr(filename3, '/');
00462       if (endpart) {
00463          *endpart = '\0';
00464          endpart++;
00465          snprintf(filename2, sizeof(filename2), "%s/%s/%s", filename3, preflang, endpart);
00466       } else
00467          snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
00468       fmts = ast_fileexists(filename2, NULL, NULL);
00469    }
00470    if (fmts < 1) {
00471       strncpy(filename2, filename, sizeof(filename2)-1);
00472       fmts = ast_fileexists(filename2, NULL, NULL);
00473    }
00474    if (fmts < 1) {
00475       ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
00476       return NULL;
00477    }
00478    chan->oldwriteformat = chan->writeformat;
00479    /* Set the channel to a format we can work with */
00480    res = ast_set_write_format(chan, fmts);
00481    
00482    fd = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
00483    if (fd >= 0)
00484       return chan->stream;
00485    return NULL;
00486 }
00487 
00488 struct ast_filestream *ast_openvstream(struct ast_channel *chan, char *filename, char *preflang)
00489 {
00490    /* This is a fairly complex routine.  Essentially we should do 
00491       the following:
00492       
00493       1) Find which file handlers produce our type of format.
00494       2) Look for a filename which it can handle.
00495       3) If we find one, then great.  
00496       4) If not, see what files are there
00497       5) See what we can actually support
00498       6) Choose the one with the least costly translator path and
00499           set it up.
00500          
00501    */
00502    int fd = -1;
00503    int fmts = -1;
00504    char filename2[256];
00505    char lang2[MAX_LANGUAGE];
00506    /* XXX H.263 only XXX */
00507    char *fmt = "h263";
00508    if (preflang && !ast_strlen_zero(preflang)) {
00509       snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
00510       fmts = ast_fileexists(filename2, fmt, NULL);
00511       if (fmts < 1) {
00512          strncpy(lang2, preflang, sizeof(lang2)-1);
00513          snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename);
00514          fmts = ast_fileexists(filename2, fmt, NULL);
00515       }
00516    }
00517    if (fmts < 1) {
00518       strncpy(filename2, filename, sizeof(filename2)-1);
00519       fmts = ast_fileexists(filename2, fmt, NULL);
00520    }
00521    if (fmts < 1) {
00522       return NULL;
00523    }
00524    fd = ast_filehelper(filename2, (char *)chan, fmt, ACTION_OPEN);
00525    if (fd >= 0)
00526       return chan->vstream;
00527    ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename);
00528    return NULL;
00529 }
00530 
00531 struct ast_frame *ast_readframe(struct ast_filestream *s)
00532 {
00533    struct ast_frame *f = NULL;
00534    int whennext = 0; 
00535    if (s && s->fmt)
00536       f = s->fmt->read(s, &whennext);
00537    return f;
00538 }
00539 
00540 static int ast_readaudio_callback(void *data)
00541 {
00542    struct ast_filestream *s = data;
00543    struct ast_frame *fr;
00544    int whennext = 0;
00545 
00546    while(!whennext) {
00547       fr = s->fmt->read(s, &whennext);
00548       if (fr) {
00549          if (ast_write(s->owner, fr)) {
00550             ast_log(LOG_WARNING, "Failed to write frame\n");
00551             s->owner->streamid = -1;
00552 #ifdef ZAPTEL_OPTIMIZATIONS
00553             ast_settimeout(s->owner, 0, NULL, NULL);
00554 #endif         
00555             return 0;
00556          }
00557       } else {
00558          /* Stream has finished */
00559          s->owner->streamid = -1;
00560 #ifdef ZAPTEL_OPTIMIZATIONS
00561          ast_settimeout(s->owner, 0, NULL, NULL);
00562 #endif         
00563          return 0;
00564       }
00565    }
00566    if (whennext != s->lasttimeout) {
00567 #ifdef ZAPTEL_OPTIMIZATIONS
00568       if (s->owner->timingfd > -1)
00569          ast_settimeout(s->owner, whennext, ast_readaudio_callback, s);
00570       else
00571 #endif      
00572          s->owner->streamid = ast_sched_add(s->owner->sched, whennext/8, ast_readaudio_callback, s);
00573       s->lasttimeout = whennext;
00574       return 0;
00575    }
00576    return 1;
00577 }
00578 
00579 static int ast_readvideo_callback(void *data)
00580 {
00581    struct ast_filestream *s = data;
00582    struct ast_frame *fr;
00583    int whennext = 0;
00584 
00585    while(!whennext) {
00586       fr = s->fmt->read(s, &whennext);
00587       if (fr) {
00588          if (ast_write(s->owner, fr)) {
00589             ast_log(LOG_WARNING, "Failed to write frame\n");
00590             s->owner->vstreamid = -1;
00591             return 0;
00592          }
00593       } else {
00594          /* Stream has finished */
00595          s->owner->vstreamid = -1;
00596          return 0;
00597       }
00598    }
00599    if (whennext != s->lasttimeout) {
00600       s->owner->vstreamid = ast_sched_add(s->owner->sched, whennext/8, ast_readvideo_callback, s);
00601       s->lasttimeout = whennext;
00602       return 0;
00603    }
00604    return 1;
00605 }
00606 
00607 int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
00608 {
00609    s->owner = chan;
00610    return 0;
00611 }
00612 
00613 int ast_playstream(struct ast_filestream *s)
00614 {
00615    if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
00616       ast_readaudio_callback(s);
00617    else
00618       ast_readvideo_callback(s);
00619    return 0;
00620 }
00621 
00622 int ast_seekstream(struct ast_filestream *fs, long sample_offset, int whence)
00623 {
00624    return fs->fmt->seek(fs, sample_offset, whence);
00625 }
00626 
00627 int ast_truncstream(struct ast_filestream *fs)
00628 {
00629    return fs->fmt->trunc(fs);
00630 }
00631 
00632 long ast_tellstream(struct ast_filestream *fs)
00633 {
00634    return fs->fmt->tell(fs);
00635 }
00636 
00637 int ast_stream_fastforward(struct ast_filestream *fs, long ms)
00638 {
00639    /* I think this is right, 8000 samples per second, 1000 ms a second so 8
00640     * samples per ms  */
00641    long samples = ms * 8;
00642    return ast_seekstream(fs, samples, SEEK_CUR);
00643 }
00644 
00645 int ast_stream_rewind(struct ast_filestream *fs, long ms)
00646 {
00647    long samples = ms * 8;
00648    samples = samples * -1;
00649    return ast_seekstream(fs, samples, SEEK_CUR);
00650 }
00651 
00652 int ast_closestream(struct ast_filestream *f)
00653 {
00654    char *cmd = NULL;
00655    size_t size = 0;
00656    /* Stop a running stream if there is one */
00657    if (f->owner) {
00658       if (f->fmt->format < AST_FORMAT_MAX_AUDIO) {
00659          f->owner->stream = NULL;
00660          if (f->owner->streamid > -1)
00661             ast_sched_del(f->owner->sched, f->owner->streamid);
00662          f->owner->streamid = -1;
00663 #ifdef ZAPTEL_OPTIMIZATIONS
00664          ast_settimeout(f->owner, 0, NULL, NULL);
00665 #endif         
00666       } else {
00667          f->owner->vstream = NULL;
00668          if (f->owner->vstreamid > -1)
00669             ast_sched_del(f->owner->sched, f->owner->vstreamid);
00670          f->owner->vstreamid = -1;
00671       }
00672    }
00673    /* destroy the translator on exit */
00674    if (f->trans) {
00675       ast_translator_free_path(f->trans);
00676       f->trans = NULL;
00677    }
00678 
00679    if (f->realfilename && f->filename) {
00680          size = strlen(f->filename) + strlen(f->realfilename) + 15;
00681          cmd = alloca(size);
00682          memset(cmd,0,size);
00683          snprintf(cmd,size,"/bin/mv -f %s %s",f->filename,f->realfilename);
00684          ast_safe_system(cmd);
00685    }
00686 
00687    if (f->filename) {
00688       free(f->filename);
00689       f->filename = NULL;
00690    }
00691    if (f->realfilename) {
00692       free(f->realfilename);
00693       f->realfilename = NULL;
00694    }
00695    f->fmt->close(f);
00696    return 0;
00697 }
00698 
00699 
00700 int ast_fileexists(char *filename, char *fmt, char *preflang)
00701 {
00702    char filename2[256];
00703    char tmp[256];
00704    char *postfix;
00705    char *prefix;
00706    char *c;
00707    char lang2[MAX_LANGUAGE];
00708    int res = -1;
00709    if (preflang && !ast_strlen_zero(preflang)) {
00710       /* Insert the language between the last two parts of the path */
00711       strncpy(tmp, filename, sizeof(tmp) - 1);
00712       c = strrchr(tmp, '/');
00713       if (c) {
00714          *c = '\0';
00715          postfix = c+1;
00716          prefix = tmp;
00717       } else {
00718          postfix = tmp;
00719          prefix="";
00720       }
00721       snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, preflang, postfix);
00722       res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
00723       if (res < 1) {
00724          char *stringp=NULL;
00725          strncpy(lang2, preflang, sizeof(lang2)-1);
00726          stringp=lang2;
00727          strsep(&stringp, "_");
00728          if (strcmp(lang2, preflang)) {
00729             snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, lang2, postfix);
00730             res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
00731          }
00732       }
00733    }
00734    if (res < 1) {
00735       res = ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
00736    }
00737    return res;
00738 }
00739 
00740 int ast_filedelete(char *filename, char *fmt)
00741 {
00742    return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
00743 }
00744 
00745 int ast_filerename(char *filename, char *filename2, char *fmt)
00746 {
00747    return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
00748 }
00749 
00750 int ast_filecopy(char *filename, char *filename2, char *fmt)
00751 {
00752    return ast_filehelper(filename, filename2, fmt, ACTION_COPY);
00753 }
00754 
00755 int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang)
00756 {
00757    struct ast_filestream *fs;
00758    struct ast_filestream *vfs;
00759 
00760    fs = ast_openstream(chan, filename, preflang);
00761    vfs = ast_openvstream(chan, filename, preflang);
00762    if (vfs)
00763       ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n");
00764    if (fs){
00765       if (ast_applystream(chan, fs))
00766          return -1;
00767       if (vfs && ast_applystream(chan, vfs))
00768          return -1;
00769       if (ast_playstream(fs))
00770          return -1;
00771       if (vfs && ast_playstream(vfs))
00772          return -1;
00773 #if 1
00774       if (option_verbose > 2)
00775          ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (language '%s')\n", filename, preflang ? preflang : "default");
00776 #endif
00777       return 0;
00778    }
00779    ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", filename, ast_getformatname(chan->nativeformats), strerror(errno));
00780    return -1;
00781 }
00782 
00783 struct ast_filestream *ast_readfile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
00784 {
00785    int fd,myflags = 0;
00786    struct ast_format *f;
00787    struct ast_filestream *fs=NULL;
00788    char *fn;
00789    char *ext;
00790    if (ast_mutex_lock(&formatlock)) {
00791       ast_log(LOG_WARNING, "Unable to lock format list\n");
00792       return NULL;
00793    }
00794    f = formats;
00795    while(f) {
00796       if (exts_compare(f->exts, type)) {
00797          char *stringp=NULL;
00798          /* XXX Implement check XXX */
00799          ext = strdup(f->exts);
00800          stringp=ext;
00801          ext = strsep(&stringp, "|");
00802          fn = build_filename(filename, ext);
00803          fd = open(fn, flags | myflags);
00804          if (fd >= 0) {
00805             errno = 0;
00806             if ((fs = f->open(fd))) {
00807                fs->trans = NULL;
00808                fs->fmt = f;
00809                fs->flags = flags;
00810                fs->mode = mode;
00811                fs->filename = strdup(filename);
00812                fs->vfs = NULL;
00813             } else {
00814                ast_log(LOG_WARNING, "Unable to open %s\n", fn);
00815                close(fd);
00816                unlink(fn);
00817             }
00818          } else if (errno != EEXIST)
00819             ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
00820          free(fn);
00821          free(ext);
00822          break;
00823       }
00824       f = f->next;
00825    }
00826    ast_mutex_unlock(&formatlock);
00827    if (!f) 
00828       ast_log(LOG_WARNING, "No such format '%s'\n", type);
00829    return fs;
00830 }
00831 
00832 struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
00833 {
00834    int fd,myflags = 0;
00835    struct ast_format *f;
00836    struct ast_filestream *fs=NULL;
00837    char *fn,*orig_fn=NULL;
00838    char *ext;
00839    char *buf=NULL;
00840    size_t size = 0;
00841 
00842    if (ast_mutex_lock(&formatlock)) {
00843       ast_log(LOG_WARNING, "Unable to lock format list\n");
00844       return NULL;
00845    }
00846    /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
00847    if (flags & O_APPEND){ 
00848       /* We really can't use O_APPEND as it will break WAV header updates */
00849       flags &= ~O_APPEND;
00850    }else{
00851       myflags = O_TRUNC;
00852    }
00853    
00854    myflags |= O_WRONLY | O_CREAT;
00855 
00856    f = formats;
00857    while(f) {
00858       if (exts_compare(f->exts, type)) {
00859          char *stringp=NULL;
00860          /* XXX Implement check XXX */
00861          ext = ast_strdupa(f->exts);
00862          stringp=ext;
00863          ext = strsep(&stringp, "|");
00864          fn = build_filename(filename, ext);
00865          fd = open(fn, flags | myflags, mode);
00866 
00867          if (option_cache_record_files && fd >= 0) {
00868             close(fd);
00869             /*
00870                We touch orig_fn just as a place-holder so other things (like vmail) see the file is there.
00871                What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place.
00872             */
00873             orig_fn = ast_strdupa(fn); 
00874             for (size=0;size<strlen(fn);size++) {
00875                if (fn[size] == '/')
00876                   fn[size] = '_';
00877             }
00878 
00879             size += (strlen(record_cache_dir) + 10);
00880             buf = alloca(size);
00881             memset(buf, 0, size);
00882             snprintf(buf, size, "%s/%s", record_cache_dir, fn);
00883             free(fn);
00884             fn=buf;
00885             fd = open(fn, flags | myflags, mode);
00886          }
00887          if (fd >= 0) {
00888             errno = 0;
00889             if ((fs = f->rewrite(fd, comment))) {
00890                fs->trans = NULL;
00891                fs->fmt = f;
00892                fs->flags = flags;
00893                fs->mode = mode;
00894                if (option_cache_record_files) {
00895                   fs->realfilename = build_filename(filename, ext);
00896                   fs->filename = strdup(fn);
00897                } else {
00898                   fs->realfilename = NULL;
00899                   fs->filename = strdup(filename);
00900                }
00901                fs->vfs = NULL;
00902             } else {
00903                ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
00904                close(fd);
00905                unlink(fn);
00906                if (orig_fn)
00907                   unlink(orig_fn);
00908             }
00909          } else if (errno != EEXIST) {
00910             ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
00911             if (orig_fn)
00912                unlink(orig_fn);
00913          }
00914          if (!buf) /* if buf != NULL then fn is already free and pointing to it */
00915             free(fn);
00916 
00917          break;
00918       }
00919       f = f->next;
00920    }
00921    ast_mutex_unlock(&formatlock);
00922    if (!f) 
00923       ast_log(LOG_WARNING, "No such format '%s'\n", type);
00924    return fs;
00925 }
00926 
00927 char ast_waitstream(struct ast_channel *c, char *breakon)
00928 {
00929    /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */
00930    int res;
00931    struct ast_frame *fr;
00932    if (!breakon) breakon = "";
00933    while(c->stream) {
00934       res = ast_sched_wait(c->sched);
00935       if ((res < 0) && !c->timingfunc) {
00936          ast_stopstream(c);
00937          break;
00938       }
00939       if (res < 0)
00940          res = 1000;
00941       res = ast_waitfor(c, res);
00942       if (res < 0) {
00943          ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
00944          return res;
00945       } else if (res > 0) {
00946          fr = ast_read(c);
00947          if (!fr) {
00948 #if 0
00949             ast_log(LOG_DEBUG, "Got hung up\n");
00950 #endif
00951             return -1;
00952          }
00953          
00954          switch(fr->frametype) {
00955          case AST_FRAME_DTMF:
00956             res = fr->subclass;
00957             if (strchr(breakon, res)) {
00958                ast_frfree(fr);
00959                return res;
00960             }
00961             break;
00962          case AST_FRAME_CONTROL:
00963             switch(fr->subclass) {
00964             case AST_CONTROL_HANGUP:
00965                ast_frfree(fr);
00966                return -1;
00967             case AST_CONTROL_RINGING:
00968             case AST_CONTROL_ANSWER:
00969                /* Unimportant */
00970                break;
00971             default:
00972                ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
00973             }
00974          }
00975          /* Ignore */
00976          ast_frfree(fr);
00977       }
00978       ast_sched_runq(c->sched);
00979    }
00980    return (c->_softhangup ? -1 : 0);
00981 }
00982 
00983 char ast_waitstream_fr(struct ast_channel *c, char *breakon, char *forward, char *rewind, int ms)
00984 {
00985    int res;
00986    struct ast_frame *fr;
00987 
00988    if (!breakon)
00989          breakon = "";
00990    if (!forward)
00991          forward = "";
00992    if (!rewind)
00993          rewind = "";
00994    
00995    while(c->stream) {
00996       res = ast_sched_wait(c->sched);
00997       if ((res < 0) && !c->timingfunc) {
00998          ast_stopstream(c);
00999          break;
01000       }
01001       if (res < 0)
01002          res = 1000;
01003       res = ast_waitfor(c, res);
01004       if (res < 0) {
01005          ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
01006          return res;
01007       } else
01008       if (res > 0) {
01009          fr = ast_read(c);
01010          if (!fr) {
01011 #if 0
01012             ast_log(LOG_DEBUG, "Got hung up\n");
01013 #endif
01014             return -1;
01015          }
01016          
01017          switch(fr->frametype) {
01018          case AST_FRAME_DTMF:
01019             res = fr->subclass;
01020             if (strchr(forward,res)) {
01021                ast_stream_fastforward(c->stream, ms);
01022             } else if (strchr(rewind,res)) {
01023                ast_stream_rewind(c->stream, ms);
01024             } else if (strchr(breakon, res)) {
01025                ast_frfree(fr);
01026                return res;
01027             }              
01028             break;
01029          case AST_FRAME_CONTROL:
01030             switch(fr->subclass) {
01031             case AST_CONTROL_HANGUP:
01032                ast_frfree(fr);
01033                return -1;
01034             case AST_CONTROL_RINGING:
01035             case AST_CONTROL_ANSWER:
01036                /* Unimportant */
01037                break;
01038             default:
01039                ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
01040             }
01041          }
01042          /* Ignore */
01043          ast_frfree(fr);
01044       } else
01045          ast_sched_runq(c->sched);
01046    
01047       
01048    }
01049    return (c->_softhangup ? -1 : 0);
01050 }
01051 
01052 char ast_waitstream_full(struct ast_channel *c, char *breakon, int audiofd, int cmdfd)
01053 {
01054    int res;
01055    int ms;
01056    int outfd;
01057    struct ast_frame *fr;
01058    struct ast_channel *rchan;
01059 
01060    if (!breakon)
01061       breakon = "";
01062    
01063    while(c->stream) {
01064       ms = ast_sched_wait(c->sched);
01065       if ((ms < 0) && !c->timingfunc) {
01066          ast_stopstream(c);
01067          break;
01068       }
01069       if (ms < 0)
01070          ms = 1000;
01071       rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
01072       if (!rchan && (outfd < 0) && (ms)) {
01073          ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
01074          return -1;
01075       } else if (outfd > -1) {
01076          /* The FD we were watching has something waiting */
01077          return 1;
01078       } else if (rchan) {
01079          fr = ast_read(c);
01080          if (!fr) {
01081 #if 0
01082             ast_log(LOG_DEBUG, "Got hung up\n");
01083 #endif
01084             return -1;
01085          }
01086          
01087          switch(fr->frametype) {
01088          case AST_FRAME_DTMF:
01089             res = fr->subclass;
01090             if (strchr(breakon, res)) {
01091                ast_frfree(fr);
01092                return res;
01093             }
01094             break;
01095          case AST_FRAME_CONTROL:
01096             switch(fr->subclass) {
01097             case AST_CONTROL_HANGUP:
01098                ast_frfree(fr);
01099                return -1;
01100             case AST_CONTROL_RINGING:
01101             case AST_CONTROL_ANSWER:
01102                /* Unimportant */
01103                break;
01104             default:
01105                ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
01106             }
01107          case AST_FRAME_VOICE:
01108             /* Write audio if appropriate */
01109             if (audiofd > -1)
01110                write(audiofd, fr->data, fr->datalen);
01111          }
01112          /* Ignore */
01113          ast_frfree(fr);
01114       }
01115       ast_sched_runq(c->sched);
01116    
01117       
01118    }
01119    return (c->_softhangup ? -1 : 0);
01120 }
01121 
01122 static int show_file_formats(int fd, int argc, char *argv[])
01123 {
01124 #define FORMAT "%-10s %-10s %-20s\n"
01125 #define FORMAT2 "%-10s %-10s %-20s\n"
01126    struct ast_format *f;
01127    if (argc != 3)
01128       return RESULT_SHOWUSAGE;
01129    ast_cli(fd, FORMAT, "Format", "Name", "Extensions");
01130            
01131    if (ast_mutex_lock(&formatlock)) {
01132       ast_log(LOG_WARNING, "Unable to lock format list\n");
01133       return -1;
01134    }
01135 
01136    f = formats;
01137    while(f) {
01138       ast_cli(fd, FORMAT2, ast_getformatname(f->format), f->name, f->exts);
01139       f = f->next;
01140    };
01141    ast_mutex_unlock(&formatlock);
01142    return RESULT_SUCCESS;
01143 }
01144 
01145 struct ast_cli_entry show_file =
01146 {
01147    { "show", "file", "formats" },
01148    show_file_formats,
01149    "Displays file formats",
01150    "Usage: show file formats\n"
01151    "       displays currently registered file formats (if any)\n"
01152 };
01153 
01154 int ast_file_init(void)
01155 {
01156    ast_cli_register(&show_file);
01157    return 0;
01158 }

Generated on Wed Mar 16 20:08:34 2005 for Asterisk by  doxygen 1.4.0