2003-06-28 18:35:58 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk - - An open source telephony toolkit .
1999-12-02 20:24:59 +00:00
*
2006-01-03 22:16:23 +00:00
* Copyright ( C ) 1999 - 2006 , Digium , Inc .
2005-09-14 20:46:50 +00:00
*
* Mark Spencer < markster @ digium . com >
1999-12-02 20:24:59 +00:00
*
2005-09-14 20:46:50 +00:00
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* any of the maintainers of this project for assistance ;
* the project provides a web site , mailing lists and IRC
* channels for your use .
1999-12-02 20:24:59 +00:00
*
* This program is free software , distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree .
*/
2005-10-24 20:12:06 +00:00
/*! \file
2005-09-14 20:46:50 +00:00
*
2005-10-24 20:12:06 +00:00
* \ brief Generic File Format Support .
2005-12-30 21:18:06 +00:00
*
* \ author Mark Spencer < markster @ digium . com >
1999-12-02 20:24:59 +00:00
*/
2006-06-07 18:54:56 +00:00
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
2003-04-23 19:09:13 +00:00
# include <sys/types.h>
2005-04-22 13:11:34 +00:00
# include <errno.h>
# include <unistd.h>
# include <stdlib.h>
# include <string.h>
# include <stdio.h>
# include <fcntl.h>
# include <dirent.h>
# include <sys/types.h>
# include <sys/stat.h>
2005-04-21 06:02:45 +00:00
# include "asterisk/frame.h"
# include "asterisk/file.h"
# include "asterisk/cli.h"
# include "asterisk/logger.h"
# include "asterisk/channel.h"
# include "asterisk/sched.h"
# include "asterisk/options.h"
# include "asterisk/translate.h"
# include "asterisk/utils.h"
# include "asterisk/lock.h"
# include "asterisk/app.h"
2005-07-12 01:34:06 +00:00
# include "asterisk/pbx.h"
2006-01-09 21:30:46 +00:00
# include "asterisk/linkedlists.h"
2006-06-07 18:54:56 +00:00
# include "asterisk/module.h"
1999-12-02 20:24:59 +00:00
2006-04-04 12:59:25 +00:00
/*
* The following variable controls the layout of localized sound files .
* If 0 , use the historical layout with prefix just before the filename
* ( i . e . digits / en / 1. gsm , digits / it / 1. gsm or default to digits / 1. gsm ) ,
* if 1 put the prefix at the beginning of the filename
* ( i . e . en / digits / 1. gsm , it / digits / 1. gsm or default to digits / 1. gsm ) .
* The latter permits a language to be entirely in one directory .
*/
2006-12-27 21:18:27 +00:00
int ast_language_is_prefix = 1 ;
1999-12-02 20:24:59 +00:00
2007-01-26 22:55:06 +00:00
static AST_RWLIST_HEAD_STATIC ( formats , ast_format ) ;
1999-12-02 20:24:59 +00:00
2006-08-21 02:11:39 +00:00
int __ast_format_register ( const struct ast_format * f , struct ast_module * mod )
1999-12-02 20:24:59 +00:00
{
struct ast_format * tmp ;
2006-04-04 12:59:25 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_WRLOCK ( & formats ) ;
AST_RWLIST_TRAVERSE ( & formats , tmp , list ) {
2006-04-04 12:59:25 +00:00
if ( ! strcasecmp ( f - > name , tmp - > name ) ) {
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats ) ;
2006-04-04 12:59:25 +00:00
ast_log ( LOG_WARNING , " Tried to register '%s' format, already registered \n " , f - > name ) ;
1999-12-02 20:24:59 +00:00
return - 1 ;
}
2006-04-04 12:59:25 +00:00
}
2006-08-21 02:11:39 +00:00
if ( ! ( tmp = ast_calloc ( 1 , sizeof ( * tmp ) ) ) ) {
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats ) ;
1999-12-02 20:24:59 +00:00
return - 1 ;
}
2006-04-04 12:59:25 +00:00
* tmp = * f ;
2006-08-21 02:11:39 +00:00
tmp - > module = mod ;
2006-04-04 12:59:25 +00:00
if ( tmp - > buf_size ) {
/*
* Align buf_size properly , rounding up to the machine - specific
* alignment for pointers .
*/
struct _test_align { void * a , * b ; } p ;
int align = ( char * ) & p . b - ( char * ) & p . a ;
tmp - > buf_size = ( ( f - > buf_size + align - 1 ) / align ) * align ;
}
memset ( & tmp - > list , 0 , sizeof ( tmp - > list ) ) ;
2007-01-26 22:55:06 +00:00
AST_RWLIST_INSERT_HEAD ( & formats , tmp , list ) ;
AST_RWLIST_UNLOCK ( & formats ) ;
1999-12-02 20:24:59 +00:00
if ( option_verbose > 1 )
2006-04-04 12:59:25 +00:00
ast_verbose ( VERBOSE_PREFIX_2 " Registered file format %s, extension(s) %s \n " , f - > name , f - > exts ) ;
2006-08-21 02:11:39 +00:00
1999-12-02 20:24:59 +00:00
return 0 ;
}
2004-11-13 16:13:07 +00:00
int ast_format_unregister ( const char * name )
1999-12-02 20:24:59 +00:00
{
2006-01-09 21:30:46 +00:00
struct ast_format * tmp ;
int res = - 1 ;
2007-01-26 22:55:06 +00:00
AST_RWLIST_WRLOCK ( & formats ) ;
AST_RWLIST_TRAVERSE_SAFE_BEGIN ( & formats , tmp , list ) {
1999-12-02 20:24:59 +00:00
if ( ! strcasecmp ( name , tmp - > name ) ) {
2007-01-26 22:55:06 +00:00
AST_RWLIST_REMOVE_CURRENT ( & formats , list ) ;
1999-12-02 20:24:59 +00:00
free ( tmp ) ;
2006-01-09 21:30:46 +00:00
res = 0 ;
1999-12-02 20:24:59 +00:00
}
}
2007-01-26 22:55:06 +00:00
AST_RWLIST_TRAVERSE_SAFE_END
AST_RWLIST_UNLOCK ( & formats ) ;
2006-01-09 21:30:46 +00:00
2006-04-13 17:22:16 +00:00
if ( ! res ) {
2006-01-09 21:30:46 +00:00
if ( option_verbose > 1 )
2006-04-04 12:59:25 +00:00
ast_verbose ( VERBOSE_PREFIX_2 " Unregistered format %s \n " , name ) ;
2006-01-09 21:30:46 +00:00
} else
ast_log ( LOG_WARNING , " Tried to unregister format %s, already unregistered \n " , name ) ;
return res ;
1999-12-02 20:24:59 +00:00
}
int ast_stopstream ( struct ast_channel * tmp )
{
/* Stop a running stream if there is one */
2003-06-29 03:24:39 +00:00
if ( tmp - > stream ) {
ast_closestream ( tmp - > stream ) ;
2006-05-02 20:06:21 +00:00
tmp - > stream = NULL ;
2004-04-06 22:17:32 +00:00
if ( tmp - > oldwriteformat & & ast_set_write_format ( tmp , tmp - > oldwriteformat ) )
2003-06-29 03:24:39 +00:00
ast_log ( LOG_WARNING , " Unable to restore format back to %d \n " , tmp - > oldwriteformat ) ;
}
1999-12-02 20:24:59 +00:00
return 0 ;
}
int ast_writestream ( struct ast_filestream * fs , struct ast_frame * f )
{
int res = - 1 ;
2006-04-04 12:59:25 +00:00
int alt = 0 ;
2003-06-29 03:24:39 +00:00
if ( f - > frametype = = AST_FRAME_VIDEO ) {
if ( fs - > fmt - > format < AST_FORMAT_MAX_AUDIO ) {
/* This is the audio portion. Call the video one... */
if ( ! fs - > vfs & & fs - > filename ) {
2006-01-07 17:54:22 +00:00
const char * type = ast_getformatname ( f - > subclass & ~ 0x1 ) ;
2003-06-29 03:24:39 +00:00
fs - > vfs = ast_writefile ( fs - > filename , type , NULL , fs - > flags , 0 , fs - > mode ) ;
2006-10-03 15:53:07 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Opened video output file \n " ) ;
2003-06-29 03:24:39 +00:00
}
if ( fs - > vfs )
return ast_writestream ( fs - > vfs , f ) ;
2006-04-04 12:59:25 +00:00
/* else ignore */
2003-06-29 03:24:39 +00:00
return 0 ;
} else {
/* Might / might not have mark set */
alt = 1 ;
}
} else if ( f - > frametype ! = AST_FRAME_VOICE ) {
1999-12-02 20:24:59 +00:00
ast_log ( LOG_WARNING , " Tried to write non-voice frame \n " ) ;
return - 1 ;
}
2003-06-29 03:24:39 +00:00
if ( ( ( fs - > fmt - > format | alt ) & f - > subclass ) = = f - > subclass ) {
2000-01-02 23:51:30 +00:00
res = fs - > fmt - > write ( fs , f ) ;
if ( res < 0 )
ast_log ( LOG_WARNING , " Natural write failed \n " ) ;
2006-04-04 12:59:25 +00:00
else if ( res > 0 )
2000-01-02 23:51:30 +00:00
ast_log ( LOG_WARNING , " Huh?? \n " ) ;
} else {
1999-12-02 20:24:59 +00:00
/* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
the one we ' ve setup a translator for , we do the " wrong thing " XXX */
2006-04-04 12:59:25 +00:00
if ( fs - > trans & & f - > subclass ! = fs - > lastwriteformat ) {
2003-05-28 19:45:07 +00:00
ast_translator_free_path ( fs - > trans ) ;
fs - > trans = NULL ;
}
1999-12-02 20:24:59 +00:00
if ( ! fs - > trans )
2001-03-10 19:12:11 +00:00
fs - > trans = ast_translator_build_path ( fs - > fmt - > format , f - > subclass ) ;
1999-12-02 20:24:59 +00:00
if ( ! fs - > trans )
2006-04-04 12:59:25 +00:00
ast_log ( LOG_WARNING , " Unable to translate to format %s, source format %s \n " ,
fs - > fmt - > name , ast_getformatname ( f - > subclass ) ) ;
1999-12-02 20:24:59 +00:00
else {
2006-04-04 12:59:25 +00:00
struct ast_frame * trf ;
2003-05-28 19:45:07 +00:00
fs - > lastwriteformat = f - > subclass ;
2001-03-10 19:12:11 +00:00
/* Get the translated frame but don't consume the original in case they're using it on another stream */
trf = ast_translate ( fs - > trans , f , 0 ) ;
2002-09-03 21:55:10 +00:00
if ( trf ) {
res = fs - > fmt - > write ( fs , trf ) ;
if ( res )
ast_log ( LOG_WARNING , " Translated frame write failed \n " ) ;
} else
res = 0 ;
1999-12-02 20:24:59 +00:00
}
}
2006-04-04 12:59:25 +00:00
return res ;
1999-12-02 20:24:59 +00:00
}
2004-11-13 16:13:07 +00:00
static int copy ( const char * infile , const char * outfile )
2001-06-13 20:36:52 +00:00
{
2006-04-04 12:59:25 +00:00
int ifd , ofd , len ;
char buf [ 4096 ] ; /* XXX make it lerger. */
2004-06-23 18:00:50 +00:00
2001-06-13 20:36:52 +00:00
if ( ( ifd = open ( infile , O_RDONLY ) ) < 0 ) {
ast_log ( LOG_WARNING , " Unable to open %s in read-only mode \n " , infile ) ;
return - 1 ;
}
2006-12-21 19:44:20 +00:00
if ( ( ofd = open ( outfile , O_WRONLY | O_TRUNC | O_CREAT , AST_FILE_MODE ) ) < 0 ) {
2001-06-13 20:36:52 +00:00
ast_log ( LOG_WARNING , " Unable to open %s in write-only mode \n " , outfile ) ;
close ( ifd ) ;
return - 1 ;
}
2006-04-04 12:59:25 +00:00
while ( ( len = read ( ifd , buf , sizeof ( buf ) ) ) ) {
int res ;
2001-06-13 20:36:52 +00:00
if ( len < 0 ) {
ast_log ( LOG_WARNING , " Read failed on %s: %s \n " , infile , strerror ( errno ) ) ;
2006-04-04 12:59:25 +00:00
break ;
2001-06-13 20:36:52 +00:00
}
2006-04-04 12:59:25 +00:00
/* XXX handle partial writes */
res = write ( ofd , buf , len ) ;
if ( res ! = len ) {
ast_log ( LOG_WARNING , " Write failed on %s (%d of %d): %s \n " , outfile , res , len , strerror ( errno ) ) ;
len = - 1 ; /* error marker */
break ;
2001-06-13 20:36:52 +00:00
}
2006-04-04 12:59:25 +00:00
}
2001-06-13 20:36:52 +00:00
close ( ifd ) ;
close ( ofd ) ;
2006-04-04 12:59:25 +00:00
if ( len < 0 ) {
unlink ( outfile ) ;
return - 1 ; /* error */
}
return 0 ; /* success */
2001-06-13 20:36:52 +00:00
}
2006-04-04 12:59:25 +00:00
/*!
* \ brief construct a filename . Absolute pathnames are preserved ,
* relative names are prefixed by the sounds / directory .
* The wav49 suffix is replaced by ' WAV ' .
* Returns a malloc ' ed string to be freed by the caller .
*/
2004-11-14 22:38:34 +00:00
static char * build_filename ( const char * filename , const char * ext )
1999-12-02 20:24:59 +00:00
{
2006-04-04 12:59:25 +00:00
char * fn = NULL ;
2004-07-14 07:44:19 +00:00
2006-04-04 12:59:25 +00:00
if ( ! strcmp ( ext , " wav49 " ) )
ext = " WAV " ;
2005-01-10 04:18:18 +00:00
2006-04-04 12:59:25 +00:00
if ( filename [ 0 ] = = ' / ' )
asprintf ( & fn , " %s.%s " , filename , ext ) ;
else
asprintf ( & fn , " %s/sounds/%s.%s " ,
2006-04-15 22:53:53 +00:00
ast_config_AST_DATA_DIR , filename , ext ) ;
1999-12-02 20:24:59 +00:00
return fn ;
}
2006-04-04 12:59:25 +00:00
/* compare type against the list 'exts' */
/* XXX need a better algorithm */
2004-11-14 22:38:34 +00:00
static int exts_compare ( const char * exts , const char * type )
2004-02-25 22:31:51 +00:00
{
char tmp [ 256 ] ;
2006-04-04 12:59:25 +00:00
char * stringp = tmp , * ext ;
2004-02-25 22:31:51 +00:00
2005-06-05 16:32:16 +00:00
ast_copy_string ( tmp , exts , sizeof ( tmp ) ) ;
2004-02-25 22:31:51 +00:00
while ( ( ext = strsep ( & stringp , " | " ) ) ) {
2006-04-04 12:59:25 +00:00
if ( ! strcmp ( ext , type ) )
2004-02-25 22:31:51 +00:00
return 1 ;
}
return 0 ;
}
2006-04-04 12:59:25 +00:00
static struct ast_filestream * get_filestream ( struct ast_format * fmt , FILE * bfile )
{
struct ast_filestream * s ;
int l = sizeof ( * s ) + fmt - > buf_size + fmt - > desc_size ; /* total allocation size */
if ( ( s = ast_calloc ( 1 , l ) ) = = NULL )
return NULL ;
s - > fmt = fmt ;
s - > f = bfile ;
if ( fmt - > desc_size )
s - > private = ( ( char * ) ( s + 1 ) ) + fmt - > buf_size ;
if ( fmt - > buf_size )
s - > buf = ( char * ) ( s + 1 ) ;
s - > fr . src = fmt - > name ;
return s ;
}
/*
* Default implementations of open and rewrite .
* Only use them if you don ' t have expensive stuff to do .
*/
enum wrap_fn { WRAP_OPEN , WRAP_REWRITE } ;
static int fn_wrapper ( struct ast_filestream * s , const char * comment , enum wrap_fn mode )
{
struct ast_format * f = s - > fmt ;
int ret = - 1 ;
if ( mode = = WRAP_OPEN & & f - > open & & f - > open ( s ) )
ast_log ( LOG_WARNING , " Unable to open format %s \n " , f - > name ) ;
else if ( mode = = WRAP_REWRITE & & f - > rewrite & & f - > rewrite ( s , comment ) )
ast_log ( LOG_WARNING , " Unable to rewrite format %s \n " , f - > name ) ;
else {
/* preliminary checks succeed. update usecount */
2006-08-21 02:11:39 +00:00
ast_module_ref ( f - > module ) ;
2006-04-04 12:59:25 +00:00
ret = 0 ;
}
return ret ;
}
static int rewrite_wrapper ( struct ast_filestream * s , const char * comment )
{
return fn_wrapper ( s , comment , WRAP_REWRITE ) ;
}
static int open_wrapper ( struct ast_filestream * s )
{
return fn_wrapper ( s , NULL , WRAP_OPEN ) ;
}
2006-02-09 02:08:04 +00:00
enum file_action {
2006-04-04 12:59:25 +00:00
ACTION_EXISTS = 1 , /* return matching format if file exists, 0 otherwise */
ACTION_DELETE , /* delete file, return 0 on success, -1 on error */
ACTION_RENAME , /* rename file. return 0 on success, -1 on error */
2006-02-09 02:08:04 +00:00
ACTION_OPEN ,
2006-04-04 12:59:25 +00:00
ACTION_COPY /* copy file. return 0 on success, -1 on error */
2006-02-09 02:08:04 +00:00
} ;
1999-12-02 20:24:59 +00:00
2006-04-04 12:59:25 +00:00
/*!
* \ brief perform various actions on a file . Second argument
* arg2 depends on the command :
* unused for EXISTS and DELETE
* destination file name ( const char * ) for COPY and RENAME
* struct ast_channel * for OPEN
2006-04-06 15:08:31 +00:00
* if fmt is NULL , OPEN will return the first matching entry ,
* whereas other functions will run on all matching entries .
2006-04-04 12:59:25 +00:00
*/
static int ast_filehelper ( const char * filename , const void * arg2 , const char * fmt , const enum file_action action )
1999-12-02 20:24:59 +00:00
{
struct ast_format * f ;
2006-04-04 12:59:25 +00:00
int res = ( action = = ACTION_EXISTS ) ? 0 : - 1 ;
2007-01-26 22:55:06 +00:00
AST_RWLIST_RDLOCK ( & formats ) ;
2006-04-04 12:59:25 +00:00
/* Check for a specific format */
2007-01-26 22:55:06 +00:00
AST_RWLIST_TRAVERSE ( & formats , f , list ) {
2006-04-06 15:08:31 +00:00
char * stringp , * ext = NULL ;
2006-04-04 12:59:25 +00:00
if ( fmt & & ! exts_compare ( f - > exts , fmt ) )
continue ;
/* Look for a file matching the supported extensions.
* The file must exist , and for OPEN , must match
* one of the formats supported by the channel .
*/
2006-04-06 15:08:31 +00:00
stringp = ast_strdupa ( f - > exts ) ; /* this is in the stack so does not need to be freed */
2006-04-04 12:59:25 +00:00
while ( ( ext = strsep ( & stringp , " | " ) ) ) {
struct stat st ;
2006-04-06 15:08:31 +00:00
char * fn = build_filename ( filename , ext ) ;
2006-04-04 12:59:25 +00:00
if ( fn = = NULL )
continue ;
if ( stat ( fn , & st ) ) { /* file not existent */
free ( fn ) ;
continue ;
}
/* for 'OPEN' we need to be sure that the format matches
* what the channel can process
*/
if ( action = = ACTION_OPEN ) {
struct ast_channel * chan = ( struct ast_channel * ) arg2 ;
FILE * bfile ;
struct ast_filestream * s ;
if ( ! ( chan - > writeformat & f - > format ) & &
! ( f - > format > = AST_FORMAT_MAX_AUDIO & & fmt ) ) {
1999-12-02 20:24:59 +00:00
free ( fn ) ;
2006-04-04 12:59:25 +00:00
continue ; /* not a supported format */
}
if ( ( bfile = fopen ( fn , " r " ) ) = = NULL ) {
free ( fn ) ;
continue ; /* cannot open file */
}
s = get_filestream ( f , bfile ) ;
if ( ! s ) {
fclose ( bfile ) ;
free ( fn ) ; /* cannot allocate descriptor */
continue ;
1999-12-02 20:24:59 +00:00
}
2006-04-04 12:59:25 +00:00
if ( open_wrapper ( s ) ) {
fclose ( bfile ) ;
free ( fn ) ;
free ( s ) ;
continue ; /* cannot run open on file */
}
/* ok this is good for OPEN */
res = 1 ; /* found */
s - > lasttimeout = - 1 ;
s - > fmt = f ;
s - > trans = NULL ;
s - > filename = NULL ;
2007-04-08 01:42:32 +00:00
if ( s - > fmt - > format < AST_FORMAT_MAX_AUDIO ) {
if ( chan - > stream )
ast_closestream ( chan - > stream ) ;
2006-04-04 12:59:25 +00:00
chan - > stream = s ;
2007-04-08 01:42:32 +00:00
} else {
if ( chan - > vstream )
ast_closestream ( chan - > vstream ) ;
2006-04-04 12:59:25 +00:00
chan - > vstream = s ;
2007-04-08 01:42:32 +00:00
}
2006-08-23 20:22:18 +00:00
free ( fn ) ;
2006-04-06 15:08:31 +00:00
break ;
2006-04-04 12:59:25 +00:00
}
2006-04-06 15:08:31 +00:00
switch ( action ) {
case ACTION_OPEN :
break ; /* will never get here */
case ACTION_EXISTS : /* return the matching format */
res | = f - > format ;
break ;
case ACTION_DELETE :
if ( ( res = unlink ( fn ) ) )
ast_log ( LOG_WARNING , " unlink(%s) failed: %s \n " , fn , strerror ( errno ) ) ;
break ;
case ACTION_RENAME :
case ACTION_COPY : {
char * nfn = build_filename ( ( const char * ) arg2 , ext ) ;
if ( ! nfn )
ast_log ( LOG_WARNING , " Out of memory \n " ) ;
else {
res = action = = ACTION_COPY ? copy ( fn , nfn ) : rename ( fn , nfn ) ;
if ( res )
ast_log ( LOG_WARNING , " %s(%s,%s) failed: %s \n " ,
action = = ACTION_COPY ? " copy " : " rename " ,
fn , nfn , strerror ( errno ) ) ;
free ( nfn ) ;
}
}
break ;
2006-04-04 12:59:25 +00:00
2006-04-06 15:08:31 +00:00
default :
ast_log ( LOG_WARNING , " Unknown helper %d \n " , action ) ;
2006-04-04 12:59:25 +00:00
}
2006-04-06 15:08:31 +00:00
free ( fn ) ;
2006-04-04 12:59:25 +00:00
}
1999-12-02 20:24:59 +00:00
}
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats ) ;
1999-12-02 20:24:59 +00:00
return res ;
}
2006-04-04 12:59:25 +00:00
/*!
* \ brief helper routine to locate a file with a given format
* and language preference .
* Try preflang , preflang with stripped ' _ ' suffix , or NULL .
* In the standard asterisk , language goes just before the last component .
* In an alternative configuration , the language should be a prefix
* to the actual filename .
*
* The last parameter ( s ) point to a buffer of sufficient size ,
* which on success is filled with the matching filename .
*/
static int fileexists_core ( const char * filename , const char * fmt , const char * preflang ,
char * buf , int buflen )
{
int res = - 1 ;
int langlen ; /* length of language string */
const char * c = strrchr ( filename , ' / ' ) ;
int offset = c ? c - filename + 1 : 0 ; /* points right after the last '/' */
if ( preflang = = NULL )
preflang = " " ;
langlen = strlen ( preflang ) ;
if ( buflen < langlen + strlen ( filename ) + 2 ) {
ast_log ( LOG_WARNING , " buffer too small \n " ) ;
buf [ 0 ] = ' \0 ' ; /* set to empty */
buf = alloca ( langlen + strlen ( filename ) + 2 ) ; /* room for everything */
}
if ( buf = = NULL )
return 0 ;
buf [ 0 ] = ' \0 ' ;
for ( ; ; ) {
if ( ast_language_is_prefix ) { /* new layout */
if ( langlen ) {
strcpy ( buf , preflang ) ;
buf [ langlen ] = ' / ' ;
strcpy ( buf + langlen + 1 , filename ) ;
} else
strcpy ( buf , filename ) ; /* first copy the full string */
} else { /* old layout */
strcpy ( buf , filename ) ; /* first copy the full string */
if ( langlen ) {
/* insert the language and suffix if needed */
strcpy ( buf + offset , preflang ) ;
sprintf ( buf + offset + langlen , " /%s " , filename + offset ) ;
}
}
res = ast_filehelper ( buf , NULL , fmt , ACTION_EXISTS ) ;
if ( res > 0 ) /* found format */
break ;
if ( langlen = = 0 ) /* no more formats */
break ;
if ( preflang [ langlen ] = = ' _ ' ) /* we are on the local suffix */
langlen = 0 ; /* try again with no language */
else
langlen = ( c = strchr ( preflang , ' _ ' ) ) ? c - preflang : 0 ;
}
return res ;
}
2004-11-13 16:13:07 +00:00
struct ast_filestream * ast_openstream ( struct ast_channel * chan , const char * filename , const char * preflang )
2004-12-24 01:40:07 +00:00
{
return ast_openstream_full ( chan , filename , preflang , 0 ) ;
}
struct ast_filestream * ast_openstream_full ( struct ast_channel * chan , const char * filename , const char * preflang , int asis )
1999-12-02 20:24:59 +00:00
{
2006-04-04 12:59:25 +00:00
/*
* Use fileexists_core ( ) to find a file in a compatible
* language and format , set up a suitable translator ,
* and open the stream .
*/
int fmts , res , buflen ;
char * buf ;
2004-12-24 01:40:07 +00:00
if ( ! asis ) {
/* do this first, otherwise we detect the wrong writeformat */
ast_stopstream ( chan ) ;
if ( chan - > generator )
ast_deactivate_generator ( chan ) ;
}
2006-04-04 12:59:25 +00:00
if ( preflang = = NULL )
preflang = " " ;
buflen = strlen ( preflang ) + strlen ( filename ) + 2 ;
buf = alloca ( buflen ) ;
if ( buf = = NULL )
return NULL ;
fmts = fileexists_core ( filename , NULL , preflang , buf , buflen ) ;
if ( fmts > 0 )
fmts & = AST_FORMAT_AUDIO_MASK ;
1999-12-02 20:24:59 +00:00
if ( fmts < 1 ) {
ast_log ( LOG_WARNING , " File %s does not exist in any format \n " , filename ) ;
2003-02-06 22:11:43 +00:00
return NULL ;
1999-12-02 20:24:59 +00:00
}
2001-03-10 19:12:11 +00:00
chan - > oldwriteformat = chan - > writeformat ;
/* Set the channel to a format we can work with */
2004-04-06 22:17:32 +00:00
res = ast_set_write_format ( chan , fmts ) ;
2006-04-04 12:59:25 +00:00
res = ast_filehelper ( buf , chan , NULL , ACTION_OPEN ) ;
2005-10-16 16:12:51 +00:00
if ( res > = 0 )
2003-02-06 22:11:43 +00:00
return chan - > stream ;
return NULL ;
}
2004-11-13 16:13:07 +00:00
struct ast_filestream * ast_openvstream ( struct ast_channel * chan , const char * filename , const char * preflang )
2003-06-29 03:24:39 +00:00
{
2006-04-04 12:59:25 +00:00
/* As above, but for video. But here we don't have translators
* so we must enforce a format .
*/
2006-01-07 17:54:22 +00:00
unsigned int format ;
2006-04-04 12:59:25 +00:00
char * buf ;
int buflen ;
if ( preflang = = NULL )
preflang = " " ;
buflen = strlen ( preflang ) + strlen ( filename ) + 2 ;
buf = alloca ( buflen ) ;
if ( buf = = NULL )
return NULL ;
2006-01-07 17:54:22 +00:00
for ( format = AST_FORMAT_MAX_AUDIO < < 1 ; format < = AST_FORMAT_MAX_VIDEO ; format = format < < 1 ) {
2006-04-04 12:59:25 +00:00
int fd ;
const char * fmt ;
2006-01-07 17:54:22 +00:00
if ( ! ( chan - > nativeformats & format ) )
continue ;
fmt = ast_getformatname ( format ) ;
2006-04-04 12:59:25 +00:00
if ( fileexists_core ( filename , fmt , preflang , buf , buflen ) < 1 ) /* no valid format */
2006-01-07 17:54:22 +00:00
continue ;
2006-04-04 12:59:25 +00:00
fd = ast_filehelper ( buf , chan , fmt , ACTION_OPEN ) ;
2006-01-07 17:54:22 +00:00
if ( fd > = 0 )
return chan - > vstream ;
ast_log ( LOG_WARNING , " File %s has video but couldn't be opened \n " , filename ) ;
2003-06-29 03:24:39 +00:00
}
return NULL ;
}
2003-12-19 18:06:29 +00:00
struct ast_frame * ast_readframe ( struct ast_filestream * s )
{
struct ast_frame * f = NULL ;
int whennext = 0 ;
if ( s & & s - > fmt )
f = s - > fmt - > read ( s , & whennext ) ;
return f ;
}
2003-06-28 22:50:47 +00:00
static int ast_readaudio_callback ( void * data )
2003-02-06 22:11:43 +00:00
{
2003-06-28 22:50:47 +00:00
struct ast_filestream * s = data ;
int whennext = 0 ;
2007-01-23 00:11:32 +00:00
while ( ! whennext ) {
2006-04-04 12:59:25 +00:00
struct ast_frame * fr = s - > fmt - > read ( s , & whennext ) ;
if ( ! fr /* stream complete */ | | ast_write ( s - > owner , fr ) /* error writing */ ) {
if ( fr )
2003-06-28 22:50:47 +00:00
ast_log ( LOG_WARNING , " Failed to write frame \n " ) ;
s - > owner - > streamid = - 1 ;
2006-04-24 17:11:45 +00:00
# ifdef HAVE_ZAPTEL
2003-06-29 20:32:26 +00:00
ast_settimeout ( s - > owner , 0 , NULL , NULL ) ;
# endif
2003-06-28 22:50:47 +00:00
return 0 ;
}
}
if ( whennext ! = s - > lasttimeout ) {
2006-04-24 17:11:45 +00:00
# ifdef HAVE_ZAPTEL
2003-07-01 23:13:05 +00:00
if ( s - > owner - > timingfd > - 1 )
2003-07-01 23:09:18 +00:00
ast_settimeout ( s - > owner , whennext , ast_readaudio_callback , s ) ;
else
2003-06-29 20:32:26 +00:00
# endif
2003-07-01 23:17:10 +00:00
s - > owner - > streamid = ast_sched_add ( s - > owner - > sched , whennext / 8 , ast_readaudio_callback , s ) ;
2003-06-28 22:50:47 +00:00
s - > lasttimeout = whennext ;
return 0 ;
2003-02-06 22:11:43 +00:00
}
2003-06-28 22:50:47 +00:00
return 1 ;
}
2003-06-29 03:24:39 +00:00
static int ast_readvideo_callback ( void * data )
{
struct ast_filestream * s = data ;
int whennext = 0 ;
2006-04-04 12:59:25 +00:00
while ( ! whennext ) {
struct ast_frame * fr = s - > fmt - > read ( s , & whennext ) ;
if ( ! fr | | ast_write ( s - > owner , fr ) ) { /* no stream or error, as above */
if ( fr )
2003-06-29 03:24:39 +00:00
ast_log ( LOG_WARNING , " Failed to write frame \n " ) ;
s - > owner - > vstreamid = - 1 ;
return 0 ;
}
}
if ( whennext ! = s - > lasttimeout ) {
s - > owner - > vstreamid = ast_sched_add ( s - > owner - > sched , whennext / 8 , ast_readvideo_callback , s ) ;
s - > lasttimeout = whennext ;
return 0 ;
}
return 1 ;
}
2003-06-28 22:50:47 +00:00
int ast_applystream ( struct ast_channel * chan , struct ast_filestream * s )
{
s - > owner = chan ;
2003-02-06 22:11:43 +00:00
return 0 ;
}
int ast_playstream ( struct ast_filestream * s )
{
2003-06-29 03:24:39 +00:00
if ( s - > fmt - > format < AST_FORMAT_MAX_AUDIO )
ast_readaudio_callback ( s ) ;
else
ast_readvideo_callback ( s ) ;
2003-02-06 22:11:43 +00:00
return 0 ;
}
2006-02-20 23:35:12 +00:00
int ast_seekstream ( struct ast_filestream * fs , off_t sample_offset , int whence )
2003-02-06 22:11:43 +00:00
{
return fs - > fmt - > seek ( fs , sample_offset , whence ) ;
}
int ast_truncstream ( struct ast_filestream * fs )
{
return fs - > fmt - > trunc ( fs ) ;
}
2006-02-20 23:35:12 +00:00
off_t ast_tellstream ( struct ast_filestream * fs )
2003-02-06 22:11:43 +00:00
{
return fs - > fmt - > tell ( fs ) ;
}
2006-02-20 23:35:12 +00:00
int ast_stream_fastforward ( struct ast_filestream * fs , off_t ms )
2003-02-06 22:11:43 +00:00
{
2006-04-04 12:59:25 +00:00
return ast_seekstream ( fs , ms * DEFAULT_SAMPLES_PER_MS , SEEK_CUR ) ;
2003-02-06 22:11:43 +00:00
}
2006-02-20 23:35:12 +00:00
int ast_stream_rewind ( struct ast_filestream * fs , off_t ms )
2003-02-06 22:11:43 +00:00
{
2006-04-04 12:59:25 +00:00
return ast_seekstream ( fs , - ms * DEFAULT_SAMPLES_PER_MS , SEEK_CUR ) ;
2003-02-06 22:11:43 +00:00
}
int ast_closestream ( struct ast_filestream * f )
{
2004-09-07 01:49:08 +00:00
char * cmd = NULL ;
size_t size = 0 ;
2003-02-06 22:11:43 +00:00
/* Stop a running stream if there is one */
2003-06-28 22:50:47 +00:00
if ( f - > owner ) {
2003-06-29 03:24:39 +00:00
if ( f - > fmt - > format < AST_FORMAT_MAX_AUDIO ) {
2003-06-29 04:50:45 +00:00
f - > owner - > stream = NULL ;
2003-06-29 03:24:39 +00:00
if ( f - > owner - > streamid > - 1 )
ast_sched_del ( f - > owner - > sched , f - > owner - > streamid ) ;
f - > owner - > streamid = - 1 ;
2006-04-24 17:11:45 +00:00
# ifdef HAVE_ZAPTEL
2003-06-29 20:32:26 +00:00
ast_settimeout ( f - > owner , 0 , NULL , NULL ) ;
# endif
2003-06-29 03:24:39 +00:00
} else {
2003-06-29 04:50:45 +00:00
f - > owner - > vstream = NULL ;
2003-06-29 03:24:39 +00:00
if ( f - > owner - > vstreamid > - 1 )
ast_sched_del ( f - > owner - > sched , f - > owner - > vstreamid ) ;
f - > owner - > vstreamid = - 1 ;
}
2003-06-28 22:50:47 +00:00
}
2003-08-11 20:24:14 +00:00
/* destroy the translator on exit */
2006-04-04 12:59:25 +00:00
if ( f - > trans )
2003-08-11 20:24:14 +00:00
ast_translator_free_path ( f - > trans ) ;
2004-09-07 01:49:08 +00:00
if ( f - > realfilename & & f - > filename ) {
size = strlen ( f - > filename ) + strlen ( f - > realfilename ) + 15 ;
cmd = alloca ( size ) ;
memset ( cmd , 0 , size ) ;
snprintf ( cmd , size , " /bin/mv -f %s %s " , f - > filename , f - > realfilename ) ;
ast_safe_system ( cmd ) ;
}
2004-09-20 23:15:49 +00:00
2006-04-04 12:59:25 +00:00
if ( f - > filename )
2004-09-07 01:49:08 +00:00
free ( f - > filename ) ;
2006-04-04 12:59:25 +00:00
if ( f - > realfilename )
2004-09-07 01:49:08 +00:00
free ( f - > realfilename ) ;
2006-04-04 12:59:25 +00:00
if ( f - > fmt - > close )
f - > fmt - > close ( f ) ;
fclose ( f - > f ) ;
if ( f - > vfs )
2006-01-08 04:30:10 +00:00
ast_closestream ( f - > vfs ) ;
2006-08-21 02:11:39 +00:00
ast_module_unref ( f - > fmt - > module ) ;
2006-04-04 12:59:25 +00:00
free ( f ) ;
2003-02-06 22:11:43 +00:00
return 0 ;
}
2006-04-04 12:59:25 +00:00
/*
* Look the various language - specific places where a file could exist .
*/
2004-11-13 16:13:07 +00:00
int ast_fileexists ( const char * filename , const char * fmt , const char * preflang )
2003-02-06 22:11:43 +00:00
{
2006-04-04 12:59:25 +00:00
char * buf ;
int buflen ;
if ( preflang = = NULL )
preflang = " " ;
buflen = strlen ( preflang ) + strlen ( filename ) + 2 ; /* room for everything */
buf = alloca ( buflen ) ;
if ( buf = = NULL )
return 0 ;
return fileexists_core ( filename , fmt , preflang , buf , buflen ) ;
2003-02-06 22:11:43 +00:00
}
2004-11-13 16:13:07 +00:00
int ast_filedelete ( const char * filename , const char * fmt )
2003-02-06 22:11:43 +00:00
{
return ast_filehelper ( filename , NULL , fmt , ACTION_DELETE ) ;
}
2004-11-13 16:13:07 +00:00
int ast_filerename ( const char * filename , const char * filename2 , const char * fmt )
2003-02-06 22:11:43 +00:00
{
return ast_filehelper ( filename , filename2 , fmt , ACTION_RENAME ) ;
}
2004-11-13 16:13:07 +00:00
int ast_filecopy ( const char * filename , const char * filename2 , const char * fmt )
2003-02-06 22:11:43 +00:00
{
return ast_filehelper ( filename , filename2 , fmt , ACTION_COPY ) ;
}
2004-11-13 16:13:07 +00:00
int ast_streamfile ( struct ast_channel * chan , const char * filename , const char * preflang )
2003-02-06 22:11:43 +00:00
{
struct ast_filestream * fs ;
2006-01-07 17:54:22 +00:00
struct ast_filestream * vfs = NULL ;
char fmt [ 256 ] ;
2003-02-06 22:11:43 +00:00
fs = ast_openstream ( chan , filename , preflang ) ;
2006-01-07 17:54:22 +00:00
if ( fs )
vfs = ast_openvstream ( chan , filename , preflang ) ;
2006-10-03 15:53:07 +00:00
if ( vfs ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " Ooh, found a video stream, too, format %s \n " , ast_getformatname ( vfs - > fmt - > format ) ) ;
}
2004-09-07 15:01:48 +00:00
if ( fs ) {
if ( ast_applystream ( chan , fs ) )
2003-02-06 22:11:43 +00:00
return - 1 ;
2004-09-07 15:01:48 +00:00
if ( vfs & & ast_applystream ( chan , vfs ) )
2003-06-29 03:24:39 +00:00
return - 1 ;
2004-09-07 15:01:48 +00:00
if ( ast_playstream ( fs ) )
2003-02-06 22:11:43 +00:00
return - 1 ;
2004-09-07 15:01:48 +00:00
if ( vfs & & ast_playstream ( vfs ) )
2003-06-29 03:24:39 +00:00
return - 1 ;
2000-01-09 23:17:36 +00:00
if ( option_verbose > 2 )
2007-03-07 01:07:16 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " <%s> Playing '%s.%s' (language '%s') \n " , chan - > name , filename , ast_getformatname ( chan - > writeformat ) , preflang ? preflang : " default " ) ;
2007-01-16 17:39:39 +00:00
1999-12-02 20:24:59 +00:00
return 0 ;
}
2006-01-07 17:54:22 +00:00
ast_log ( LOG_WARNING , " Unable to open %s (format %s): %s \n " , filename , ast_getformatname_multiple ( fmt , sizeof ( fmt ) , chan - > nativeformats ) , strerror ( errno ) ) ;
1999-12-02 20:24:59 +00:00
return - 1 ;
}
2004-11-13 16:13:07 +00:00
struct ast_filestream * ast_readfile ( const char * filename , const char * type , const char * comment , int flags , int check , mode_t mode )
2003-12-19 18:06:29 +00:00
{
2005-10-16 16:12:51 +00:00
FILE * bfile ;
2003-12-19 18:06:29 +00:00
struct ast_format * f ;
2005-07-05 14:37:24 +00:00
struct ast_filestream * fs = NULL ;
2003-12-19 18:06:29 +00:00
char * fn ;
2005-07-05 14:37:24 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_RDLOCK ( & formats ) ;
2005-07-05 14:37:24 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_TRAVERSE ( & formats , f , list ) {
2006-04-04 12:59:25 +00:00
fs = NULL ;
2005-07-05 14:37:24 +00:00
if ( ! exts_compare ( f - > exts , type ) )
continue ;
fn = build_filename ( filename , type ) ;
2006-04-04 12:59:25 +00:00
errno = 0 ;
2005-10-16 16:12:51 +00:00
bfile = fopen ( fn , " r " ) ;
2006-04-04 12:59:25 +00:00
if ( ! bfile | | ( fs = get_filestream ( f , bfile ) ) = = NULL | |
2006-11-15 20:51:18 +00:00
open_wrapper ( fs ) ) {
2006-04-04 12:59:25 +00:00
ast_log ( LOG_WARNING , " Unable to open %s \n " , fn ) ;
if ( fs )
2007-04-08 01:42:32 +00:00
ast_free ( fs ) ;
2006-11-15 20:51:18 +00:00
if ( bfile )
fclose ( bfile ) ;
free ( fn ) ;
2006-04-04 12:59:25 +00:00
continue ;
}
/* found it */
fs - > trans = NULL ;
fs - > fmt = f ;
fs - > flags = flags ;
fs - > mode = mode ;
fs - > filename = strdup ( filename ) ;
fs - > vfs = NULL ;
break ;
2003-12-19 18:06:29 +00:00
}
2005-07-05 14:37:24 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats ) ;
2005-07-05 14:37:24 +00:00
if ( ! fs )
2003-12-19 18:06:29 +00:00
ast_log ( LOG_WARNING , " No such format '%s' \n " , type ) ;
2005-07-05 14:37:24 +00:00
2003-12-19 18:06:29 +00:00
return fs ;
}
1999-12-02 20:24:59 +00:00
2004-11-13 16:13:07 +00:00
struct ast_filestream * ast_writefile ( const char * filename , const char * type , const char * comment , int flags , int check , mode_t mode )
1999-12-02 20:24:59 +00:00
{
2005-07-05 14:37:24 +00:00
int fd , myflags = 0 ;
2005-10-16 23:26:35 +00:00
/* compiler claims this variable can be used before initialization... */
FILE * bfile = NULL ;
1999-12-02 20:24:59 +00:00
struct ast_format * f ;
2005-07-05 14:37:24 +00:00
struct ast_filestream * fs = NULL ;
char * buf = NULL ;
2004-09-07 01:49:08 +00:00
size_t size = 0 ;
2006-06-14 02:21:47 +00:00
int format_found = 0 ;
2004-09-07 01:49:08 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_RDLOCK ( & formats ) ;
2005-07-05 14:37:24 +00:00
2003-02-06 22:11:43 +00:00
/* set the O_TRUNC flag if and only if there is no O_APPEND specified */
2006-04-04 12:59:25 +00:00
/* We really can't use O_APPEND as it will break WAV header updates */
2005-02-03 05:35:03 +00:00
if ( flags & O_APPEND ) {
flags & = ~ O_APPEND ;
} else {
2003-12-19 18:06:29 +00:00
myflags = O_TRUNC ;
2005-02-03 05:35:03 +00:00
}
2003-12-19 18:06:29 +00:00
myflags | = O_WRONLY | O_CREAT ;
2006-04-04 12:59:25 +00:00
/* XXX need to fix this - we should just do the fopen,
* not open followed by fdopen ( )
*/
2007-01-26 22:55:06 +00:00
AST_RWLIST_TRAVERSE ( & formats , f , list ) {
2006-04-04 12:59:25 +00:00
char * fn , * orig_fn = NULL ;
2006-01-09 21:30:46 +00:00
if ( fs )
break ;
2005-07-05 14:37:24 +00:00
if ( ! exts_compare ( f - > exts , type ) )
continue ;
2006-06-14 02:21:47 +00:00
else
format_found = 1 ;
2005-07-05 14:37:24 +00:00
fn = build_filename ( filename , type ) ;
fd = open ( fn , flags | myflags , mode ) ;
2005-10-16 16:12:51 +00:00
if ( fd > - 1 ) {
/* fdopen() the resulting file stream */
bfile = fdopen ( fd , ( ( flags | myflags ) & O_RDWR ) ? " w+ " : " w " ) ;
if ( ! bfile ) {
ast_log ( LOG_WARNING , " Whoa, fdopen failed: %s! \n " , strerror ( errno ) ) ;
close ( fd ) ;
fd = - 1 ;
}
}
2005-07-05 14:37:24 +00:00
2005-12-04 20:40:46 +00:00
if ( ast_opt_cache_record_files & & ( fd > - 1 ) ) {
2005-07-05 14:37:24 +00:00
char * c ;
2006-04-04 12:59:25 +00:00
fclose ( bfile ) ; /* this also closes fd */
2005-07-05 14:37:24 +00:00
/*
We touch orig_fn just as a place - holder so other things ( like vmail ) see the file is there .
What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place .
*/
orig_fn = ast_strdupa ( fn ) ;
for ( c = fn ; * c ; c + + )
if ( * c = = ' / ' )
* c = ' _ ' ;
size = strlen ( fn ) + strlen ( record_cache_dir ) + 2 ;
buf = alloca ( size ) ;
2005-10-16 23:26:35 +00:00
strcpy ( buf , record_cache_dir ) ;
strcat ( buf , " / " ) ;
strcat ( buf , fn ) ;
2005-07-05 14:37:24 +00:00
free ( fn ) ;
fn = buf ;
2003-12-19 18:06:29 +00:00
fd = open ( fn , flags | myflags , mode ) ;
2005-10-16 16:12:51 +00:00
if ( fd > - 1 ) {
/* fdopen() the resulting file stream */
bfile = fdopen ( fd , ( ( flags | myflags ) & O_RDWR ) ? " w+ " : " w " ) ;
if ( ! bfile ) {
ast_log ( LOG_WARNING , " Whoa, fdopen failed: %s! \n " , strerror ( errno ) ) ;
close ( fd ) ;
fd = - 1 ;
}
}
2005-07-05 14:37:24 +00:00
}
2005-10-16 23:26:35 +00:00
if ( fd > - 1 ) {
2005-07-05 14:37:24 +00:00
errno = 0 ;
2006-04-04 12:59:25 +00:00
fs = get_filestream ( f , bfile ) ;
if ( ! fs | | rewrite_wrapper ( fs , comment ) ) {
2005-07-05 14:37:24 +00:00
ast_log ( LOG_WARNING , " Unable to rewrite %s \n " , fn ) ;
close ( fd ) ;
if ( orig_fn ) {
unlink ( fn ) ;
2004-09-07 01:49:08 +00:00
unlink ( orig_fn ) ;
2005-07-05 14:37:24 +00:00
}
2006-04-04 12:59:25 +00:00
if ( fs )
2007-04-08 01:42:32 +00:00
ast_free ( fs ) ;
2006-04-04 12:59:25 +00:00
}
fs - > trans = NULL ;
fs - > fmt = f ;
fs - > flags = flags ;
fs - > mode = mode ;
if ( orig_fn ) {
fs - > realfilename = strdup ( orig_fn ) ;
fs - > filename = strdup ( fn ) ;
} else {
fs - > realfilename = NULL ;
fs - > filename = strdup ( filename ) ;
2004-09-07 01:49:08 +00:00
}
2006-04-04 12:59:25 +00:00
fs - > vfs = NULL ;
/* If truncated, we'll be at the beginning; if not truncated, then append */
f - > seek ( fs , 0 , SEEK_END ) ;
2005-07-05 14:37:24 +00:00
} else if ( errno ! = EEXIST ) {
ast_log ( LOG_WARNING , " Unable to open file %s: %s \n " , fn , strerror ( errno ) ) ;
if ( orig_fn )
unlink ( orig_fn ) ;
1999-12-02 20:24:59 +00:00
}
2005-07-05 14:37:24 +00:00
/* if buf != NULL then fn is already free and pointing to it */
if ( ! buf )
free ( fn ) ;
1999-12-02 20:24:59 +00:00
}
2005-07-05 14:37:24 +00:00
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats ) ;
2006-06-14 02:21:47 +00:00
if ( ! format_found )
1999-12-02 20:24:59 +00:00
ast_log ( LOG_WARNING , " No such format '%s' \n " , type ) ;
2005-07-05 14:37:24 +00:00
1999-12-02 20:24:59 +00:00
return fs ;
}
2006-04-04 12:59:25 +00:00
/*!
* \ brief the core of all waitstream ( ) functions
*/
static int waitstream_core ( struct ast_channel * c , const char * breakon ,
const char * forward , const char * rewind , int skip_ms ,
int audiofd , int cmdfd , const char * context )
2003-04-03 07:37:30 +00:00
{
2005-02-06 06:43:32 +00:00
if ( ! breakon )
2006-04-04 12:59:25 +00:00
breakon = " " ;
2005-02-06 06:43:32 +00:00
if ( ! forward )
2006-04-04 12:59:25 +00:00
forward = " " ;
2005-02-06 06:43:32 +00:00
if ( ! rewind )
2006-04-04 12:59:25 +00:00
rewind = " " ;
2003-02-23 06:00:11 +00:00
2006-04-04 12:59:25 +00:00
while ( c - > stream ) {
int res ;
int ms = ast_sched_wait ( c - > sched ) ;
if ( ms < 0 & & ! c - > timingfunc ) {
2003-07-01 16:16:28 +00:00
ast_stopstream ( c ) ;
2003-02-23 06:00:11 +00:00
break ;
}
2003-06-29 21:54:58 +00:00
if ( ms < 0 )
ms = 1000 ;
2006-09-01 16:50:02 +00:00
if ( cmdfd < 0 ) {
2006-04-04 12:59:25 +00:00
res = ast_waitfor ( c , ms ) ;
if ( res < 0 ) {
ast_log ( LOG_WARNING , " Select failed (%s) \n " , strerror ( errno ) ) ;
return res ;
}
} else {
int outfd ;
struct ast_channel * rchan = ast_waitfor_nandfds ( & c , 1 , & cmdfd , ( cmdfd > - 1 ) ? 1 : 0 , NULL , & outfd , & ms ) ;
if ( ! rchan & & ( outfd < 0 ) & & ( ms ) ) {
/* Continue */
if ( errno = = EINTR )
continue ;
ast_log ( LOG_WARNING , " Wait failed (%s) \n " , strerror ( errno ) ) ;
2003-02-23 06:00:11 +00:00
return - 1 ;
2006-04-04 12:59:25 +00:00
} else if ( outfd > - 1 ) { /* this requires cmdfd set */
/* The FD we were watching has something waiting */
return 1 ;
2003-02-23 06:00:11 +00:00
}
2006-04-04 12:59:25 +00:00
/* if rchan is set, it is 'c' */
res = rchan ? 1 : 0 ; /* map into 'res' values */
}
if ( res > 0 ) {
struct ast_frame * fr = ast_read ( c ) ;
if ( ! fr )
return - 1 ;
2007-01-23 00:11:32 +00:00
switch ( fr - > frametype ) {
2006-08-31 01:59:02 +00:00
case AST_FRAME_DTMF_END :
2006-04-04 12:59:25 +00:00
if ( context ) {
const char exten [ 2 ] = { fr - > subclass , ' \0 ' } ;
if ( ast_exists_extension ( c , context , exten , 1 , c - > cid . cid_num ) ) {
2007-03-02 00:22:22 +00:00
res = fr - > subclass ;
2006-04-04 12:59:25 +00:00
ast_frfree ( fr ) ;
return res ;
}
} else {
res = fr - > subclass ;
if ( strchr ( forward , res ) ) {
ast_stream_fastforward ( c - > stream , skip_ms ) ;
} else if ( strchr ( rewind , res ) ) {
ast_stream_rewind ( c - > stream , skip_ms ) ;
} else if ( strchr ( breakon , res ) ) {
ast_frfree ( fr ) ;
return res ;
}
2003-02-23 06:00:11 +00:00
}
break ;
case AST_FRAME_CONTROL :
2007-01-23 00:11:32 +00:00
switch ( fr - > subclass ) {
2003-02-23 06:00:11 +00:00
case AST_CONTROL_HANGUP :
2006-10-05 19:58:50 +00:00
case AST_CONTROL_BUSY :
2006-10-06 16:56:05 +00:00
case AST_CONTROL_CONGESTION :
2003-02-23 06:00:11 +00:00
ast_frfree ( fr ) ;
return - 1 ;
case AST_CONTROL_RINGING :
case AST_CONTROL_ANSWER :
2006-05-08 07:56:42 +00:00
case AST_CONTROL_VIDUPDATE :
2006-09-28 11:12:58 +00:00
case AST_CONTROL_HOLD :
case AST_CONTROL_UNHOLD :
2003-02-23 06:00:11 +00:00
/* Unimportant */
break ;
default :
ast_log ( LOG_WARNING , " Unexpected control subclass '%d' \n " , fr - > subclass ) ;
}
2006-09-28 11:12:58 +00:00
break ;
2003-02-23 06:00:11 +00:00
case AST_FRAME_VOICE :
/* Write audio if appropriate */
if ( audiofd > - 1 )
write ( audiofd , fr - > data , fr - > datalen ) ;
2006-08-31 01:59:02 +00:00
default :
/* Ignore all others */
break ;
2003-02-23 06:00:11 +00:00
}
ast_frfree ( fr ) ;
2003-05-12 04:23:55 +00:00
}
ast_sched_runq ( c - > sched ) ;
2003-02-23 06:00:11 +00:00
}
return ( c - > _softhangup ? - 1 : 0 ) ;
}
2004-06-30 03:22:29 +00:00
2006-04-04 12:59:25 +00:00
int ast_waitstream_fr ( struct ast_channel * c , const char * breakon , const char * forward , const char * rewind , int ms )
{
return waitstream_core ( c , breakon , forward , rewind , ms ,
- 1 /* no audiofd */ , - 1 /* no cmdfd */ , NULL /* no context */ ) ;
}
int ast_waitstream ( struct ast_channel * c , const char * breakon )
{
return waitstream_core ( c , breakon , NULL , NULL , 0 , - 1 , - 1 , NULL ) ;
}
int ast_waitstream_full ( struct ast_channel * c , const char * breakon , int audiofd , int cmdfd )
{
return waitstream_core ( c , breakon , NULL , NULL , 0 ,
audiofd , cmdfd , NULL /* no context */ ) ;
}
2005-07-12 01:34:06 +00:00
int ast_waitstream_exten ( struct ast_channel * c , const char * context )
{
/* Waitstream, with return in the case of a valid 1 digit extension */
/* in the current or specified context being pressed */
2006-04-04 12:59:25 +00:00
if ( ! context )
context = c - > context ;
return waitstream_core ( c , NULL , NULL , NULL , 0 ,
- 1 , - 1 , context ) ;
2005-07-12 01:34:06 +00:00
}
2006-04-21 20:28:32 +00:00
/*
* if the file name is non - empty , try to play it .
* Return 0 if success , - 1 if error , digit if interrupted by a digit .
* If digits = = " " then we can simply check for non - zero .
*/
2006-11-17 23:18:51 +00:00
int ast_stream_and_wait ( struct ast_channel * chan , const char * file , const char * digits )
2006-04-21 20:28:32 +00:00
{
int res = 0 ;
if ( ! ast_strlen_zero ( file ) ) {
2006-11-17 23:18:51 +00:00
res = ast_streamfile ( chan , file , chan - > language ) ;
2006-04-21 20:28:32 +00:00
if ( ! res )
res = ast_waitstream ( chan , digits ) ;
}
return res ;
}
2004-06-30 03:22:29 +00:00
static int show_file_formats ( int fd , int argc , char * argv [ ] )
{
# define FORMAT "%-10s %-10s %-20s\n"
# define FORMAT2 "%-10s %-10s %-20s\n"
struct ast_format * f ;
2005-08-23 15:43:47 +00:00
int count_fmt = 0 ;
2007-01-26 23:49:29 +00:00
if ( argc ! = 4 )
2004-06-30 03:22:29 +00:00
return RESULT_SHOWUSAGE ;
ast_cli ( fd , FORMAT , " Format " , " Name " , " Extensions " ) ;
2007-01-26 22:55:06 +00:00
AST_RWLIST_RDLOCK ( & formats ) ;
AST_RWLIST_TRAVERSE ( & formats , f , list ) {
2004-06-30 03:22:29 +00:00
ast_cli ( fd , FORMAT2 , ast_getformatname ( f - > format ) , f - > name , f - > exts ) ;
2005-08-23 15:43:47 +00:00
count_fmt + + ;
2006-01-09 21:30:46 +00:00
}
2007-01-26 22:55:06 +00:00
AST_RWLIST_UNLOCK ( & formats ) ;
2005-08-23 15:43:47 +00:00
ast_cli ( fd , " %d file formats registered. \n " , count_fmt ) ;
2004-06-30 03:22:29 +00:00
return RESULT_SUCCESS ;
2005-07-15 16:13:26 +00:00
# undef FORMAT
# undef FORMAT2
2004-06-30 03:22:29 +00:00
}
2006-12-06 07:28:56 +00:00
static const char show_file_formats_usage [ ] =
2006-11-02 23:16:09 +00:00
" Usage: core show file formats \n "
2006-09-18 19:54:18 +00:00
" Displays currently registered file formats (if any) \n " ;
struct ast_cli_entry cli_file [ ] = {
2006-11-02 23:16:09 +00:00
{ { " core " , " show " , " file " , " formats " } ,
2006-09-18 19:54:18 +00:00
show_file_formats , " Displays file formats " ,
2006-09-21 21:17:39 +00:00
show_file_formats_usage } ,
2004-06-30 03:22:29 +00:00
} ;
int ast_file_init ( void )
{
2006-09-18 19:54:18 +00:00
ast_cli_register_multiple ( cli_file , sizeof ( cli_file ) / sizeof ( struct ast_cli_entry ) ) ;
2004-06-30 03:22:29 +00:00
return 0 ;
}