2002-03-24 23:00:47 +00:00
/*
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2004-01-08 08:18:22 +00:00
| PHP Version 5 |
2002-03-24 23:00:47 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2007-01-01 09:36:18 +00:00
| Copyright ( c ) 1997 - 2007 The PHP Group |
2002-03-24 23:00:47 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2006-01-01 12:51:34 +00:00
| This source file is subject to version 3.01 of the PHP license , |
2002-03-24 23:00:47 +00:00
| that is bundled with this package in the file LICENSE , and is |
2003-06-10 20:04:29 +00:00
| available through the world - wide - web at the following url : |
2006-01-01 12:51:34 +00:00
| http : //www.php.net/license/3_01.txt |
2002-03-24 23:00:47 +00:00
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world - wide - web , please send a note to |
| license @ php . net so we can mail you a copy immediately . |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2002-11-25 12:30:28 +00:00
| Author : Hartmut Holzgraefe < hholzgra @ php . net > |
2002-03-24 23:00:47 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
$ Id $
This module contains a lot of stuff taken from Apache mod_mime_magic ,
2002-09-24 06:39:43 +00:00
so the license section is a little bit longer than usual :
2002-03-24 23:00:47 +00:00
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* The Apache Software License , Version 1.1
*
* Copyright ( c ) 2000 The Apache Software Foundation . All rights
* reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in
* the documentation and / or other materials provided with the
* distribution .
*
* 3. The end - user documentation included with the redistribution ,
* if any , must include the following acknowledgment :
* " This product includes software developed by the
* Apache Software Foundation ( http : //www.apache.org/)."
* Alternately , this acknowledgment may appear in the software itself ,
* if and wherever such third - party acknowledgments normally appear .
*
* 4. The names " Apache " and " Apache Software Foundation " must
* not be used to endorse or promote products derived from this
* software without prior written permission . For written
* permission , please contact apache @ apache . org .
*
* 5. Products derived from this software may not be called " Apache " ,
* nor may " Apache " appear in their name , without prior written
* permission of the Apache Software Foundation .
*
* THIS SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
* LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF
* USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation . For more
* information on the Apache Software Foundation , please see
* < http : //www.apache.org/>.
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications ,
* University of Illinois , Urbana - Champaign .
*/
/*
* mod_mime_magic : MIME type lookup via file magic numbers
* Copyright ( c ) 1996 - 1997 Cisco Systems , Inc .
*
* This software was submitted by Cisco Systems to the Apache Group in July
* 1997. Future revisions and derivatives of this source code must
* acknowledge Cisco Systems as the original contributor of this module .
* All other licensing and usage conditions are those of the Apache Group .
*
* Some of this code is derived from the free version of the file command
* originally posted to comp . sources . unix . Copyright info for that program
* is included below as required .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* - Copyright ( c ) Ian F . Darwin , 1987. Written by Ian F . Darwin .
*
* This software is not subject to any license of the American Telephone and
* Telegraph Company or of the Regents of the University of California .
*
* Permission is granted to anyone to use this software for any purpose on any
* computer system , and to alter it and redistribute it freely , subject to
* the following restrictions :
*
* 1. The author is not responsible for the consequences of use of this
* software , no matter how awful , even if they arise from flaws in it .
*
* 2. The origin of this software must not be misrepresented , either by
* explicit claim or by omission . Since few users ever read sources , credits
* must appear in the documentation .
*
* 3. Altered versions must be plainly marked as such , and must not be
* misrepresented as being the original software . Since few users ever read
* sources , credits must appear in the documentation .
*
* 4. This notice may not be removed or altered .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* For compliance with Mr Darwin ' s terms : this has been very significantly
* modified from the free " file " command .
* - all - in - one file for compilation convenience when moving from one
* version of Apache to the next .
* - Memory allocation is done through the Apache API ' s pool structure .
* - All functions have had necessary Apache API request or server
* structures passed to them where necessary to call other Apache API
* routines . ( i . e . usually for logging , files , or memory allocation in
* itself or a called function . )
* - struct magic has been converted from an array to a single - ended linked
* list because it only grows one record at a time , it ' s only accessed
* sequentially , and the Apache API has no equivalent of realloc ( ) .
* - Functions have been changed to get their parameters from the server
* configuration instead of globals . ( It should be reentrant now but has
* not been tested in a threaded environment . )
* - Places where it used to print results to stdout now saves them in a
* list where they ' re used to set the MIME type in the Apache request
* record .
* - Command - line flags have been removed since they will never be used here .
*
* Ian Kluft < ikluft @ cisco . com >
* Engineering Information Framework
* Central Engineering
* Cisco Systems , Inc .
* San Jose , CA , USA
*
* Initial installation July / August 1996
* Misc bug fixes May 1997
* Submission to Apache Group July 1997
*
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
2002-06-11 09:37:31 +00:00
2002-03-24 23:00:47 +00:00
# include "php.h"
2002-06-11 09:37:31 +00:00
# include "php_streams.h"
2002-03-24 23:00:47 +00:00
# include "php_ini.h"
# include "ext/standard/info.h"
# include "php_mime_magic.h"
2003-11-04 05:15:19 +00:00
# include "phpmimemagic.h"
2002-03-24 23:00:47 +00:00
# include <fcntl.h>
# include <sys/types.h>
2002-08-22 13:16:03 +00:00
# ifdef HAVE_UNISTD_H
2002-03-24 23:00:47 +00:00
# include <unistd.h>
2002-08-22 13:16:03 +00:00
# endif
# ifdef PHP_WIN32
2002-08-22 13:23:51 +00:00
# define PHP_MIME_MAGIC_FILE_PATH PHP_PREFIX "\\magic.mime"
2002-08-22 13:16:03 +00:00
# endif
2002-03-24 23:00:47 +00:00
2003-08-19 22:13:01 +00:00
# define BYTE 1
# define SHORT 2
# define LONG 4
# define STRING 5
# define DATE 6
# define BESHORT 7
# define BELONG 8
# define BEDATE 9
# define LESHORT 10
# define LELONG 11
# define LEDATE 12
2002-03-27 13:51:55 +00:00
static int apprentice ( void ) ;
static int ascmagic ( unsigned char * , int ) ;
2003-09-26 07:47:01 +00:00
static int is_tar ( unsigned char * , unsigned int ) ;
2002-03-27 13:51:55 +00:00
static int softmagic ( unsigned char * , int ) ;
static void tryit ( unsigned char * , int , int ) ;
static int getvalue ( struct magic * , char * * ) ;
static int hextoint ( int ) ;
static char * getstr ( char * , char * , int , int * ) ;
static int parse ( char * , int ) ;
static int match ( unsigned char * , int ) ;
static int mget ( union VALUETYPE * , unsigned char * ,
struct magic * , int ) ;
static int mcheck ( union VALUETYPE * , struct magic * ) ;
static void mprint ( union VALUETYPE * , struct magic * ) ;
static int mconvert ( union VALUETYPE * , struct magic * ) ;
static int magic_rsl_get ( char * * , char * * ) ;
2003-06-04 14:21:40 +00:00
static int magic_process ( zval * TSRMLS_DC ) ;
2002-03-27 13:51:55 +00:00
static long from_oct ( int , char * ) ;
2003-06-04 14:21:40 +00:00
static int fsmagic ( zval * TSRMLS_DC ) ;
2002-03-27 13:51:55 +00:00
2003-02-09 19:09:52 +00:00
# if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
2002-03-27 13:51:55 +00:00
static int zmagic ( unsigned char * , int ) ;
# endif
static magic_req_rec * magic_set_config ( void ) ;
static void magic_free_config ( magic_req_rec * ) ;
2002-03-24 23:00:47 +00:00
ZEND_DECLARE_MODULE_GLOBALS ( mime_magic )
2006-06-15 18:33:09 +00:00
static PHP_GINIT_FUNCTION ( mime_magic ) ;
2002-03-24 23:00:47 +00:00
/* True global resources - no need for thread safety here */
static magic_server_config_rec mime_global ;
2002-06-11 09:37:31 +00:00
/* {{{ mime_magic_functions[] */
2005-12-06 02:28:41 +00:00
zend_function_entry mime_magic_functions [ ] = {
2002-06-11 09:37:31 +00:00
PHP_FE ( mime_content_type , NULL )
{ NULL , NULL , NULL }
2002-03-24 23:00:47 +00:00
} ;
/* }}} */
/* {{{ mime_magic_module_entry
*/
zend_module_entry mime_magic_module_entry = {
# if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER ,
# endif
" mime_magic " ,
mime_magic_functions ,
PHP_MINIT ( mime_magic ) ,
PHP_MSHUTDOWN ( mime_magic ) ,
2002-06-11 09:37:31 +00:00
NULL ,
NULL ,
2002-03-24 23:00:47 +00:00
PHP_MINFO ( mime_magic ) ,
# if ZEND_MODULE_API_NO >= 20010901
2002-06-11 09:37:31 +00:00
" 0.1 " ,
2002-03-24 23:00:47 +00:00
# endif
2006-06-15 18:33:09 +00:00
# if ZEND_MODULE_API_NO >= 20060613
PHP_MODULE_GLOBALS ( mime_magic ) ,
PHP_GINIT ( mime_magic ) ,
NULL ,
NULL ,
STANDARD_MODULE_PROPERTIES_EX
# else
2002-03-24 23:00:47 +00:00
STANDARD_MODULE_PROPERTIES
2006-06-15 18:33:09 +00:00
# endif
2002-03-24 23:00:47 +00:00
} ;
/* }}} */
# ifdef COMPILE_DL_MIME_MAGIC
ZEND_GET_MODULE ( mime_magic )
# endif
/* {{{ PHP_INI
*/
PHP_INI_BEGIN ( )
2002-08-22 12:21:25 +00:00
STD_PHP_INI_ENTRY ( " mime_magic.magicfile " , PHP_MIME_MAGIC_FILE_PATH , PHP_INI_SYSTEM , OnUpdateString , magicfile , zend_mime_magic_globals , mime_magic_globals )
2004-01-16 11:15:13 +00:00
STD_PHP_INI_BOOLEAN ( " mime_magic.debug " , " 0 " , PHP_INI_SYSTEM , OnUpdateBool , debug , zend_mime_magic_globals , mime_magic_globals )
2002-03-24 23:00:47 +00:00
PHP_INI_END ( )
/* }}} */
2006-06-15 18:33:09 +00:00
/* {{{ PHP_GINIT_FUNCTION
2002-03-24 23:00:47 +00:00
*/
2006-06-15 18:33:09 +00:00
static PHP_GINIT_FUNCTION ( mime_magic )
2002-03-24 23:00:47 +00:00
{
2005-10-18 12:41:29 +00:00
memset ( mime_magic_globals , 0 , sizeof ( zend_mime_magic_globals ) ) ;
mime_global . magic = NULL ;
mime_global . last = NULL ;
2002-03-24 23:00:47 +00:00
}
/* }}} */
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION ( mime_magic )
{
REGISTER_INI_ENTRIES ( ) ;
2005-10-14 15:30:39 +00:00
if ( MIME_MAGIC_G ( magicfile ) ) {
2003-01-25 16:25:37 +00:00
if ( apprentice ( ) ) {
MIME_MAGIC_G ( status ) = " invalid magic file, disabled " ;
} else {
MIME_MAGIC_G ( status ) = " enabled " ;
}
} else {
MIME_MAGIC_G ( status ) = " no magic file given, disabled " ;
2002-03-24 23:00:47 +00:00
}
2003-01-25 16:25:37 +00:00
2002-03-24 23:00:47 +00:00
return SUCCESS ;
}
/* }}} */
/* {{{ PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION ( mime_magic )
{
UNREGISTER_INI_ENTRIES ( ) ;
2002-12-24 23:28:05 +00:00
if ( mime_global . magic ! = NULL & & mime_global . magic ! = ( struct magic * ) - 1 ) {
2002-11-18 21:15:41 +00:00
struct magic * iter = mime_global . magic ;
while ( iter ! = NULL ) {
struct magic * iter_next = iter - > next ;
free ( iter ) ;
iter = iter_next ;
}
}
2002-03-24 23:00:47 +00:00
return SUCCESS ;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION ( mime_magic )
{
php_info_print_table_start ( ) ;
2003-01-25 16:25:37 +00:00
php_info_print_table_header ( 2 , " mime_magic support " , MIME_MAGIC_G ( status ) ) ;
2002-03-24 23:00:47 +00:00
php_info_print_table_end ( ) ;
DISPLAY_INI_ENTRIES ( ) ;
}
/* }}} */
2003-06-04 14:21:40 +00:00
/* {{{ proto string mime_content_type(string filename|resource stream)
2002-03-24 23:00:47 +00:00
Return content - type for file */
PHP_FUNCTION ( mime_content_type )
{
2003-06-04 14:21:40 +00:00
zval * what ;
2002-03-24 23:00:47 +00:00
magic_server_config_rec * conf = & mime_global ;
char * content_type = NULL , * content_encoding = NULL ;
2003-06-04 14:21:40 +00:00
if ( zend_parse_parameters ( ZEND_NUM_ARGS ( ) TSRMLS_CC , " z " , & what ) = = FAILURE ) {
2002-03-24 23:00:47 +00:00
return ;
}
2003-06-04 14:21:40 +00:00
switch ( Z_TYPE_P ( what ) ) {
case IS_STRING :
break ;
case IS_RESOURCE :
{
php_stream * stream ;
2003-06-05 14:42:12 +00:00
php_stream_from_zval_no_verify ( stream , & what ) ;
2003-06-04 14:21:40 +00:00
if ( stream ) {
break ;
}
}
/* fallthru if not a stream resource */
default :
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " can only process string or stream arguments " ) ;
2005-05-25 03:05:47 +00:00
RETURN_FALSE ;
2003-06-04 14:21:40 +00:00
break ;
}
2002-12-24 23:28:05 +00:00
if ( conf - > magic = = ( struct magic * ) - 1 ) {
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
2005-10-18 12:41:29 +00:00
php_error_docref ( " http://www.php.net/mime_magic " TSRMLS_CC , E_ERROR , " mime_magic could not be initialized, magic file %s is not available " , MIME_MAGIC_G ( magicfile ) ) ;
2002-11-15 15:13:30 +00:00
RETURN_FALSE ;
}
2002-03-24 23:00:47 +00:00
if ( ! conf - > magic ) {
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( " http://www.php.net/mime_magic " TSRMLS_CC , E_WARNING , " mime_magic not initialized " ) ;
2002-03-24 23:00:47 +00:00
RETURN_FALSE ;
}
2002-05-21 10:14:12 +00:00
MIME_MAGIC_G ( req_dat ) = magic_set_config ( ) ;
2002-03-24 23:00:47 +00:00
2003-06-04 14:21:40 +00:00
if ( MIME_MAGIC_OK ! = magic_process ( what TSRMLS_CC ) ) {
2002-03-24 23:00:47 +00:00
RETVAL_FALSE ;
2002-03-26 19:57:32 +00:00
} else if ( MIME_MAGIC_OK ! = magic_rsl_get ( & content_type , & content_encoding ) ) {
2002-03-24 23:00:47 +00:00
RETVAL_FALSE ;
} else {
RETVAL_STRING ( content_type , 1 ) ;
}
if ( content_type ) efree ( content_type ) ;
if ( content_encoding ) efree ( content_encoding ) ;
magic_free_config ( MIME_MAGIC_G ( req_dat ) ) ;
}
/* }}} */
# define EATAB {while (isspace((unsigned char) *l)) ++l;}
/*
* apprentice - load configuration from the magic file r
* API request record
*/
static int apprentice ( void )
{
FILE * f ;
char line [ BUFSIZ + 1 ] ;
int errs = 0 ;
int lineno ;
char * fname ;
magic_server_config_rec * conf = & mime_global ;
2003-01-26 09:13:31 +00:00
TSRMLS_FETCH ( ) ;
2002-03-24 23:00:47 +00:00
2005-10-18 12:41:29 +00:00
if ( ! MIME_MAGIC_G ( magicfile ) ) {
return - 1 ;
}
fname = MIME_MAGIC_G ( magicfile ) ; /* todo cwd? */
2002-11-15 16:25:31 +00:00
f = fopen ( fname , " rb " ) ;
2002-03-24 23:00:47 +00:00
if ( f = = NULL ) {
2002-12-24 23:28:05 +00:00
conf - > magic = ( struct magic * ) - 1 ;
2002-03-24 23:00:47 +00:00
return - 1 ;
}
/* set up the magic list (empty) */
conf - > magic = conf - > last = NULL ;
/* parse it */
for ( lineno = 1 ; fgets ( line , BUFSIZ , f ) ! = NULL ; lineno + + ) {
int ws_offset ;
/* delete newline */
if ( line [ 0 ] ) {
line [ strlen ( line ) - 1 ] = ' \0 ' ;
}
/* skip leading whitespace */
ws_offset = 0 ;
while ( line [ ws_offset ] & & isspace ( line [ ws_offset ] ) ) {
ws_offset + + ;
}
/* skip blank lines */
if ( line [ ws_offset ] = = 0 ) {
continue ;
}
/* comment, do not parse */
if ( line [ ws_offset ] = = ' # ' )
continue ;
/* parse it */
if ( parse ( line + ws_offset , lineno ) ! = 0 )
+ + errs ;
}
( void ) fclose ( f ) ;
return ( errs ? - 1 : 0 ) ;
}
/*
* extend the sign bit if the comparison is to be signed
*/
static unsigned long signextend ( struct magic * m , unsigned long v )
{
if ( ! ( m - > flag & UNSIGNED ) )
switch ( m - > type ) {
/*
* Do not remove the casts below . They are vital . When later
* compared with the data , the sign extension must have happened .
*/
case BYTE :
v = ( char ) v ;
break ;
case SHORT :
case BESHORT :
case LESHORT :
v = ( short ) v ;
break ;
case DATE :
case BEDATE :
case LEDATE :
case LONG :
case BELONG :
case LELONG :
v = ( long ) v ;
break ;
case STRING :
break ;
2003-01-19 00:45:53 +00:00
default :
{
TSRMLS_FETCH ( ) ;
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( " http://www.php.net/mime_magic " TSRMLS_CC , E_WARNING , " : can't happen: m->type=%d " , m - > type ) ;
2002-03-24 23:00:47 +00:00
return - 1 ;
2003-01-19 00:45:53 +00:00
}
2002-03-24 23:00:47 +00:00
}
return v ;
}
2003-01-25 16:25:37 +00:00
/*
*
*/
2004-01-15 06:09:16 +00:00
static int is_valid_mimetype ( char * p , int p_len )
2003-01-25 16:25:37 +00:00
{
2004-01-15 06:09:16 +00:00
if ( p_len > 0 ) {
do {
if ( ! isalnum ( * p ) & & ( * p ! = ' - ' ) & & ( * p ! = ' . ' ) ) {
return 0 ;
}
} while ( * ( + + p ) ! = ' / ' ) ;
+ + p ;
do {
if ( ! isalnum ( * p ) & & ( * p ! = ' - ' ) & & ( * p ! = ' . ' ) & & ! isspace ( * p ) ) {
return 0 ;
}
} while ( * ( + + p ) ) ;
}
2003-01-25 16:25:37 +00:00
return 1 ;
}
2002-03-24 23:00:47 +00:00
/*
* parse one line from magic file , put into magic [ index + + ] if valid
*/
static int parse ( char * l , int lineno )
{
struct magic * m ;
char * t , * s ;
magic_server_config_rec * conf = & mime_global ;
2003-01-19 00:45:53 +00:00
TSRMLS_FETCH ( ) ;
2002-03-24 23:00:47 +00:00
/* allocate magic structure entry */
m = ( struct magic * ) calloc ( 1 , sizeof ( struct magic ) ) ;
/* append to linked list */
m - > next = NULL ;
if ( ! conf - > magic | | ! conf - > last ) {
conf - > magic = conf - > last = m ;
}
else {
conf - > last - > next = m ;
conf - > last = m ;
}
/* set values in magic structure */
m - > flag = 0 ;
m - > cont_level = 0 ;
m - > lineno = lineno ;
while ( * l = = ' > ' ) {
+ + l ; /* step over */
m - > cont_level + + ;
}
if ( m - > cont_level ! = 0 & & * l = = ' ( ' ) {
+ + l ; /* step over */
m - > flag | = INDIR ;
}
/* get offset, then skip over it */
m - > offset = ( int ) strtol ( l , & t , 0 ) ;
if ( l = = t ) {
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( " http://www.php.net/mime_magic " TSRMLS_CC , E_WARNING , " : (%s:%d) offset `%s' invalid " , MIME_MAGIC_G ( magicfile ) , lineno , l ) ;
2002-03-24 23:00:47 +00:00
}
l = t ;
if ( m - > flag & INDIR ) {
m - > in . type = LONG ;
m - > in . offset = 0 ;
/*
* read [ . lbs ] [ + - ] nnnnn )
*/
if ( * l = = ' . ' ) {
switch ( * + + l ) {
case ' l ' :
m - > in . type = LONG ;
break ;
case ' s ' :
m - > in . type = SHORT ;
break ;
case ' b ' :
m - > in . type = BYTE ;
break ;
default :
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( " http://www.php.net/mime_magic " TSRMLS_CC , E_WARNING , " : (%s:%d) indirect offset type %c invalid " , MIME_MAGIC_G ( magicfile ) , lineno , * l ) ;
2002-03-24 23:00:47 +00:00
break ;
}
l + + ;
}
s = l ;
if ( * l = = ' + ' | | * l = = ' - ' )
l + + ;
if ( isdigit ( ( unsigned char ) * l ) ) {
m - > in . offset = strtol ( l , & t , 0 ) ;
if ( * s = = ' - ' )
m - > in . offset = - m - > in . offset ;
}
else
t = l ;
if ( * t + + ! = ' ) ' ) {
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
2003-09-26 07:47:01 +00:00
php_error_docref ( " http://www.php.net/mime_magic " TSRMLS_CC , E_WARNING , " : (%s:%d) missing ')' in indirect offset " , MIME_MAGIC_G ( magicfile ) , lineno ) ;
2002-03-24 23:00:47 +00:00
}
l = t ;
}
while ( isdigit ( ( unsigned char ) * l ) )
+ + l ;
EATAB ;
# define NBYTE 4
# define NSHORT 5
# define NLONG 4
# define NSTRING 6
# define NDATE 4
# define NBESHORT 7
# define NBELONG 6
# define NBEDATE 6
# define NLESHORT 7
# define NLELONG 6
# define NLEDATE 6
if ( * l = = ' u ' ) {
+ + l ;
m - > flag | = UNSIGNED ;
}
/* get type, skip it */
if ( strncmp ( l , " byte " , NBYTE ) = = 0 ) {
m - > type = BYTE ;
l + = NBYTE ;
}
else if ( strncmp ( l , " short " , NSHORT ) = = 0 ) {
m - > type = SHORT ;
l + = NSHORT ;
}
else if ( strncmp ( l , " long " , NLONG ) = = 0 ) {
m - > type = LONG ;
l + = NLONG ;
}
else if ( strncmp ( l , " string " , NSTRING ) = = 0 ) {
m - > type = STRING ;
l + = NSTRING ;
}
else if ( strncmp ( l , " date " , NDATE ) = = 0 ) {
m - > type = DATE ;
l + = NDATE ;
}
else if ( strncmp ( l , " beshort " , NBESHORT ) = = 0 ) {
m - > type = BESHORT ;
l + = NBESHORT ;
}
else if ( strncmp ( l , " belong " , NBELONG ) = = 0 ) {
m - > type = BELONG ;
l + = NBELONG ;
}
else if ( strncmp ( l , " bedate " , NBEDATE ) = = 0 ) {
m - > type = BEDATE ;
l + = NBEDATE ;
}
else if ( strncmp ( l , " leshort " , NLESHORT ) = = 0 ) {
m - > type = LESHORT ;
l + = NLESHORT ;
}
else if ( strncmp ( l , " lelong " , NLELONG ) = = 0 ) {
m - > type = LELONG ;
l + = NLELONG ;
}
else if ( strncmp ( l , " ledate " , NLEDATE ) = = 0 ) {
m - > type = LEDATE ;
l + = NLEDATE ;
}
else {
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( " http://www.php.net/mime_magic " TSRMLS_CC , E_WARNING , " : (%s:%d) type %s invalid " , MIME_MAGIC_G ( magicfile ) , lineno , l ) ;
2002-03-24 23:00:47 +00:00
return - 1 ;
}
/* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
if ( * l = = ' & ' ) {
+ + l ;
m - > mask = signextend ( m , strtol ( l , & l , 0 ) ) ;
}
else
m - > mask = ~ 0L ;
EATAB ;
switch ( * l ) {
case ' > ' :
case ' < ' :
/* Old-style anding: "0 byte &0x80 dynamically linked" */
case ' & ' :
case ' ^ ' :
case ' = ' :
m - > reln = * l ;
+ + l ;
break ;
case ' ! ' :
if ( m - > type ! = STRING ) {
m - > reln = * l ;
+ + l ;
break ;
}
/* FALL THROUGH */
default :
if ( * l = = ' x ' & & isspace ( ( unsigned char ) l [ 1 ] ) ) {
m - > reln = * l ;
+ + l ;
goto GetDesc ; /* Bill The Cat */
}
m - > reln = ' = ' ;
break ;
}
EATAB ;
if ( getvalue ( m , & l ) )
return - 1 ;
/*
* now get last part - the description
*/
GetDesc :
EATAB ;
if ( l [ 0 ] = = ' \b ' ) {
+ + l ;
m - > nospflag = 1 ;
}
else if ( ( l [ 0 ] = = ' \\ ' ) & & ( l [ 1 ] = = ' b ' ) ) {
+ + l ;
+ + l ;
m - > nospflag = 1 ;
}
else
m - > nospflag = 0 ;
2003-01-25 16:25:37 +00:00
2004-01-15 06:09:16 +00:00
if ( ! is_valid_mimetype ( l , strlen ( l ) ) ) {
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
2004-01-15 06:09:16 +00:00
php_error_docref ( " http://www.php.net/mime_magic " TSRMLS_CC , E_WARNING , " : (%s:%d) '%s' is not a valid mimetype, entry skipped " , MIME_MAGIC_G ( magicfile ) , lineno , l ) ;
2003-01-25 16:25:37 +00:00
return - 1 ;
}
2006-12-28 20:45:27 +00:00
strlcpy ( m - > desc , l , sizeof ( m - > desc ) ) ;
2002-03-24 23:00:47 +00:00
return 0 ;
}
/*
* Read a numeric value from a pointer , into the value union of a magic
* pointer , according to the magic type . Update the string pointer to point
* just after the number read . Return 0 for success , non - zero for failure .
*/
static int getvalue ( struct magic * m , char * * p )
{
int slen ;
if ( m - > type = = STRING ) {
* p = getstr ( * p , m - > value . s , sizeof ( m - > value . s ) , & slen ) ;
m - > vallen = slen ;
}
else if ( m - > reln ! = ' x ' )
m - > value . l = signextend ( m , strtol ( * p , p , 0 ) ) ;
return 0 ;
}
/*
* Convert a string containing C character escapes . Stop at an unescaped
* space or tab . Copy the converted version to " p " , returning its length in
* * slen . Return updated scan pointer as function result .
*/
static char * getstr ( register char * s , register char * p ,
int plen , int * slen )
{
char * origs = s , * origp = p ;
char * pmax = p + plen - 1 ;
register int c ;
register int val ;
while ( ( c = * s + + ) ! = ' \0 ' ) {
if ( isspace ( ( unsigned char ) c ) )
break ;
if ( p > = pmax ) {
2003-01-19 00:45:53 +00:00
TSRMLS_FETCH ( ) ;
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( " http://www.php.net/mime_magic " TSRMLS_CC , E_WARNING , " string too long: %s " , origs ) ;
2002-03-24 23:00:47 +00:00
break ;
}
if ( c = = ' \\ ' ) {
switch ( c = * s + + ) {
case ' \0 ' :
goto out ;
default :
* p + + = ( char ) c ;
break ;
case ' n ' :
* p + + = ' \n ' ;
break ;
case ' r ' :
* p + + = ' \r ' ;
break ;
case ' b ' :
* p + + = ' \b ' ;
break ;
case ' t ' :
* p + + = ' \t ' ;
break ;
case ' f ' :
* p + + = ' \f ' ;
break ;
case ' v ' :
* p + + = ' \v ' ;
break ;
/* \ and up to 3 octal digits */
case ' 0 ' :
case ' 1 ' :
case ' 2 ' :
case ' 3 ' :
case ' 4 ' :
case ' 5 ' :
case ' 6 ' :
case ' 7 ' :
val = c - ' 0 ' ;
c = * s + + ; /* try for 2 */
if ( c > = ' 0 ' & & c < = ' 7 ' ) {
val = ( val < < 3 ) | ( c - ' 0 ' ) ;
c = * s + + ; /* try for 3 */
if ( c > = ' 0 ' & & c < = ' 7 ' )
val = ( val < < 3 ) | ( c - ' 0 ' ) ;
else
- - s ;
}
else
- - s ;
* p + + = ( char ) val ;
break ;
/* \x and up to 3 hex digits */
case ' x ' :
val = ' x ' ; /* Default if no digits */
c = hextoint ( * s + + ) ; /* Get next char */
if ( c > = 0 ) {
val = c ;
c = hextoint ( * s + + ) ;
if ( c > = 0 ) {
val = ( val < < 4 ) + c ;
c = hextoint ( * s + + ) ;
if ( c > = 0 ) {
val = ( val < < 4 ) + c ;
}
else
- - s ;
}
else
- - s ;
}
else
- - s ;
* p + + = ( char ) val ;
break ;
}
}
else
* p + + = ( char ) c ;
}
out :
* p = ' \0 ' ;
* slen = p - origp ;
return s ;
}
/* Single hex char to int; -1 if not a hex char. */
static int hextoint ( int c )
{
if ( isdigit ( ( unsigned char ) c ) )
return c - ' 0 ' ;
if ( ( c > = ' a ' ) & & ( c < = ' f ' ) )
return c + 10 - ' a ' ;
if ( ( c > = ' A ' ) & & ( c < = ' F ' ) )
return c + 10 - ' A ' ;
return - 1 ;
}
/*
* RSL ( result string list ) processing routines
*
* These collect strings that would have been printed in fragments by file ( 1 )
* into a list of magic_rsl structures with the strings . When complete ,
* they ' re concatenated together to become the MIME content and encoding
* types .
*
* return value conventions for these functions : functions which return int :
* failure = - 1 , other = result functions which return pointers : failure = 0 ,
* other = result
*/
/* allocate a per-request structure and put it in the request record */
static magic_req_rec * magic_set_config ( void )
{
magic_req_rec * req_dat = ( magic_req_rec * ) emalloc ( sizeof ( magic_req_rec ) ) ;
req_dat - > head = req_dat - > tail = ( magic_rsl * ) NULL ;
return req_dat ;
}
static void magic_free_config ( magic_req_rec * req_dat ) {
magic_rsl * curr , * next ;
if ( ! req_dat ) return ;
curr = req_dat - > head ;
while ( curr ) {
next = curr - > next ;
efree ( curr - > str ) ;
efree ( curr ) ;
curr = next ;
}
efree ( req_dat ) ;
}
/* add a string to the result string list for this request */
/* it is the responsibility of the caller to allocate "str" */
static int magic_rsl_add ( char * str )
{
2002-05-21 10:14:12 +00:00
magic_req_rec * req_dat ;
2002-03-24 23:00:47 +00:00
magic_rsl * rsl ;
2002-05-21 10:14:12 +00:00
TSRMLS_FETCH ( ) ;
2002-03-24 23:00:47 +00:00
2002-05-21 10:14:12 +00:00
req_dat = MIME_MAGIC_G ( req_dat ) ;
/* make sure we have a list to put it in */
2002-03-24 23:00:47 +00:00
if ( ! req_dat ) {
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " request config should not be NULL " ) ;
2002-03-24 23:00:47 +00:00
if ( ! ( req_dat = magic_set_config ( ) ) ) {
/* failure */
return - 1 ;
}
}
/* allocate the list entry */
rsl = ( magic_rsl * ) emalloc ( sizeof ( magic_rsl ) ) ;
/* fill it */
rsl - > str = estrdup ( str ) ;
rsl - > next = ( magic_rsl * ) NULL ;
/* append to the list */
if ( req_dat - > head & & req_dat - > tail ) {
req_dat - > tail - > next = rsl ;
req_dat - > tail = rsl ;
}
else {
req_dat - > head = req_dat - > tail = rsl ;
}
/* success */
return 0 ;
}
/* RSL hook for puts-type functions */
static int magic_rsl_puts ( char * str )
{
return magic_rsl_add ( str ) ;
}
/* RSL hook for printf-type functions */
static int magic_rsl_printf ( char * str , . . . )
{
va_list ap ;
char buf [ MAXMIMESTRING ] ;
/* assemble the string into the buffer */
va_start ( ap , str ) ;
vsnprintf ( buf , sizeof ( buf ) , str , ap ) ;
va_end ( ap ) ;
/* add the buffer to the list */
return magic_rsl_add ( buf ) ;
}
/* RSL hook for putchar-type functions */
static int magic_rsl_putchar ( char c )
{
char str [ 2 ] ;
/* high overhead for 1 char - just hope they don't do this much */
str [ 0 ] = c ;
str [ 1 ] = ' \0 ' ;
return magic_rsl_add ( str ) ;
}
/* allocate and copy a contiguous string from a result string list */
static char * rsl_strdup ( int start_frag , int start_pos , int len )
{
char * result ; /* return value */
int cur_frag , /* current fragment number/counter */
cur_pos , /* current position within fragment */
res_pos ; /* position in result string */
magic_rsl * frag ; /* list-traversal pointer */
2002-05-21 10:14:12 +00:00
magic_req_rec * req_dat ;
TSRMLS_FETCH ( ) ;
req_dat = MIME_MAGIC_G ( req_dat ) ;
2002-03-24 23:00:47 +00:00
/* allocate the result string */
2004-01-15 06:09:16 +00:00
result = ( char * ) emalloc ( len + 2 ) ;
2002-03-24 23:00:47 +00:00
/* loop through and collect the string */
res_pos = 0 ;
for ( frag = req_dat - > head , cur_frag = 0 ;
frag - > next ;
frag = frag - > next , cur_frag + + ) {
/* loop to the first fragment */
if ( cur_frag < start_frag )
continue ;
/* loop through and collect chars */
for ( cur_pos = ( cur_frag = = start_frag ) ? start_pos : 0 ;
frag - > str [ cur_pos ] ;
cur_pos + + ) {
if ( cur_frag > = start_frag
& & cur_pos > = start_pos
& & res_pos < = len ) {
result [ res_pos + + ] = frag - > str [ cur_pos ] ;
if ( res_pos > len ) {
break ;
}
}
}
}
/* clean up and return */
result [ res_pos ] = 0 ;
2002-06-11 09:37:31 +00:00
2002-03-24 23:00:47 +00:00
return result ;
}
/*
* magic_process - process input file r Apache API request record
* ( formerly called " process " in file command , prefix added for clarity ) Opens
* the file and reads a fixed - size buffer to begin processing the contents .
*/
2003-06-04 14:21:40 +00:00
static int magic_process ( zval * what TSRMLS_DC )
2002-03-24 23:00:47 +00:00
{
2002-06-11 09:37:31 +00:00
php_stream * stream ;
2002-03-24 23:00:47 +00:00
unsigned char buf [ HOWMANY + 1 ] ; /* one extra for terminating '\0' */
int nbytes = 0 ; /* number of bytes read from a datafile */
int result ;
2003-06-04 14:21:40 +00:00
off_t streampos ;
2002-03-24 23:00:47 +00:00
/*
* first try judging the file based on its filesystem status
*/
2003-06-04 14:21:40 +00:00
switch ( ( result = fsmagic ( what TSRMLS_CC ) ) ) {
2002-03-26 19:57:32 +00:00
case MIME_MAGIC_DONE :
2002-03-24 23:00:47 +00:00
magic_rsl_putchar ( ' \n ' ) ;
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_OK ;
case MIME_MAGIC_OK :
2002-03-24 23:00:47 +00:00
break ;
default :
/* fatal error, bail out */
return result ;
}
2003-06-04 14:21:40 +00:00
switch ( Z_TYPE_P ( what ) ) {
case IS_STRING :
stream = php_stream_open_wrapper ( Z_STRVAL_P ( what ) , " rb " , IGNORE_PATH | ENFORCE_SAFE_MODE | REPORT_ERRORS , NULL ) ;
if ( stream = = NULL ) {
/* We can't open it, but we were able to stat it. */
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " can't read `%s' " , Z_STRVAL_P ( what ) ) ;
/* let some other handler decide what the problem is */
return MIME_MAGIC_DECLINED ;
}
break ;
case IS_RESOURCE :
php_stream_from_zval_no_verify ( stream , & what ) ; /* we tested this before, so it should work here */
streampos = php_stream_tell ( stream ) ; /* remember stream position for restauration */
php_stream_seek ( stream , 0 , SEEK_SET ) ;
break ;
2005-07-11 03:24:28 +00:00
default :
return - 1 ;
2003-06-04 14:21:40 +00:00
}
2002-06-11 09:37:31 +00:00
2002-03-24 23:00:47 +00:00
/*
* try looking at the first HOWMANY bytes
*/
2002-06-11 09:37:31 +00:00
if ( ( nbytes = php_stream_read ( stream , ( char * ) buf , sizeof ( buf ) - 1 ) ) = = - 1 ) {
2003-06-04 14:21:40 +00:00
if ( MIME_MAGIC_G ( debug ) ) {
if ( Z_TYPE_P ( what ) = = IS_RESOURCE ) {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " read failed on stream " ) ;
} else {
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " read failed: %s " , Z_STRVAL_P ( what ) ) ;
}
}
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_ERROR ;
2002-03-24 23:00:47 +00:00
}
if ( nbytes = = 0 )
magic_rsl_puts ( MIME_TEXT_UNKNOWN ) ;
else {
buf [ nbytes + + ] = ' \0 ' ; /* null-terminate it */
tryit ( buf , nbytes , 1 ) ;
}
2003-06-04 14:21:40 +00:00
if ( Z_TYPE_P ( what ) = = IS_RESOURCE ) {
php_stream_seek ( stream , streampos , SEEK_SET ) ;
} else {
( void ) php_stream_close ( stream ) ;
}
2002-03-24 23:00:47 +00:00
( void ) magic_rsl_putchar ( ' \n ' ) ;
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_OK ;
2002-03-24 23:00:47 +00:00
}
static void tryit ( unsigned char * buf , int nb , int checkzmagic )
{
/*
* Try compression stuff
*/
2003-02-09 19:09:52 +00:00
# if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
2002-03-24 23:00:47 +00:00
if ( checkzmagic = = 1 ) {
if ( zmagic ( buf , nb ) = = 1 )
return ;
}
# endif
/*
* try tests in / etc / magic ( or surrogate magic file )
*/
if ( softmagic ( buf , nb ) = = 1 )
return ;
/*
* try known keywords , check for ascii - ness too .
*/
if ( ascmagic ( buf , nb ) = = 1 )
return ;
/*
* abandon hope , all ye who remain here
*/
magic_rsl_puts ( MIME_BINARY_UNKNOWN ) ;
}
/*
2002-03-26 19:57:32 +00:00
* return MIME_MAGIC_DONE to indicate it ' s been handled
* return MIME_MAGIC_OK to indicate it ' s a regular file still needing handling
2002-03-24 23:00:47 +00:00
* other returns indicate a failure of some sort
*/
2003-06-04 14:21:40 +00:00
static int fsmagic ( zval * what TSRMLS_DC )
2002-03-24 23:00:47 +00:00
{
2002-06-11 09:37:31 +00:00
php_stream_statbuf stat_ssb ;
2002-03-24 23:00:47 +00:00
2003-06-04 14:21:40 +00:00
switch ( Z_TYPE_P ( what ) ) {
case IS_STRING :
if ( ! php_stream_stat_path ( Z_STRVAL_P ( what ) , & stat_ssb ) ) {
return MIME_MAGIC_OK ;
}
break ;
case IS_RESOURCE :
{
php_stream * stream ;
php_stream_from_zval_no_verify ( stream , & what ) ;
if ( ! php_stream_stat ( stream , & stat_ssb ) ) {
return MIME_MAGIC_OK ;
}
}
break ;
2002-06-11 09:37:31 +00:00
}
2002-03-24 23:00:47 +00:00
2002-06-11 09:37:31 +00:00
switch ( stat_ssb . sb . st_mode & S_IFMT ) {
2002-03-24 23:00:47 +00:00
case S_IFDIR :
magic_rsl_puts ( DIR_MAGIC_TYPE ) ;
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_DONE ;
2002-03-24 23:00:47 +00:00
case S_IFCHR :
/*
* ( void ) magic_rsl_printf ( r , " character special (%d/%d) " ,
* major ( sb - > st_rdev ) , minor ( sb - > st_rdev ) ) ;
*/
( void ) magic_rsl_puts ( MIME_BINARY_UNKNOWN ) ;
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_DONE ;
2002-03-24 23:00:47 +00:00
# ifdef S_IFBLK
case S_IFBLK :
/*
* ( void ) magic_rsl_printf ( r , " block special (%d/%d) " ,
* major ( sb - > st_rdev ) , minor ( sb - > st_rdev ) ) ;
*/
( void ) magic_rsl_puts ( MIME_BINARY_UNKNOWN ) ;
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_DONE ;
2002-03-24 23:00:47 +00:00
/* TODO add code to handle V7 MUX and Blit MUX files */
# endif
# ifdef S_IFIFO
case S_IFIFO :
/*
* magic_rsl_puts ( r , " fifo (named pipe) " ) ;
*/
( void ) magic_rsl_puts ( MIME_BINARY_UNKNOWN ) ;
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_DONE ;
2002-03-24 23:00:47 +00:00
# endif
# ifdef S_IFLNK
case S_IFLNK :
/* We used stat(), the only possible reason for this is that the
* symlink is broken .
*/
2003-06-04 14:21:40 +00:00
if ( MIME_MAGIC_G ( debug ) ) {
/* no need to check argument type here, this can only happen with strings */
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " broken symlink (%s) " , Z_STRVAL_P ( what ) ) ;
}
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_ERROR ;
2002-03-24 23:00:47 +00:00
# endif
# ifdef S_IFSOCK
# ifndef __COHERENT__
case S_IFSOCK :
magic_rsl_puts ( MIME_BINARY_UNKNOWN ) ;
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_DONE ;
2002-03-24 23:00:47 +00:00
# endif
# endif
case S_IFREG :
break ;
2002-06-11 09:37:31 +00:00
case 0 :
break ;
2002-03-24 23:00:47 +00:00
default :
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " invalid mode 0%o. " , ( unsigned int ) stat_ssb . sb . st_mode ) ;
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_ERROR ;
2002-03-24 23:00:47 +00:00
}
/*
* regular file , check next possibility
*/
2002-06-11 09:37:31 +00:00
if ( stat_ssb . sb . st_size = = 0 ) {
2002-03-24 23:00:47 +00:00
magic_rsl_puts ( MIME_TEXT_UNKNOWN ) ;
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_DONE ;
2002-03-24 23:00:47 +00:00
}
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_OK ;
2002-03-24 23:00:47 +00:00
}
/*
* softmagic - lookup one file in database ( already read from / etc / magic by
* apprentice . c ) . Passed the name and FILE * of one file to be typed .
*/
/* ARGSUSED1 */ /* nbytes passed for regularity, maybe need later */
static int softmagic ( unsigned char * buf , int nbytes )
{
if ( match ( buf , nbytes ) )
return 1 ;
return 0 ;
}
/*
* Go through the whole list , stopping if you find a match . Process all the
* continuations of that match before returning .
*
* We support multi - level continuations :
*
* At any time when processing a successful top - level match , there is a current
* continuation level ; it represents the level of the last successfully
* matched continuation .
*
* Continuations above that level are skipped as , if we see one , it means that
* the continuation that controls them - i . e , the lower - level continuation
* preceding them - failed to match .
*
* Continuations below that level are processed as , if we see one , it means
* we ' ve finished processing or skipping higher - level continuations under the
* control of a successful or unsuccessful lower - level continuation , and are
* now seeing the next lower - level continuation and should process it . The
* current continuation level reverts to the level of the one we ' re seeing .
*
* Continuations at the current level are processed as , if we see one , there ' s
* no lower - level continuation that may have failed .
*
* If a continuation matches , we bump the current continuation level so that
* higher - level continuations are processed .
*/
static int match ( unsigned char * s , int nbytes )
{
int cont_level = 0 ;
int need_separator = 0 ;
union VALUETYPE p ;
magic_server_config_rec * conf = & mime_global ;
struct magic * m ;
for ( m = conf - > magic ; m ; m = m - > next ) {
/* check if main entry matches */
if ( ! mget ( & p , s , m , nbytes ) | |
! mcheck ( & p , m ) ) {
struct magic * m_cont ;
/*
* main entry didn ' t match , flush its continuations
*/
if ( ! m - > next | | ( m - > next - > cont_level = = 0 ) ) {
continue ;
}
m_cont = m - > next ;
while ( m_cont & & ( m_cont - > cont_level ! = 0 ) ) {
/*
* this trick allows us to keep * m in sync when the continue
* advances the pointer
*/
m = m_cont ;
m_cont = m_cont - > next ;
}
continue ;
}
/* if we get here, the main entry rule was a match */
/* this will be the last run through the loop */
/* print the match */
mprint ( & p , m ) ;
/*
* If we printed something , we ' ll need to print a blank before we
* print something else .
*/
if ( m - > desc [ 0 ] )
need_separator = 1 ;
/* and any continuations that match */
cont_level + + ;
/*
* while ( m & & m - > next & & m - > next - > cont_level ! = 0 & & ( m = m - > next
* ) )
*/
m = m - > next ;
while ( m & & ( m - > cont_level ! = 0 ) ) {
if ( cont_level > = m - > cont_level ) {
if ( cont_level > m - > cont_level ) {
/*
* We ' re at the end of the level " cont_level "
* continuations .
*/
cont_level = m - > cont_level ;
}
if ( mget ( & p , s , m , nbytes ) & &
mcheck ( & p , m ) ) {
/*
* This continuation matched . Print its message , with a
* blank before it if the previous item printed and this
* item isn ' t empty .
*/
/* space if previous printed */
if ( need_separator
& & ( m - > nospflag = = 0 )
& & ( m - > desc [ 0 ] ! = ' \0 ' )
) {
( void ) magic_rsl_putchar ( ' ' ) ;
need_separator = 0 ;
}
mprint ( & p , m ) ;
if ( m - > desc [ 0 ] )
need_separator = 1 ;
/*
* If we see any continuations at a higher level , process
* them .
*/
cont_level + + ;
}
}
/* move to next continuation record */
m = m - > next ;
}
return 1 ; /* all through */
}
return 0 ; /* no match at all */
}
/* an optimization over plain strcmp() */
# define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
static int ascmagic ( unsigned char * buf , int nbytes )
{
int has_escapes = 0 ;
unsigned char * s ;
char nbuf [ HOWMANY + 1 ] ; /* one extra for terminating '\0' */
char * token ;
register struct names * p ;
int small_nbytes ;
2006-11-26 17:02:13 +00:00
char * strtok_buf = NULL ;
2002-03-24 23:00:47 +00:00
/* these are easy, do them first */
/*
* for troff , look for . + letter + letter or . \ " ; this must be done to
* disambiguate tar archives ' . / file and other trash from real troff
* input .
*/
if ( * buf = = ' . ' ) {
unsigned char * tp = buf + 1 ;
while ( isspace ( * tp ) )
+ + tp ; /* skip leading whitespace */
if ( ( isalnum ( * tp ) | | * tp = = ' \\ ' ) & &
( isalnum ( * ( tp + 1 ) ) | | * tp = = ' " ' ) ) {
magic_rsl_puts ( " application/x-troff " ) ;
return 1 ;
}
}
if ( ( * buf = = ' c ' | | * buf = = ' C ' ) & & isspace ( * ( buf + 1 ) ) ) {
/* Fortran */
magic_rsl_puts ( " text/plain " ) ;
return 1 ;
}
/* look for tokens from names.h - this is expensive!, so we'll limit
* ourselves to only SMALL_HOWMANY bytes */
small_nbytes = ( nbytes > SMALL_HOWMANY ) ? SMALL_HOWMANY : nbytes ;
/* make a copy of the buffer here because strtok() will destroy it */
s = ( unsigned char * ) memcpy ( nbuf , buf , small_nbytes ) ;
s [ small_nbytes ] = ' \0 ' ;
has_escapes = ( memchr ( s , ' \033 ' , small_nbytes ) ! = NULL ) ;
2006-11-26 17:02:13 +00:00
while ( ( token = php_strtok_r ( ( char * ) s , " \t \n \r \f " , & strtok_buf ) ) ! = NULL ) {
2002-03-24 23:00:47 +00:00
s = NULL ; /* make strtok() keep on tokin' */
for ( p = names ; p < names + NNAMES ; p + + ) {
if ( STREQ ( p - > name , token ) ) {
magic_rsl_puts ( types [ p - > type ] ) ;
if ( has_escapes )
magic_rsl_puts ( " (with escape sequences) " ) ;
return 1 ;
}
}
}
switch ( is_tar ( buf , nbytes ) ) {
case 1 :
/* V7 tar archive */
magic_rsl_puts ( " application/x-tar " ) ;
return 1 ;
case 2 :
/* POSIX tar archive */
magic_rsl_puts ( " application/x-tar " ) ;
return 1 ;
}
/* all else fails, but it is ascii... */
if ( has_escapes ) {
/* text with escape sequences */
/* we leave this open for further differentiation later */
magic_rsl_puts ( " text/plain " ) ;
}
else {
/* plain text */
magic_rsl_puts ( " text/plain " ) ;
}
return 1 ;
}
/*
* is_tar ( ) - - figure out whether file is a tar archive .
*
* Stolen ( by author of file utility ) from the public domain tar program : Public
* Domain version written 26 Aug 1985 John Gilmore ( ihnp4 ! hoptoad ! gnu ) .
*
* @ ( # ) list . c 1.18 9 / 23 / 86 Public Domain - gnu $ Id : mod_mime_magic . c , v 1.7
* 1997 / 06 / 24 00 : 41 : 02 ikluft Exp ikluft $
*
* Comments changed and some code / comments reformatted for file command by Ian
* Darwin .
*/
# define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
/*
* Return 0 if the checksum is bad ( i . e . , probably not a tar archive ) , 1 for
* old UNIX tar file , 2 for Unix Std ( POSIX ) tar file .
*/
2003-09-26 07:47:01 +00:00
static int is_tar ( unsigned char * buf , unsigned int nbytes )
2002-03-24 23:00:47 +00:00
{
register union record * header = ( union record * ) buf ;
register int i ;
register long sum , recsum ;
register char * p ;
if ( nbytes < sizeof ( union record ) )
return 0 ;
recsum = from_oct ( 8 , header - > header . chksum ) ;
sum = 0 ;
p = header - > charptr ;
for ( i = sizeof ( union record ) ; - - i > = 0 ; ) {
/*
* We can ' t use unsigned char here because of old compilers , e . g . V7 .
*/
sum + = 0xFF & * p + + ;
}
/* Adjust checksum to count the "chksum" field as blanks. */
for ( i = sizeof ( header - > header . chksum ) ; - - i > = 0 ; )
sum - = 0xFF & header - > header . chksum [ i ] ;
sum + = ' ' * sizeof header - > header . chksum ;
if ( sum ! = recsum )
return 0 ; /* Not a tar archive */
if ( 0 = = strcmp ( header - > header . magic , TMAGIC ) )
return 2 ; /* Unix Standard tar archive */
return 1 ; /* Old fashioned tar archive */
}
/*
* Quick and dirty octal conversion .
*
* Result is - 1 if the field is invalid ( all blank , or nonoctal ) .
*/
static long from_oct ( int digs , char * where )
{
register long value ;
while ( isspace ( * where ) ) { /* Skip spaces */
where + + ;
if ( - - digs < = 0 )
return - 1 ; /* All blank field */
}
value = 0 ;
while ( digs > 0 & & isodigit ( * where ) ) { /* Scan til nonoctal */
value = ( value < < 3 ) | ( * where + + - ' 0 ' ) ;
- - digs ;
}
if ( digs > 0 & & * where & & ! isspace ( * where ) )
return - 1 ; /* Ended on non-space/nul */
return value ;
}
static int mget ( union VALUETYPE * p , unsigned char * s ,
struct magic * m , int nbytes )
{
long offset = m - > offset ;
2003-01-10 04:37:26 +00:00
if ( offset + ( long ) sizeof ( union VALUETYPE ) > nbytes )
2002-03-24 23:00:47 +00:00
return 0 ;
memcpy ( p , s + offset , sizeof ( union VALUETYPE ) ) ;
if ( ! mconvert ( p , m ) )
return 0 ;
if ( m - > flag & INDIR ) {
switch ( m - > in . type ) {
case BYTE :
offset = p - > b + m - > in . offset ;
break ;
case SHORT :
offset = p - > h + m - > in . offset ;
break ;
case LONG :
offset = p - > l + m - > in . offset ;
break ;
}
2003-01-10 04:37:26 +00:00
if ( offset + ( long ) sizeof ( union VALUETYPE ) > nbytes )
2002-03-24 23:00:47 +00:00
return 0 ;
memcpy ( p , s + offset , sizeof ( union VALUETYPE ) ) ;
if ( ! mconvert ( p , m ) )
return 0 ;
}
return 1 ;
}
static int mcheck ( union VALUETYPE * p , struct magic * m )
{
register unsigned long l = m - > value . l ;
register unsigned long v ;
int matched ;
2003-01-19 00:45:53 +00:00
TSRMLS_FETCH ( ) ;
2002-03-24 23:00:47 +00:00
if ( ( m - > value . s [ 0 ] = = ' x ' ) & & ( m - > value . s [ 1 ] = = ' \0 ' ) ) {
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " BOINK " ) ;
return 1 ;
2002-03-24 23:00:47 +00:00
}
switch ( m - > type ) {
case BYTE :
v = p - > b ;
break ;
case SHORT :
case BESHORT :
case LESHORT :
v = p - > h ;
break ;
case LONG :
case BELONG :
case LELONG :
case DATE :
case BEDATE :
case LEDATE :
v = p - > l ;
break ;
case STRING :
l = 0 ;
/*
* What we want here is : v = strncmp ( m - > value . s , p - > s , m - > vallen ) ;
* but ignoring any nulls . bcmp doesn ' t give - / + / 0 and isn ' t
* universally available anyway .
*/
v = 0 ;
{
register unsigned char * a = ( unsigned char * ) m - > value . s ;
register unsigned char * b = ( unsigned char * ) p - > s ;
register int len = m - > vallen ;
while ( - - len > = 0 )
if ( ( v = * b + + - * a + + ) ! = 0 )
break ;
}
break ;
default :
/* bogosity, pretend that it just wasn't a match */
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " invalid type %d in mcheck(). " , m - > type ) ;
2002-03-24 23:00:47 +00:00
return 0 ;
}
v = signextend ( m , v ) & m - > mask ;
switch ( m - > reln ) {
case ' x ' :
matched = 1 ;
break ;
case ' ! ' :
matched = v ! = l ;
break ;
case ' = ' :
matched = v = = l ;
break ;
case ' > ' :
if ( m - > flag & UNSIGNED ) {
matched = v > l ;
}
else {
matched = ( long ) v > ( long ) l ;
}
break ;
case ' < ' :
if ( m - > flag & UNSIGNED ) {
matched = v < l ;
}
else {
matched = ( long ) v < ( long ) l ;
}
break ;
case ' & ' :
matched = ( v & l ) = = l ;
break ;
case ' ^ ' :
matched = ( v & l ) ! = l ;
break ;
default :
/* bogosity, pretend it didn't match */
matched = 0 ;
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " mcheck: can't happen: invalid relation %d. " , m - > reln ) ;
2002-03-24 23:00:47 +00:00
break ;
}
return matched ;
}
2003-02-09 19:09:52 +00:00
# if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB)
2002-03-24 23:00:47 +00:00
/*
* compress routines : zmagic ( ) - returns 0 if not recognized , uncompresses
* and prints information if recognized uncompress ( s , method , old , n , newch )
* - uncompress old into new , using method , return sizeof new
*/
static int zmagic ( unsigned char * buf , int nbytes )
{
2002-03-27 11:56:42 +00:00
if ( buf [ 0 ] ! = 0x1f ) return 0 ;
2002-03-24 23:00:47 +00:00
2002-03-27 11:56:42 +00:00
switch ( buf [ 1 ] ) {
case 0x9d : /* .Z "x-compress" */
break ; /* not yet supportet */
2002-03-24 23:00:47 +00:00
2002-03-27 11:56:42 +00:00
case 0x8b : /* .gz "x-gzip" */
#if 0
if ( ( newsize = magic_uncompress ( i , & newbuf , nbytes ) ) > 0 ) {
tryit ( newbuf , newsize , 0 ) ;
/* set encoding type in the request record */
/* TODO r->content_encoding = compr[i].encoding; */
}
2002-03-24 23:00:47 +00:00
# endif
2002-03-27 11:56:42 +00:00
break ; /* not yet supported */
case 0x1e : /* simply packed ? */
break ; /* not yet supported */
2002-03-24 23:00:47 +00:00
}
2002-03-27 11:56:42 +00:00
return 0 ;
2002-03-24 23:00:47 +00:00
}
# endif
static void mprint ( union VALUETYPE * p , struct magic * m )
{
char * pp , * rt ;
unsigned long v ;
switch ( m - > type ) {
case BYTE :
v = p - > b ;
break ;
case SHORT :
case BESHORT :
case LESHORT :
v = p - > h ;
break ;
case LONG :
case BELONG :
case LELONG :
v = p - > l ;
break ;
case STRING :
if ( m - > reln = = ' = ' ) {
( void ) magic_rsl_printf ( m - > desc , m - > value . s ) ;
}
else {
( void ) magic_rsl_printf ( m - > desc , p - > s ) ;
}
return ;
case DATE :
case BEDATE :
case LEDATE :
2006-11-30 15:13:19 +00:00
{
char ctimebuf [ 52 ] ;
pp = php_ctime_r ( ( time_t * ) & p - > l , ctimebuf ) ;
if ( ( rt = strchr ( pp , ' \n ' ) ) ! = NULL ) {
* rt = ' \0 ' ;
}
( void ) magic_rsl_printf ( m - > desc , pp ) ;
return ;
}
2002-03-24 23:00:47 +00:00
default :
2003-01-19 00:45:53 +00:00
{
TSRMLS_FETCH ( ) ;
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " invalid m->type (%d) in mprint(). " , m - > type ) ;
return ;
}
2002-03-24 23:00:47 +00:00
}
v = signextend ( m , v ) & m - > mask ;
( void ) magic_rsl_printf ( m - > desc , ( unsigned long ) v ) ;
}
/*
* Convert the byte order of the data we are looking at
*/
static int mconvert ( union VALUETYPE * p , struct magic * m )
{
char * rt ;
switch ( m - > type ) {
case BYTE :
case SHORT :
case LONG :
case DATE :
return 1 ;
case STRING :
/* Null terminate and eat the return */
p - > s [ sizeof ( p - > s ) - 1 ] = ' \0 ' ;
if ( ( rt = strchr ( p - > s , ' \n ' ) ) ! = NULL )
* rt = ' \0 ' ;
return 1 ;
case BESHORT :
p - > h = ( short ) ( ( p - > hs [ 0 ] < < 8 ) | ( p - > hs [ 1 ] ) ) ;
return 1 ;
case BELONG :
case BEDATE :
p - > l = ( long )
( ( p - > hl [ 0 ] < < 24 ) | ( p - > hl [ 1 ] < < 16 ) | ( p - > hl [ 2 ] < < 8 ) | ( p - > hl [ 3 ] ) ) ;
return 1 ;
case LESHORT :
p - > h = ( short ) ( ( p - > hs [ 1 ] < < 8 ) | ( p - > hs [ 0 ] ) ) ;
return 1 ;
case LELONG :
case LEDATE :
p - > l = ( long )
( ( p - > hl [ 3 ] < < 24 ) | ( p - > hl [ 2 ] < < 16 ) | ( p - > hl [ 1 ] < < 8 ) | ( p - > hl [ 0 ] ) ) ;
return 1 ;
default :
2003-01-19 00:45:53 +00:00
{
TSRMLS_FETCH ( ) ;
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " invalid type %d in mconvert(). " , m - > type ) ;
return 0 ;
}
2002-03-24 23:00:47 +00:00
}
}
/* states for the state-machine algorithm in magic_rsl_to_request() */
typedef enum {
rsl_leading_space , rsl_type , rsl_subtype , rsl_separator , rsl_encoding
} rsl_states ;
/* process the RSL and set the MIME info in the request record */
static int magic_rsl_get ( char * * content_type , char * * content_encoding )
{
int cur_frag , /* current fragment number/counter */
cur_pos , /* current position within fragment */
type_frag , /* content type starting point: fragment */
type_pos , /* content type starting point: position */
type_len , /* content type length */
encoding_frag , /* content encoding starting point: fragment */
encoding_pos , /* content encoding starting point: position */
encoding_len ; /* content encoding length */
magic_rsl * frag ; /* list-traversal pointer */
rsl_states state ;
2002-05-21 10:14:12 +00:00
magic_req_rec * req_dat ;
TSRMLS_FETCH ( ) ;
req_dat = MIME_MAGIC_G ( req_dat ) ;
2002-03-24 23:00:47 +00:00
/* check if we have a result */
if ( ! req_dat | | ! req_dat - > head ) {
/* empty - no match, we defer to other Apache modules */
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_DECLINED ;
2002-03-24 23:00:47 +00:00
}
/* start searching for the type and encoding */
state = rsl_leading_space ;
type_frag = type_pos = type_len = 0 ;
encoding_frag = encoding_pos = encoding_len = 0 ;
for ( frag = req_dat - > head , cur_frag = 0 ;
frag & & frag - > next ;
frag = frag - > next , cur_frag + + ) {
/* loop through the characters in the fragment */
for ( cur_pos = 0 ; frag - > str [ cur_pos ] ; cur_pos + + ) {
if ( isspace ( frag - > str [ cur_pos ] ) ) {
/* process whitespace actions for each state */
if ( state = = rsl_leading_space ) {
/* eat whitespace in this state */
continue ;
}
else if ( state = = rsl_type ) {
/* whitespace: type has no slash! */
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_DECLINED ;
2002-03-24 23:00:47 +00:00
}
else if ( state = = rsl_subtype ) {
/* whitespace: end of MIME type */
state + + ;
continue ;
}
else if ( state = = rsl_separator ) {
/* eat whitespace in this state */
continue ;
}
else if ( state = = rsl_encoding ) {
/* whitespace: end of MIME encoding */
/* we're done */
frag = req_dat - > tail ;
break ;
}
else {
/* should not be possible */
/* abandon malfunctioning module */
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " : bad state %d (ws) " , state ) ;
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_DECLINED ;
2002-03-24 23:00:47 +00:00
}
/* NOTREACHED */
}
else if ( state = = rsl_type & &
frag - > str [ cur_pos ] = = ' / ' ) {
/* copy the char and go to rsl_subtype state */
type_len + + ;
state + + ;
}
else {
/* process non-space actions for each state */
if ( state = = rsl_leading_space ) {
/* non-space: begin MIME type */
state + + ;
type_frag = cur_frag ;
type_pos = cur_pos ;
type_len = 1 ;
continue ;
}
else if ( state = = rsl_type | |
state = = rsl_subtype ) {
/* non-space: adds to type */
type_len + + ;
continue ;
}
else if ( state = = rsl_separator ) {
/* non-space: begin MIME encoding */
state + + ;
encoding_frag = cur_frag ;
encoding_pos = cur_pos ;
encoding_len = 1 ;
continue ;
}
else if ( state = = rsl_encoding ) {
/* non-space: adds to encoding */
encoding_len + + ;
continue ;
}
else {
/* should not be possible */
/* abandon malfunctioning module */
2003-01-25 16:25:37 +00:00
if ( MIME_MAGIC_G ( debug ) )
php_error_docref ( NULL TSRMLS_CC , E_WARNING , " bad state %d (ns) " , state ) ;
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_DECLINED ;
2002-03-24 23:00:47 +00:00
}
/* NOTREACHED */
}
/* NOTREACHED */
}
}
/* if we ended prior to state rsl_subtype, we had incomplete info */
if ( state ! = rsl_subtype & & state ! = rsl_separator & &
state ! = rsl_encoding ) {
/* defer to other modules */
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_DECLINED ;
2002-03-24 23:00:47 +00:00
}
/* save the info in the request record */
if ( state = = rsl_subtype | | state = = rsl_encoding | |
state = = rsl_encoding ) {
char * tmp ;
tmp = rsl_strdup ( type_frag , type_pos , type_len ) ;
/* XXX: this could be done at config time I'm sure... but I'm
* confused by all this magic_rsl stuff . - djg */
/* TODO ap_content_type_tolower(tmp); */
* content_type = tmp ;
}
if ( state = = rsl_encoding ) {
char * tmp ;
tmp = rsl_strdup ( encoding_frag ,
encoding_pos , encoding_len ) ;
/* XXX: this could be done at config time I'm sure... but I'm
* confused by all this magic_rsl stuff . - djg */
/* TODO ap_str_tolower(tmp); */
* content_encoding = tmp ;
}
/* detect memory allocation errors */
2003-10-20 14:36:51 +00:00
if ( ! content_type | | ! ( * content_type ) | | ( state = = rsl_encoding & & ! * content_encoding ) ) {
return MIME_MAGIC_ERROR ;
2002-03-24 23:00:47 +00:00
}
/* success! */
2002-03-26 19:57:32 +00:00
return MIME_MAGIC_OK ;
2002-03-24 23:00:47 +00:00
}
/*
* Local variables :
* tab - width : 4
* c - basic - offset : 4
* End :
* vim600 : noet sw = 4 ts = 4 fdm = marker
* vim < 600 : noet sw = 4 ts = 4
*/