2003-06-28 18:35:58 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk -- An open source telephony toolkit.
*
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>
*
* 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.
*
* This program is free software, distributed under the terms of
* 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
1999-12-02 20:24:59 +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 );
2007-06-06 21:20:11 +00:00
ast_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 );
}
2007-05-06 13:11:15 +00:00
/* Stop the video stream too */
if ( tmp -> vstream != NULL ) {
ast_closestream ( tmp -> vstream );
tmp -> vstream = NULL ;
}
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 );
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "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 */
2007-06-06 21:20:11 +00:00
ast_free ( fn );
2006-04-04 12:59:25 +00:00
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 )) {
2007-06-06 21:20:11 +00:00
ast_free ( fn );
2006-04-04 12:59:25 +00:00
continue ; /* not a supported format */
1999-12-02 20:24:59 +00:00
}
2006-04-04 12:59:25 +00:00
if ( ( bfile = fopen ( fn , "r" )) == NULL ) {
2007-06-06 21:20:11 +00:00
ast_free ( fn );
2006-04-04 12:59:25 +00:00
continue ; /* cannot open file */
}
s = get_filestream ( f , bfile );
if ( ! s ) {
fclose ( bfile );
2007-06-06 21:20:11 +00:00
ast_free ( fn ); /* cannot allocate descriptor */
2006-04-04 12:59:25 +00:00
continue ;
}
if ( open_wrapper ( s )) {
fclose ( bfile );
2007-06-06 21:20:11 +00:00
ast_free ( fn );
ast_free ( s );
2006-04-04 12:59:25 +00:00
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
}
2007-06-06 21:20:11 +00:00
ast_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 */
2006-04-04 12:59:25 +00:00
2006-04-06 15:08:31 +00:00
case ACTION_EXISTS : /* return the matching format */
res |= f -> format ;
break ;
2006-04-04 12:59:25 +00:00
2006-04-06 15:08:31 +00:00
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 ));
2007-06-06 21:20:11 +00:00
ast_free ( nfn );
2006-04-06 15:08:31 +00:00
}
}
break ;
default :
ast_log ( LOG_WARNING , "Unknown helper %d \n " , action );
2006-04-04 12:59:25 +00:00
}
2007-06-06 21:20:11 +00:00
ast_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 )
2003-02-06 22:11:43 +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 ;
2003-02-06 22:11:43 +00:00
if ( fmts < 1 ) {
ast_log ( LOG_WARNING , "File %s does not exist in any format \n " , filename );
return NULL ;
}
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 )
{
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 ;
}
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-02-06 22:11:43 +00:00
int ast_applystream ( struct ast_channel * chan , struct ast_filestream * s )
{
2003-06-28 22:50:47 +00:00
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 )
2007-06-06 21:20:11 +00:00
ast_free ( f -> filename );
2006-04-04 12:59:25 +00:00
if ( f -> realfilename )
2007-06-06 21:20:11 +00:00
ast_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 );
2007-06-06 21:20:11 +00:00
ast_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 )
1999-12-02 20:24:59 +00:00
{
2006-04-04 12:59:25 +00:00
char * buf ;
int buflen ;
2005-05-30 13:16:29 +00:00
2006-04-04 12:59:25 +00:00
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 );
1999-12-02 20:24:59 +00:00
}
2004-11-13 16:13:07 +00:00
int ast_filedelete ( const char * filename , const char * fmt )
1999-12-02 20:24:59 +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 )
1999-12-02 20:24:59 +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 )
2001-06-13 20:36:52 +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 )
1999-12-02 20:24:59 +00:00
{
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 ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , "Ooh, found a video stream, too, format %s \n " , ast_getformatname ( vfs -> fmt -> format ));
2006-10-03 15:53:07 +00:00
}
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 ;
2007-06-22 15:03:32 +00:00
ast_playstream ( fs );
if ( vfs )
ast_playstream ( vfs );
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 )
1999-12-02 20:24:59 +00:00
{
2005-10-16 16:12:51 +00:00
FILE * bfile ;
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 ;
1999-12-02 20:24:59 +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 );
2007-06-06 21:20:11 +00:00
ast_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 ;
2007-06-14 23:01:01 +00:00
fs -> filename = ast_strdup ( filename );
2006-04-04 12:59:25 +00:00
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 ;
}
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 )
2003-12-19 18:06:29 +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 ;
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 ;
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-12-19 18:06:29 +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 );
2007-06-06 21:20:11 +00:00
ast_free ( fn );
2005-07-05 14:37:24 +00:00
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 ;
}
}
1999-12-02 20:24:59 +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 );
unlink ( orig_fn );
}
2006-04-04 12:59:25 +00:00
if ( fs )
2007-04-08 01:42:32 +00:00
ast_free ( fs );
2007-06-06 23:41:37 +00:00
fs = NULL ;
continue ;
2005-07-05 14:37:24 +00:00
}
2006-04-04 12:59:25 +00:00
fs -> trans = NULL ;
fs -> fmt = f ;
fs -> flags = flags ;
fs -> mode = mode ;
if ( orig_fn ) {
2007-06-14 23:01:01 +00:00
fs -> realfilename = ast_strdup ( orig_fn );
fs -> filename = ast_strdup ( fn );
2006-04-04 12:59:25 +00:00
} else {
fs -> realfilename = NULL ;
2007-06-14 23:01:01 +00:00
fs -> filename = ast_strdup ( filename );
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 );
}
/* if buf != NULL then fn is already free and pointing to it */
if ( ! buf )
2007-06-06 21:20:11 +00:00
ast_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 )
1999-12-02 20:24:59 +00:00
{
2005-02-06 06:43:32 +00:00
if ( ! breakon )
breakon = "" ;
2006-04-04 12:59:25 +00:00
if ( ! forward )
forward = "" ;
if ( ! rewind )
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 ;
2003-02-23 06:00:11 +00:00
}
2006-04-04 12:59:25 +00:00
} 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 ));
return - 1 ;
} else if ( outfd > - 1 ) { /* this requires cmdfd set */
/* The FD we were watching has something waiting */
return 1 ;
}
/* 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 ;
}