File indexing completed on 2025-01-19 05:21:38
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Validate 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * @see Zend_Validate_Abstract 0024 */ 0025 // require_once 'Zend/Validate/Abstract.php'; 0026 0027 /** 0028 * Validator for the mime type of a file 0029 * 0030 * @category Zend 0031 * @package Zend_Validate 0032 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0033 * @license http://framework.zend.com/license/new-bsd New BSD License 0034 */ 0035 class Zend_Validate_File_MimeType extends Zend_Validate_Abstract 0036 { 0037 /** 0038 * @const Error type constants 0039 */ 0040 const FALSE_TYPE = 'fileMimeTypeFalse'; 0041 const NOT_DETECTED = 'fileMimeTypeNotDetected'; 0042 const NOT_READABLE = 'fileMimeTypeNotReadable'; 0043 0044 /** 0045 * @var array Error message templates 0046 */ 0047 protected $_messageTemplates = array( 0048 self::FALSE_TYPE => "File '%value%' has a false mimetype of '%type%'", 0049 self::NOT_DETECTED => "The mimetype of file '%value%' could not be detected", 0050 self::NOT_READABLE => "File '%value%' is not readable or does not exist", 0051 ); 0052 0053 /** 0054 * @var array 0055 */ 0056 protected $_messageVariables = array( 0057 'type' => '_type' 0058 ); 0059 0060 /** 0061 * @var string 0062 */ 0063 protected $_type; 0064 0065 /** 0066 * Mimetypes 0067 * 0068 * If null, there is no mimetype 0069 * 0070 * @var string|null 0071 */ 0072 protected $_mimetype; 0073 0074 /** 0075 * Magicfile to use 0076 * 0077 * @var string|null 0078 */ 0079 protected $_magicfile; 0080 0081 /** 0082 * Finfo object to use 0083 * 0084 * @var resource 0085 */ 0086 protected $_finfo; 0087 0088 /** 0089 * If no $_ENV['MAGIC'] is set, try and autodiscover it based on common locations 0090 * @var array 0091 */ 0092 protected $_magicFiles = array( 0093 '/usr/share/misc/magic', 0094 '/usr/share/misc/magic.mime', 0095 '/usr/share/misc/magic.mgc', 0096 '/usr/share/mime/magic', 0097 '/usr/share/mime/magic.mime', 0098 '/usr/share/mime/magic.mgc', 0099 '/usr/share/file/magic', 0100 '/usr/share/file/magic.mime', 0101 '/usr/share/file/magic.mgc', 0102 ); 0103 0104 /** 0105 * Indicates whether use of $_magicFiles should be attempted. 0106 * @var boolean 0107 */ 0108 protected $_tryCommonMagicFiles = true; 0109 0110 /** 0111 * Option to allow header check 0112 * 0113 * @var boolean 0114 */ 0115 protected $_headerCheck = false; 0116 0117 /** 0118 * Holds error information returned by finfo_open 0119 * 0120 * @var array 0121 */ 0122 protected $_finfoError; 0123 0124 /** 0125 * Sets validator options 0126 * 0127 * Mimetype to accept 0128 * 0129 * @param string|array $mimetype MimeType 0130 * @throws Zend_Validate_Exception 0131 */ 0132 public function __construct($mimetype) 0133 { 0134 if ($mimetype instanceof Zend_Config) { 0135 $mimetype = $mimetype->toArray(); 0136 } elseif (is_string($mimetype)) { 0137 $mimetype = explode(',', $mimetype); 0138 } elseif (!is_array($mimetype)) { 0139 // require_once 'Zend/Validate/Exception.php'; 0140 throw new Zend_Validate_Exception("Invalid options to validator provided"); 0141 } 0142 0143 if (isset($mimetype['magicfile'])) { 0144 $this->setMagicFile($mimetype['magicfile']); 0145 unset($mimetype['magicfile']); 0146 } 0147 0148 if (isset($mimetype['headerCheck'])) { 0149 $this->enableHeaderCheck($mimetype['headerCheck']); 0150 unset($mimetype['headerCheck']); 0151 } 0152 0153 $this->setMimeType($mimetype); 0154 } 0155 0156 /** 0157 * Returns the actual set magicfile 0158 * 0159 * Note that for PHP 5.3.0 or higher, we don't use $_ENV['MAGIC'] or try to 0160 * find a magic file in a common location as PHP now has a built-in internal 0161 * magic file. 0162 * 0163 * @return string 0164 */ 0165 public function getMagicFile() 0166 { 0167 if (version_compare(PHP_VERSION, '5.3.0', '<') 0168 && null === $this->_magicfile) { 0169 if (!empty($_ENV['MAGIC'])) { 0170 $this->setMagicFile($_ENV['MAGIC']); 0171 } elseif ( 0172 !(@ini_get("safe_mode") == 'On' || @ini_get("safe_mode") === 1) 0173 && $this->shouldTryCommonMagicFiles() // @see ZF-11784 0174 ) { 0175 // require_once 'Zend/Validate/Exception.php'; 0176 foreach ($this->_magicFiles as $file) { 0177 // supressing errors which are thrown due to openbase_dir restrictions 0178 try { 0179 $this->setMagicFile($file); 0180 if ($this->_magicfile !== null) { 0181 break; 0182 } 0183 } catch (Zend_Validate_Exception $e) { 0184 // Intentionally, catch and fall through 0185 } 0186 } 0187 } 0188 0189 if ($this->_magicfile === null) { 0190 $this->_magicfile = false; 0191 } 0192 } 0193 0194 return $this->_magicfile; 0195 } 0196 0197 /** 0198 * Sets the magicfile to use 0199 * if null, the MAGIC constant from php is used 0200 * if the MAGIC file is errorous, no file will be set 0201 * 0202 * @param string $file 0203 * @throws Zend_Validate_Exception When finfo can not read the magicfile 0204 * @return Zend_Validate_File_MimeType Provides a fluent interface 0205 */ 0206 public function setMagicFile($file) 0207 { 0208 if (empty($file)) { 0209 $this->_magicfile = null; 0210 } else if (!(class_exists('finfo', false))) { 0211 $this->_magicfile = null; 0212 // require_once 'Zend/Validate/Exception.php'; 0213 throw new Zend_Validate_Exception('Magicfile can not be set. There is no finfo extension installed'); 0214 } else if (!is_file($file) || !is_readable($file)) { 0215 // require_once 'Zend/Validate/Exception.php'; 0216 throw new Zend_Validate_Exception('The given magicfile can not be read'); 0217 } else { 0218 $const = defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME; 0219 set_error_handler(array($this, '_errorHandler'), E_NOTICE | E_WARNING); 0220 $this->_finfo = finfo_open($const, $file); 0221 restore_error_handler(); 0222 if (empty($this->_finfo)) { 0223 $this->_finfo = null; 0224 // require_once 'Zend/Validate/Exception.php'; 0225 throw new Zend_Validate_Exception( 0226 sprintf('The given magicfile ("%s") is not accepted by finfo', $file), 0227 null, 0228 $this->_finfoError 0229 ); 0230 } else { 0231 $this->_magicfile = $file; 0232 } 0233 } 0234 0235 return $this; 0236 } 0237 0238 /** 0239 * Enables or disables attempts to try the common magic file locations 0240 * specified by Zend_Validate_File_MimeType::_magicFiles 0241 * 0242 * @param boolean $flag 0243 * @return Zend_Validate_File_MimeType Provides fluent interface 0244 * @see http://framework.zend.com/issues/browse/ZF-11784 0245 */ 0246 public function setTryCommonMagicFilesFlag($flag = true) 0247 { 0248 $this->_tryCommonMagicFiles = (boolean) $flag; 0249 0250 return $this; 0251 } 0252 0253 /** 0254 * Accessor for Zend_Validate_File_MimeType::_magicFiles 0255 * 0256 * @return boolean 0257 * @see http://framework.zend.com/issues/browse/ZF-11784 0258 */ 0259 public function shouldTryCommonMagicFiles() 0260 { 0261 return $this->_tryCommonMagicFiles; 0262 } 0263 0264 /** 0265 * Returns the Header Check option 0266 * 0267 * @return boolean 0268 */ 0269 public function getHeaderCheck() 0270 { 0271 return $this->_headerCheck; 0272 } 0273 0274 /** 0275 * Defines if the http header should be used 0276 * Note that this is unsave and therefor the default value is false 0277 * 0278 * @param boolean $headerCheck 0279 * @return Zend_Validate_File_MimeType Provides a fluent interface 0280 */ 0281 public function enableHeaderCheck($headerCheck = true) 0282 { 0283 $this->_headerCheck = (boolean) $headerCheck; 0284 return $this; 0285 } 0286 0287 /** 0288 * Returns the set mimetypes 0289 * 0290 * @param boolean $asArray Returns the values as array, when false an concated string is returned 0291 * @return string|array 0292 */ 0293 public function getMimeType($asArray = false) 0294 { 0295 $asArray = (bool) $asArray; 0296 $mimetype = (string) $this->_mimetype; 0297 if ($asArray) { 0298 $mimetype = explode(',', $mimetype); 0299 } 0300 0301 return $mimetype; 0302 } 0303 0304 /** 0305 * Sets the mimetypes 0306 * 0307 * @param string|array $mimetype The mimetypes to validate 0308 * @return Zend_Validate_File_Extension Provides a fluent interface 0309 */ 0310 public function setMimeType($mimetype) 0311 { 0312 $this->_mimetype = null; 0313 $this->addMimeType($mimetype); 0314 return $this; 0315 } 0316 0317 /** 0318 * Adds the mimetypes 0319 * 0320 * @param string|array $mimetype The mimetypes to add for validation 0321 * @throws Zend_Validate_Exception 0322 * @return Zend_Validate_File_Extension Provides a fluent interface 0323 */ 0324 public function addMimeType($mimetype) 0325 { 0326 $mimetypes = $this->getMimeType(true); 0327 0328 if (is_string($mimetype)) { 0329 $mimetype = explode(',', $mimetype); 0330 } elseif (!is_array($mimetype)) { 0331 // require_once 'Zend/Validate/Exception.php'; 0332 throw new Zend_Validate_Exception("Invalid options to validator provided"); 0333 } 0334 0335 if (isset($mimetype['magicfile'])) { 0336 unset($mimetype['magicfile']); 0337 } 0338 0339 foreach ($mimetype as $content) { 0340 if (empty($content) || !is_string($content)) { 0341 continue; 0342 } 0343 $mimetypes[] = trim($content); 0344 } 0345 $mimetypes = array_unique($mimetypes); 0346 0347 // Sanity check to ensure no empty values 0348 foreach ($mimetypes as $key => $mt) { 0349 if (empty($mt)) { 0350 unset($mimetypes[$key]); 0351 } 0352 } 0353 0354 $this->_mimetype = implode(',', $mimetypes); 0355 0356 return $this; 0357 } 0358 0359 /** 0360 * Defined by Zend_Validate_Interface 0361 * 0362 * Returns true if the mimetype of the file matches the given ones. Also parts 0363 * of mimetypes can be checked. If you give for example "image" all image 0364 * mime types will be accepted like "image/gif", "image/jpeg" and so on. 0365 * 0366 * @param string $value Real file to check for mimetype 0367 * @param array $file File data from Zend_File_Transfer 0368 * @return boolean 0369 */ 0370 public function isValid($value, $file = null) 0371 { 0372 if ($file === null) { 0373 $file = array( 0374 'type' => null, 0375 'name' => $value 0376 ); 0377 } 0378 0379 // Is file readable ? 0380 // require_once 'Zend/Loader.php'; 0381 if (!Zend_Loader::isReadable($value)) { 0382 return $this->_throw($file, self::NOT_READABLE); 0383 } 0384 0385 $this->_type = $this->_detectMimeType($value); 0386 0387 if (empty($this->_type) && $this->_headerCheck) { 0388 $this->_type = $file['type']; 0389 } 0390 0391 if (empty($this->_type)) { 0392 return $this->_throw($file, self::NOT_DETECTED); 0393 } 0394 0395 $mimetype = $this->getMimeType(true); 0396 if (in_array($this->_type, $mimetype)) { 0397 return true; 0398 } 0399 0400 $types = explode('/', $this->_type); 0401 $types = array_merge($types, explode('-', $this->_type)); 0402 $types = array_merge($types, explode(';', $this->_type)); 0403 foreach($mimetype as $mime) { 0404 if (in_array($mime, $types)) { 0405 return true; 0406 } 0407 } 0408 0409 return $this->_throw($file, self::FALSE_TYPE); 0410 } 0411 0412 /** 0413 * Throws an error of the given type 0414 * 0415 * @param string $file 0416 * @param string $errorType 0417 * @return false 0418 */ 0419 protected function _throw($file, $errorType) 0420 { 0421 $this->_value = $file['name']; 0422 $this->_error($errorType); 0423 return false; 0424 } 0425 0426 /** 0427 * Try to detect mime type of given file. 0428 * @param string $file File which mime type should be detected 0429 * @return string File mime type or null if not detected 0430 */ 0431 protected function _detectMimeType($file) 0432 { 0433 $mimefile = $this->getMagicFile(); 0434 $type = null; 0435 0436 if (class_exists('finfo', false)) { 0437 $const = defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME; 0438 0439 if (!empty($mimefile) && empty($this->_finfo)) { 0440 set_error_handler(array($this, '_errorHandler'), E_NOTICE | E_WARNING); 0441 $this->_finfo = finfo_open($const, $mimefile); 0442 restore_error_handler(); 0443 } 0444 0445 if (empty($this->_finfo)) { 0446 set_error_handler(array($this, '_errorHandler'), E_NOTICE | E_WARNING); 0447 $this->_finfo = finfo_open($const); 0448 restore_error_handler(); 0449 } 0450 0451 if (!empty($this->_finfo)) { 0452 $type = finfo_file($this->_finfo, $file); 0453 } 0454 } 0455 0456 if (empty($type) && 0457 (function_exists('mime_content_type') && ini_get('mime_magic.magicfile'))) { 0458 $type = mime_content_type($file); 0459 } 0460 0461 return $type; 0462 } 0463 0464 /** 0465 * Saves the provided error information by finfo_open to this instance 0466 * 0467 * @param integer $errno 0468 * @param string $errstr 0469 * @param string $errfile 0470 * @param integer $errline 0471 */ 0472 protected function _errorHandler($errno, $errstr, $errfile, $errline) 0473 { 0474 $this->_finfoError = new ErrorException($errstr, $errno, 0, $errfile, $errline); 0475 } 0476 }