File indexing completed on 2024-12-29 05:27: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_File_Transfer
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  * Abstract class for file transfers (Downloads and Uploads)
0024  *
0025  * @category  Zend
0026  * @package   Zend_File_Transfer
0027  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0028  * @license   http://framework.zend.com/license/new-bsd     New BSD License
0029  */
0030 abstract class Zend_File_Transfer_Adapter_Abstract
0031 {
0032     /**@+
0033      * Plugin loader Constants
0034      */
0035     const FILTER    = 'FILTER';
0036     const VALIDATE  = 'VALIDATE';
0037     /**@-*/
0038 
0039     /**
0040      * Internal list of breaks
0041      *
0042      * @var array
0043      */
0044     protected $_break = array();
0045 
0046     /**
0047      * Internal list of filters
0048      *
0049      * @var array
0050      */
0051     protected $_filters = array();
0052 
0053     /**
0054      * Plugin loaders for filter and validation chains
0055      *
0056      * @var array
0057      */
0058     protected $_loaders = array();
0059 
0060     /**
0061      * Internal list of messages
0062      *
0063      * @var array
0064      */
0065     protected $_messages = array();
0066 
0067     /**
0068      * @var Zend_Translate
0069      */
0070     protected $_translator;
0071 
0072     /**
0073      * Is translation disabled?
0074      *
0075      * @var bool
0076      */
0077     protected $_translatorDisabled = false;
0078 
0079     /**
0080      * Internal list of validators
0081      * @var array
0082      */
0083     protected $_validators = array();
0084 
0085     /**
0086      * Internal list of files
0087      * This array looks like this:
0088      *     array(form => array( - Form is the name within the form or, if not set the filename
0089      *         name,            - Original name of this file
0090      *         type,            - Mime type of this file
0091      *         size,            - Filesize in bytes
0092      *         tmp_name,        - Internalally temporary filename for uploaded files
0093      *         error,           - Error which has occured
0094      *         destination,     - New destination for this file
0095      *         validators,      - Set validator names for this file
0096      *         files            - Set file names for this file
0097      *     ))
0098      *
0099      * @var array
0100      */
0101     protected $_files = array();
0102 
0103     /**
0104      * TMP directory
0105      * @var string
0106      */
0107     protected $_tmpDir;
0108 
0109     /**
0110      * Available options for file transfers
0111      */
0112     protected $_options = array(
0113         'ignoreNoFile'  => false,
0114         'useByteString' => true,
0115         'magicFile'     => null,
0116         'detectInfos'   => true,
0117     );
0118 
0119     /**
0120      * Send file
0121      *
0122      * @param  mixed $options
0123      * @return bool
0124      */
0125     abstract public function send($options = null);
0126 
0127     /**
0128      * Receive file
0129      *
0130      * @param  mixed $options
0131      * @return bool
0132      */
0133     abstract public function receive($options = null);
0134 
0135     /**
0136      * Is file sent?
0137      *
0138      * @param  array|string|null $files
0139      * @return bool
0140      */
0141     abstract public function isSent($files = null);
0142 
0143     /**
0144      * Is file received?
0145      *
0146      * @param  array|string|null $files
0147      * @return bool
0148      */
0149     abstract public function isReceived($files = null);
0150 
0151     /**
0152      * Has a file been uploaded ?
0153      *
0154      * @param  array|string|null $files
0155      * @return bool
0156      */
0157     abstract public function isUploaded($files = null);
0158 
0159     /**
0160      * Has the file been filtered ?
0161      *
0162      * @param array|string|null $files
0163      * @return bool
0164      */
0165     abstract public function isFiltered($files = null);
0166 
0167     /**
0168      * Retrieve progress of transfer
0169      *
0170      * @return float
0171      */
0172     public static function getProgress()
0173     {
0174         // require_once 'Zend/File/Transfer/Exception.php';
0175         throw new Zend_File_Transfer_Exception('Method must be implemented within the adapter');
0176     }
0177 
0178     /**
0179      * Set plugin loader to use for validator or filter chain
0180      *
0181      * @param  Zend_Loader_PluginLoader_Interface $loader
0182      * @param  string $type 'filter', or 'validate'
0183      * @return Zend_File_Transfer_Adapter_Abstract
0184      * @throws Zend_File_Transfer_Exception on invalid type
0185      */
0186     public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader, $type)
0187     {
0188         $type = strtoupper($type);
0189         switch ($type) {
0190             case self::FILTER:
0191             case self::VALIDATE:
0192                 $this->_loaders[$type] = $loader;
0193                 return $this;
0194             default:
0195                 // require_once 'Zend/File/Transfer/Exception.php';
0196                 throw new Zend_File_Transfer_Exception(sprintf('Invalid type "%s" provided to setPluginLoader()', $type));
0197         }
0198     }
0199 
0200     /**
0201      * Retrieve plugin loader for validator or filter chain
0202      *
0203      * Instantiates with default rules if none available for that type. Use
0204      * 'filter' or 'validate' for $type.
0205      *
0206      * @param  string $type
0207      * @return Zend_Loader_PluginLoader
0208      * @throws Zend_File_Transfer_Exception on invalid type.
0209      */
0210     public function getPluginLoader($type)
0211     {
0212         $type = strtoupper($type);
0213         switch ($type) {
0214             case self::FILTER:
0215             case self::VALIDATE:
0216                 $prefixSegment = ucfirst(strtolower($type));
0217                 $pathSegment   = $prefixSegment;
0218                 if (!isset($this->_loaders[$type])) {
0219                     $paths         = array(
0220                         'Zend_' . $prefixSegment . '_'     => 'Zend/' . $pathSegment . '/',
0221                         'Zend_' . $prefixSegment . '_File' => 'Zend/' . $pathSegment . '/File',
0222                     );
0223 
0224                     // require_once 'Zend/Loader/PluginLoader.php';
0225                     $this->_loaders[$type] = new Zend_Loader_PluginLoader($paths);
0226                 } else {
0227                     $loader = $this->_loaders[$type];
0228                     $prefix = 'Zend_' . $prefixSegment . '_File_';
0229                     if (!$loader->getPaths($prefix)) {
0230                         $loader->addPrefixPath($prefix, str_replace('_', '/', $prefix));
0231                     }
0232                 }
0233                 return $this->_loaders[$type];
0234             default:
0235                 // require_once 'Zend/File/Transfer/Exception.php';
0236                 throw new Zend_File_Transfer_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type));
0237         }
0238     }
0239 
0240     /**
0241      * Add prefix path for plugin loader
0242      *
0243      * If no $type specified, assumes it is a base path for both filters and
0244      * validators, and sets each according to the following rules:
0245      * - filters:    $prefix = $prefix . '_Filter'
0246      * - validators: $prefix = $prefix . '_Validate'
0247      *
0248      * Otherwise, the path prefix is set on the appropriate plugin loader.
0249      *
0250      * @param  string $prefix
0251      * @param  string $path
0252      * @param  string $type
0253      * @return Zend_File_Transfer_Adapter_Abstract
0254      * @throws Zend_File_Transfer_Exception for invalid type
0255      */
0256     public function addPrefixPath($prefix, $path, $type = null)
0257     {
0258         $type = strtoupper($type);
0259         switch ($type) {
0260             case self::FILTER:
0261             case self::VALIDATE:
0262                 $loader = $this->getPluginLoader($type);
0263                 $loader->addPrefixPath($prefix, $path);
0264                 return $this;
0265             case null:
0266                 $prefix = rtrim($prefix, '_');
0267                 $path   = rtrim($path, DIRECTORY_SEPARATOR);
0268                 foreach (array(self::FILTER, self::VALIDATE) as $type) {
0269                     $cType        = ucfirst(strtolower($type));
0270                     $pluginPath   = $path . DIRECTORY_SEPARATOR . $cType . DIRECTORY_SEPARATOR;
0271                     $pluginPrefix = $prefix . '_' . $cType;
0272                     $loader       = $this->getPluginLoader($type);
0273                     $loader->addPrefixPath($pluginPrefix, $pluginPath);
0274                 }
0275                 return $this;
0276             default:
0277                 // require_once 'Zend/File/Transfer/Exception.php';
0278                 throw new Zend_File_Transfer_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type));
0279         }
0280     }
0281 
0282     /**
0283      * Add many prefix paths at once
0284      *
0285      * @param  array $spec
0286      * @return Zend_File_Transfer_Exception
0287      */
0288     public function addPrefixPaths(array $spec)
0289     {
0290         if (isset($spec['prefix']) && isset($spec['path'])) {
0291             return $this->addPrefixPath($spec['prefix'], $spec['path']);
0292         }
0293         foreach ($spec as $type => $paths) {
0294             if (is_numeric($type) && is_array($paths)) {
0295                 $type = null;
0296                 if (isset($paths['prefix']) && isset($paths['path'])) {
0297                     if (isset($paths['type'])) {
0298                         $type = $paths['type'];
0299                     }
0300                     $this->addPrefixPath($paths['prefix'], $paths['path'], $type);
0301                 }
0302             } elseif (!is_numeric($type)) {
0303                 if (!isset($paths['prefix']) || !isset($paths['path'])) {
0304                     foreach ($paths as $prefix => $spec) {
0305                         if (is_array($spec)) {
0306                             foreach ($spec as $path) {
0307                                 if (!is_string($path)) {
0308                                     continue;
0309                                 }
0310                                 $this->addPrefixPath($prefix, $path, $type);
0311                             }
0312                         } elseif (is_string($spec)) {
0313                             $this->addPrefixPath($prefix, $spec, $type);
0314                         }
0315                     }
0316                 } else {
0317                     $this->addPrefixPath($paths['prefix'], $paths['path'], $type);
0318                 }
0319             }
0320         }
0321         return $this;
0322     }
0323 
0324     /**
0325      * Adds a new validator for this class
0326      *
0327      * @param  string|array $validator           Type of validator to add
0328      * @param  boolean      $breakChainOnFailure If the validation chain should stop an failure
0329      * @param  string|array $options             Options to set for the validator
0330      * @param  string|array $files               Files to limit this validator to
0331      * @return Zend_File_Transfer_Adapter
0332      */
0333     public function addValidator($validator, $breakChainOnFailure = false, $options = null, $files = null)
0334     {
0335         if ($validator instanceof Zend_Validate_Interface) {
0336             $name = get_class($validator);
0337         } elseif (is_string($validator)) {
0338             $name      = $this->getPluginLoader(self::VALIDATE)->load($validator);
0339             $validator = new $name($options);
0340             if (is_array($options) && isset($options['messages'])) {
0341                 if (is_array($options['messages'])) {
0342                     $validator->setMessages($options['messages']);
0343                 } elseif (is_string($options['messages'])) {
0344                     $validator->setMessage($options['messages']);
0345                 }
0346 
0347                 unset($options['messages']);
0348             }
0349         } else {
0350             // require_once 'Zend/File/Transfer/Exception.php';
0351             throw new Zend_File_Transfer_Exception('Invalid validator provided to addValidator; must be string or Zend_Validate_Interface');
0352         }
0353 
0354         $this->_validators[$name] = $validator;
0355         $this->_break[$name]      = $breakChainOnFailure;
0356         $files                    = $this->_getFiles($files, true, true);
0357         foreach ($files as $file) {
0358             if ($name == 'NotEmpty') {
0359                 $temp = $this->_files[$file]['validators'];
0360                 $this->_files[$file]['validators']  = array($name);
0361                 $this->_files[$file]['validators'] += $temp;
0362             } else {
0363                 $this->_files[$file]['validators'][] = $name;
0364             }
0365 
0366             $this->_files[$file]['validated']    = false;
0367         }
0368 
0369         return $this;
0370     }
0371 
0372     /**
0373      * Add Multiple validators at once
0374      *
0375      * @param  array $validators
0376      * @param  string|array $files
0377      * @return Zend_File_Transfer_Adapter_Abstract
0378      */
0379     public function addValidators(array $validators, $files = null)
0380     {
0381         foreach ($validators as $name => $validatorInfo) {
0382             if ($validatorInfo instanceof Zend_Validate_Interface) {
0383                 $this->addValidator($validatorInfo, null, null, $files);
0384             } else if (is_string($validatorInfo)) {
0385                 if (!is_int($name)) {
0386                     $this->addValidator($name, null, $validatorInfo, $files);
0387                 } else {
0388                     $this->addValidator($validatorInfo, null, null, $files);
0389                 }
0390             } else if (is_array($validatorInfo)) {
0391                 $argc                = count($validatorInfo);
0392                 $breakChainOnFailure = false;
0393                 $options             = array();
0394                 if (isset($validatorInfo['validator'])) {
0395                     $validator = $validatorInfo['validator'];
0396                     if (isset($validatorInfo['breakChainOnFailure'])) {
0397                         $breakChainOnFailure = $validatorInfo['breakChainOnFailure'];
0398                     }
0399 
0400                     if (isset($validatorInfo['options'])) {
0401                         $options = $validatorInfo['options'];
0402                     }
0403 
0404                     $this->addValidator($validator, $breakChainOnFailure, $options, $files);
0405                 } else {
0406                     if (is_string($name)) {
0407                         $validator = $name;
0408                         $options   = $validatorInfo;
0409                         $this->addValidator($validator, $breakChainOnFailure, $options, $files);
0410                     } else {
0411                         $file = $files;
0412                         switch (true) {
0413                             case (0 == $argc):
0414                                 break;
0415                             case (1 <= $argc):
0416                                 $validator  = array_shift($validatorInfo);
0417                             case (2 <= $argc):
0418                                 $breakChainOnFailure = array_shift($validatorInfo);
0419                             case (3 <= $argc):
0420                                 $options = array_shift($validatorInfo);
0421                             case (4 <= $argc):
0422                                 if (!empty($validatorInfo)) {
0423                                     $file = array_shift($validatorInfo);
0424                                 }
0425                             default:
0426                                 $this->addValidator($validator, $breakChainOnFailure, $options, $file);
0427                                 break;
0428                         }
0429                     }
0430                 }
0431             } else {
0432                 // require_once 'Zend/File/Transfer/Exception.php';
0433                 throw new Zend_File_Transfer_Exception('Invalid validator passed to addValidators()');
0434             }
0435         }
0436 
0437         return $this;
0438     }
0439 
0440     /**
0441      * Sets a validator for the class, erasing all previous set
0442      *
0443      * @param  string|array $validator Validator to set
0444      * @param  string|array $files     Files to limit this validator to
0445      * @return Zend_File_Transfer_Adapter
0446      */
0447     public function setValidators(array $validators, $files = null)
0448     {
0449         $this->clearValidators();
0450         return $this->addValidators($validators, $files);
0451     }
0452 
0453     /**
0454      * Determine if a given validator has already been registered
0455      *
0456      * @param  string $name
0457      * @return bool
0458      */
0459     public function hasValidator($name)
0460     {
0461         return (false !== $this->_getValidatorIdentifier($name));
0462     }
0463 
0464     /**
0465      * Retrieve individual validator
0466      *
0467      * @param  string $name
0468      * @return Zend_Validate_Interface|null
0469      */
0470     public function getValidator($name)
0471     {
0472         if (false === ($identifier = $this->_getValidatorIdentifier($name))) {
0473             return null;
0474         }
0475         return $this->_validators[$identifier];
0476     }
0477 
0478     /**
0479      * Returns all set validators
0480      *
0481      * @param  string|array $files (Optional) Returns the validator for this files
0482      * @return null|array List of set validators
0483      */
0484     public function getValidators($files = null)
0485     {
0486         if ($files == null) {
0487             return $this->_validators;
0488         }
0489 
0490         $files      = $this->_getFiles($files, true, true);
0491         $validators = array();
0492         foreach ($files as $file) {
0493             if (!empty($this->_files[$file]['validators'])) {
0494                 $validators += $this->_files[$file]['validators'];
0495             }
0496         }
0497 
0498         $validators = array_unique($validators);
0499         $result     = array();
0500         foreach ($validators as $validator) {
0501             $result[$validator] = $this->_validators[$validator];
0502         }
0503 
0504         return $result;
0505     }
0506 
0507     /**
0508      * Remove an individual validator
0509      *
0510      * @param  string $name
0511      * @return Zend_File_Transfer_Adapter_Abstract
0512      */
0513     public function removeValidator($name)
0514     {
0515         if (false === ($key = $this->_getValidatorIdentifier($name))) {
0516             return $this;
0517         }
0518 
0519         unset($this->_validators[$key]);
0520         foreach (array_keys($this->_files) as $file) {
0521             if (empty($this->_files[$file]['validators'])) {
0522                 continue;
0523             }
0524 
0525             $index = array_search($key, $this->_files[$file]['validators']);
0526             if ($index === false) {
0527                 continue;
0528             }
0529 
0530             unset($this->_files[$file]['validators'][$index]);
0531             $this->_files[$file]['validated'] = false;
0532         }
0533 
0534         return $this;
0535     }
0536 
0537     /**
0538      * Remove all validators
0539      *
0540      * @return Zend_File_Transfer_Adapter_Abstract
0541      */
0542     public function clearValidators()
0543     {
0544         $this->_validators = array();
0545         foreach (array_keys($this->_files) as $file) {
0546             $this->_files[$file]['validators'] = array();
0547             $this->_files[$file]['validated']  = false;
0548         }
0549 
0550         return $this;
0551     }
0552 
0553     /**
0554      * Sets Options for adapters
0555      *
0556      * @param array $options Options to set
0557      * @param array $files   (Optional) Files to set the options for
0558      * @return Zend_File_Transfer_Adapter_Abstract
0559      */
0560     public function setOptions($options = array(), $files = null) {
0561         $file = $this->_getFiles($files, false, true);
0562 
0563         if (is_array($options)) {
0564             if (empty($file)) {
0565                 $this->_options = array_merge($this->_options, $options);
0566             }
0567 
0568             foreach ($options as $name => $value) {
0569                 foreach ($file as $key => $content) {
0570                     switch ($name) {
0571                         case 'magicFile' :
0572                             $this->_files[$key]['options'][$name] = (string) $value;
0573                             break;
0574 
0575                         case 'ignoreNoFile' :
0576                         case 'useByteString' :
0577                         case 'detectInfos' :
0578                             $this->_files[$key]['options'][$name] = (boolean) $value;
0579                             break;
0580 
0581                         default:
0582                             // require_once 'Zend/File/Transfer/Exception.php';
0583                             throw new Zend_File_Transfer_Exception("Unknown option: $name = $value");
0584                     }
0585                 }
0586             }
0587         }
0588 
0589         return $this;
0590     }
0591 
0592     /**
0593      * Returns set options for adapters or files
0594      *
0595      * @param  array $files (Optional) Files to return the options for
0596      * @return array Options for given files
0597      */
0598     public function getOptions($files = null) {
0599         $file = $this->_getFiles($files, false, true);
0600 
0601         foreach ($file as $key => $content) {
0602             if (isset($this->_files[$key]['options'])) {
0603                 $options[$key] = $this->_files[$key]['options'];
0604             } else {
0605                 $options[$key] = array();
0606             }
0607         }
0608 
0609         return $options;
0610     }
0611 
0612     /**
0613      * Checks if the files are valid
0614      *
0615      * @param  string|array $files (Optional) Files to check
0616      * @return boolean True if all checks are valid
0617      */
0618     public function isValid($files = null)
0619     {
0620         $check = $this->_getFiles($files, false, true);
0621         if (empty($check)) {
0622             return false;
0623         }
0624 
0625         $translator      = $this->getTranslator();
0626         $this->_messages = array();
0627         $break           = false;
0628         foreach($check as $key => $content) {
0629             if (array_key_exists('validators', $content) &&
0630                 in_array('Zend_Validate_File_Count', $content['validators'])) {
0631                 $validator = $this->_validators['Zend_Validate_File_Count'];
0632                 $count     = $content;
0633                 if (empty($content['tmp_name'])) {
0634                     continue;
0635                 }
0636 
0637                 if (array_key_exists('destination', $content)) {
0638                     $checkit = $content['destination'];
0639                 } else {
0640                     $checkit = dirname($content['tmp_name']);
0641                 }
0642 
0643                 $checkit .= DIRECTORY_SEPARATOR . $content['name'];
0644                     $validator->addFile($checkit);
0645             }
0646         }
0647 
0648         if (isset($count)) {
0649             if (!$validator->isValid($count['tmp_name'], $count)) {
0650                 $this->_messages += $validator->getMessages();
0651             }
0652         }
0653 
0654         foreach ($check as $key => $content) {
0655             $fileerrors  = array();
0656             if (array_key_exists('validators', $content) && $content['validated']) {
0657                 continue;
0658             }
0659 
0660             if (array_key_exists('validators', $content)) {
0661                 foreach ($content['validators'] as $class) {
0662                     $validator = $this->_validators[$class];
0663                     if (method_exists($validator, 'setTranslator')) {
0664                         $validator->setTranslator($translator);
0665                     }
0666 
0667                     if (($class === 'Zend_Validate_File_Upload') and (empty($content['tmp_name']))) {
0668                         $tocheck = $key;
0669                     } else {
0670                         $tocheck = $content['tmp_name'];
0671                     }
0672 
0673                     if (!$validator->isValid($tocheck, $content)) {
0674                         $fileerrors += $validator->getMessages();
0675                     }
0676 
0677                     if (!empty($content['options']['ignoreNoFile']) and (isset($fileerrors['fileUploadErrorNoFile']))) {
0678                         unset($fileerrors['fileUploadErrorNoFile']);
0679                         break;
0680                     }
0681 
0682                     if (($class === 'Zend_Validate_File_Upload') and (count($fileerrors) > 0)) {
0683                         break;
0684                     }
0685 
0686                     if (($this->_break[$class]) and (count($fileerrors) > 0)) {
0687                         $break = true;
0688                         break;
0689                     }
0690                 }
0691             }
0692 
0693             if (count($fileerrors) > 0) {
0694                 $this->_files[$key]['validated'] = false;
0695             } else {
0696                 $this->_files[$key]['validated'] = true;
0697             }
0698 
0699             $this->_messages += $fileerrors;
0700             if ($break) {
0701                 break;
0702             }
0703         }
0704 
0705         if (count($this->_messages) > 0) {
0706             return false;
0707         }
0708 
0709         return true;
0710     }
0711 
0712     /**
0713      * Returns found validation messages
0714      *
0715      * @return array
0716      */
0717     public function getMessages()
0718     {
0719         return $this->_messages;
0720     }
0721 
0722     /**
0723      * Retrieve error codes
0724      *
0725      * @return array
0726      */
0727     public function getErrors()
0728     {
0729         return array_keys($this->_messages);
0730     }
0731 
0732     /**
0733      * Are there errors registered?
0734      *
0735      * @return boolean
0736      */
0737     public function hasErrors()
0738     {
0739         return (!empty($this->_messages));
0740     }
0741 
0742     /**
0743      * Adds a new filter for this class
0744      *
0745      * @param  string|array $filter Type of filter to add
0746      * @param  string|array $options   Options to set for the filter
0747      * @param  string|array $files     Files to limit this filter to
0748      * @return Zend_File_Transfer_Adapter
0749      */
0750     public function addFilter($filter, $options = null, $files = null)
0751     {
0752         if ($filter instanceof Zend_Filter_Interface) {
0753             $class = get_class($filter);
0754         } elseif (is_string($filter)) {
0755             $class  = $this->getPluginLoader(self::FILTER)->load($filter);
0756             $filter = new $class($options);
0757         } else {
0758             // require_once 'Zend/File/Transfer/Exception.php';
0759             throw new Zend_File_Transfer_Exception('Invalid filter specified');
0760         }
0761 
0762         $this->_filters[$class] = $filter;
0763         $files                  = $this->_getFiles($files, true, true);
0764         foreach ($files as $file) {
0765             $this->_files[$file]['filters'][] = $class;
0766         }
0767 
0768         return $this;
0769     }
0770 
0771     /**
0772      * Add Multiple filters at once
0773      *
0774      * @param  array $filters
0775      * @param  string|array $files
0776      * @return Zend_File_Transfer_Adapter_Abstract
0777      */
0778     public function addFilters(array $filters, $files = null)
0779     {
0780         foreach ($filters as $key => $spec) {
0781             if ($spec instanceof Zend_Filter_Interface) {
0782                 $this->addFilter($spec, null, $files);
0783                 continue;
0784             }
0785 
0786             if (is_string($key)) {
0787                 $this->addFilter($key, $spec, $files);
0788                 continue;
0789             }
0790 
0791             if (is_int($key)) {
0792                 if (is_string($spec)) {
0793                     $this->addFilter($spec, null, $files);
0794                     continue;
0795                 }
0796 
0797                 if (is_array($spec)) {
0798                     if (!array_key_exists('filter', $spec)) {
0799                         continue;
0800                     }
0801 
0802                     $filter = $spec['filter'];
0803                     unset($spec['filter']);
0804                     $this->addFilter($filter, $spec, $files);
0805                     continue;
0806                 }
0807 
0808                 continue;
0809             }
0810         }
0811 
0812         return $this;
0813     }
0814 
0815     /**
0816      * Sets a filter for the class, erasing all previous set
0817      *
0818      * @param  string|array $filter Filter to set
0819      * @param  string|array $files     Files to limit this filter to
0820      * @return Zend_File_Transfer_Adapter
0821      */
0822     public function setFilters(array $filters, $files = null)
0823     {
0824         $this->clearFilters();
0825         return $this->addFilters($filters, $files);
0826     }
0827 
0828     /**
0829      * Determine if a given filter has already been registered
0830      *
0831      * @param  string $name
0832      * @return bool
0833      */
0834     public function hasFilter($name)
0835     {
0836         return (false !== $this->_getFilterIdentifier($name));
0837     }
0838 
0839     /**
0840      * Retrieve individual filter
0841      *
0842      * @param  string $name
0843      * @return Zend_Filter_Interface|null
0844      */
0845     public function getFilter($name)
0846     {
0847         if (false === ($identifier = $this->_getFilterIdentifier($name))) {
0848             return null;
0849         }
0850         return $this->_filters[$identifier];
0851     }
0852 
0853     /**
0854      * Returns all set filters
0855      *
0856      * @param  string|array $files (Optional) Returns the filter for this files
0857      * @return array List of set filters
0858      * @throws Zend_File_Transfer_Exception When file not found
0859      */
0860     public function getFilters($files = null)
0861     {
0862         if ($files === null) {
0863             return $this->_filters;
0864         }
0865 
0866         $files   = $this->_getFiles($files, true, true);
0867         $filters = array();
0868         foreach ($files as $file) {
0869             if (!empty($this->_files[$file]['filters'])) {
0870                 $filters += $this->_files[$file]['filters'];
0871             }
0872         }
0873 
0874         $filters = array_unique($filters);
0875         $result  = array();
0876         foreach ($filters as $filter) {
0877             $result[] = $this->_filters[$filter];
0878         }
0879 
0880         return $result;
0881     }
0882 
0883     /**
0884      * Remove an individual filter
0885      *
0886      * @param  string $name
0887      * @return Zend_File_Transfer_Adapter_Abstract
0888      */
0889     public function removeFilter($name)
0890     {
0891         if (false === ($key = $this->_getFilterIdentifier($name))) {
0892             return $this;
0893         }
0894 
0895         unset($this->_filters[$key]);
0896         foreach (array_keys($this->_files) as $file) {
0897             if (empty($this->_files[$file]['filters'])) {
0898                 continue;
0899             }
0900 
0901             $index = array_search($key, $this->_files[$file]['filters']);
0902             if ($index === false) {
0903                 continue;
0904             }
0905 
0906             unset($this->_files[$file]['filters'][$index]);
0907         }
0908         return $this;
0909     }
0910 
0911     /**
0912      * Remove all filters
0913      *
0914      * @return Zend_File_Transfer_Adapter_Abstract
0915      */
0916     public function clearFilters()
0917     {
0918         $this->_filters = array();
0919         foreach (array_keys($this->_files) as $file) {
0920             $this->_files[$file]['filters'] = array();
0921         }
0922         return $this;
0923     }
0924 
0925     /**
0926      * Returns all set files
0927      *
0928      * @return array List of set files
0929      * @throws Zend_File_Transfer_Exception Not implemented
0930      */
0931     public function getFile()
0932     {
0933         // require_once 'Zend/File/Transfer/Exception.php';
0934         throw new Zend_File_Transfer_Exception('Method not implemented');
0935     }
0936 
0937     /**
0938      * Retrieves the filename of transferred files.
0939      *
0940      * @param  string|null   $file
0941      * @param  boolean $path (Optional) Should the path also be returned ?
0942      * @return string|array
0943      */
0944     public function getFileName($file = null, $path = true)
0945     {
0946         $files     = $this->_getFiles($file, true, true);
0947         $result    = array();
0948         $directory = "";
0949         foreach($files as $file) {
0950             if (empty($this->_files[$file]['name'])) {
0951                 continue;
0952             }
0953 
0954             if ($path === true) {
0955                 $directory = $this->getDestination($file) . DIRECTORY_SEPARATOR;
0956             }
0957 
0958             $result[$file] = $directory . $this->_files[$file]['name'];
0959         }
0960 
0961         if (count($result) == 1) {
0962             return current($result);
0963         }
0964 
0965         return $result;
0966     }
0967 
0968     /**
0969      * Retrieve additional internal file informations for files
0970      *
0971      * @param  string $file (Optional) File to get informations for
0972      * @return array
0973      */
0974     public function getFileInfo($file = null)
0975     {
0976         return $this->_getFiles($file);
0977     }
0978 
0979     /**
0980      * Adds one or more files
0981      *
0982      * @param  string|array $file      File to add
0983      * @param  string|array $validator Validators to use for this file, must be set before
0984      * @param  string|array $filter    Filters to use for this file, must be set before
0985      * @return Zend_File_Transfer_Adapter_Abstract
0986      * @throws Zend_File_Transfer_Exception Not implemented
0987      */
0988     public function addFile($file, $validator = null, $filter = null)
0989     {
0990         // require_once 'Zend/File/Transfer/Exception.php';
0991         throw new Zend_File_Transfer_Exception('Method not implemented');
0992     }
0993 
0994     /**
0995      * Returns all set types
0996      *
0997      * @return array List of set types
0998      * @throws Zend_File_Transfer_Exception Not implemented
0999      */
1000     public function getType()
1001     {
1002         // require_once 'Zend/File/Transfer/Exception.php';
1003         throw new Zend_File_Transfer_Exception('Method not implemented');
1004     }
1005 
1006     /**
1007      * Adds one or more type of files
1008      *
1009      * @param  string|array $type Type of files to add
1010      * @param  string|array $validator Validators to use for this file, must be set before
1011      * @param  string|array $filter    Filters to use for this file, must be set before
1012      * @return Zend_File_Transfer_Adapter_Abstract
1013      * @throws Zend_File_Transfer_Exception Not implemented
1014      */
1015     public function addType($type, $validator = null, $filter = null)
1016     {
1017         // require_once 'Zend/File/Transfer/Exception.php';
1018         throw new Zend_File_Transfer_Exception('Method not implemented');
1019     }
1020 
1021     /**
1022      * Sets a new destination for the given files
1023      *
1024      * @deprecated Will be changed to be a filter!!!
1025      * @param  string       $destination New destination directory
1026      * @param  string|array $files       Files to set the new destination for
1027      * @return Zend_File_Transfer_Abstract
1028      * @throws Zend_File_Transfer_Exception when the given destination is not a directory or does not exist
1029      */
1030     public function setDestination($destination, $files = null)
1031     {
1032         $orig = $files;
1033         $destination = rtrim($destination, "/\\");
1034         if (!is_dir($destination)) {
1035             // require_once 'Zend/File/Transfer/Exception.php';
1036             throw new Zend_File_Transfer_Exception(
1037                 'The given destination is not a directory or does not exist'
1038             );
1039         }
1040 
1041         if (!$this->_isPathWriteable($destination)) {
1042             // require_once 'Zend/File/Transfer/Exception.php';
1043             throw new Zend_File_Transfer_Exception(
1044                 'The given destination is not writable'
1045             );
1046         }
1047 
1048         if ($files === null) {
1049             foreach ($this->_files as $file => $content) {
1050                 $this->_files[$file]['destination'] = $destination;
1051             }
1052         } else {
1053             $files = $this->_getFiles($files, true, true);
1054             if (empty($files) and is_string($orig)) {
1055                 $this->_files[$orig]['destination'] = $destination;
1056             }
1057 
1058             foreach ($files as $file) {
1059                 $this->_files[$file]['destination'] = $destination;
1060             }
1061         }
1062 
1063         return $this;
1064     }
1065 
1066     /**
1067      * Retrieve destination directory value
1068      *
1069      * @param  null|string|array $files
1070      * @return null|string|array
1071      * @throws Zend_File_Transfer_Exception
1072      */
1073     public function getDestination($files = null)
1074     {
1075         $orig  = $files;
1076         $files = $this->_getFiles($files, false, true);
1077         $destinations = array();
1078         if (empty($files) and is_string($orig)) {
1079             if (isset($this->_files[$orig]['destination'])) {
1080                 $destinations[$orig] = $this->_files[$orig]['destination'];
1081             } else {
1082                 // require_once 'Zend/File/Transfer/Exception.php';
1083                 throw new Zend_File_Transfer_Exception(sprintf('The file transfer adapter can not find "%s"', $orig));
1084             }
1085         }
1086 
1087         foreach ($files as $key => $content) {
1088             if (isset($this->_files[$key]['destination'])) {
1089                 $destinations[$key] = $this->_files[$key]['destination'];
1090             } else {
1091                 $tmpdir = $this->_getTmpDir();
1092                 $this->setDestination($tmpdir, $key);
1093                 $destinations[$key] = $tmpdir;
1094             }
1095         }
1096 
1097         if (empty($destinations)) {
1098             $destinations = $this->_getTmpDir();
1099         } else if (count($destinations) == 1) {
1100             $destinations = current($destinations);
1101         }
1102 
1103         return $destinations;
1104     }
1105 
1106     /**
1107      * Set translator object for localization
1108      *
1109      * @param  Zend_Translate|null $translator
1110      * @return Zend_File_Transfer_Abstract
1111      * @throws Zend_File_Transfer_Exception
1112      */
1113     public function setTranslator($translator = null)
1114     {
1115         if (null === $translator) {
1116             $this->_translator = null;
1117         } elseif ($translator instanceof Zend_Translate_Adapter) {
1118             $this->_translator = $translator;
1119         } elseif ($translator instanceof Zend_Translate) {
1120             $this->_translator = $translator->getAdapter();
1121         } else {
1122             // require_once 'Zend/File/Transfer/Exception.php';
1123             throw new Zend_File_Transfer_Exception('Invalid translator specified');
1124         }
1125 
1126         return $this;
1127     }
1128 
1129     /**
1130      * Retrieve localization translator object
1131      *
1132      * @return Zend_Translate_Adapter|null
1133      */
1134     public function getTranslator()
1135     {
1136         if ($this->translatorIsDisabled()) {
1137             return null;
1138         }
1139 
1140         return $this->_translator;
1141     }
1142 
1143     /**
1144      * Indicate whether or not translation should be disabled
1145      *
1146      * @param  bool $flag
1147      * @return Zend_File_Transfer_Abstract
1148      */
1149     public function setDisableTranslator($flag)
1150     {
1151         $this->_translatorDisabled = (bool) $flag;
1152         return $this;
1153     }
1154 
1155     /**
1156      * Is translation disabled?
1157      *
1158      * @return bool
1159      */
1160     public function translatorIsDisabled()
1161     {
1162         return $this->_translatorDisabled;
1163     }
1164 
1165     /**
1166      * Returns the hash for a given file
1167      *
1168      * @param  string       $hash  Hash algorithm to use
1169      * @param  string|array $files Files to return the hash for
1170      * @return string|array Hashstring
1171      * @throws Zend_File_Transfer_Exception On unknown hash algorithm
1172      */
1173     public function getHash($hash = 'crc32', $files = null)
1174     {
1175         if (!in_array($hash, hash_algos())) {
1176             // require_once 'Zend/File/Transfer/Exception.php';
1177             throw new Zend_File_Transfer_Exception('Unknown hash algorithm');
1178         }
1179 
1180         $files  = $this->_getFiles($files);
1181         $result = array();
1182         foreach($files as $key => $value) {
1183             if (file_exists($value['name'])) {
1184                 $result[$key] = hash_file($hash, $value['name']);
1185             } else if (file_exists($value['tmp_name'])) {
1186                 $result[$key] = hash_file($hash, $value['tmp_name']);
1187             } else if (empty($value['options']['ignoreNoFile'])) {
1188                 // require_once 'Zend/File/Transfer/Exception.php';
1189                 throw new Zend_File_Transfer_Exception("The file '{$value['name']}' does not exist");
1190             }
1191         }
1192 
1193         if (count($result) == 1) {
1194             return current($result);
1195         }
1196 
1197         return $result;
1198     }
1199 
1200     /**
1201      * Returns the real filesize of the file
1202      *
1203      * @param string|array $files Files to get the filesize from
1204      * @throws Zend_File_Transfer_Exception When the file does not exist
1205      * @return string|array Filesize
1206      */
1207     public function getFileSize($files = null)
1208     {
1209         $files  = $this->_getFiles($files);
1210         $result = array();
1211         foreach($files as $key => $value) {
1212             if (file_exists($value['name']) || file_exists($value['tmp_name'])) {
1213                 if ($value['options']['useByteString']) {
1214                     $result[$key] = self::_toByteString($value['size']);
1215                 } else {
1216                     $result[$key] = $value['size'];
1217                 }
1218             } else if (empty($value['options']['ignoreNoFile'])) {
1219                 // require_once 'Zend/File/Transfer/Exception.php';
1220                 throw new Zend_File_Transfer_Exception("The file '{$value['name']}' does not exist");
1221             } else {
1222                 continue;
1223             }
1224         }
1225 
1226         if (count($result) == 1) {
1227             return current($result);
1228         }
1229 
1230         return $result;
1231     }
1232 
1233     /**
1234      * Internal method to detect the size of a file
1235      *
1236      * @param  array $value File infos
1237      * @return string Filesize of given file
1238      */
1239     protected function _detectFileSize($value)
1240     {
1241         if (file_exists($value['name'])) {
1242             $result = sprintf("%u", @filesize($value['name']));
1243         } else if (file_exists($value['tmp_name'])) {
1244             $result = sprintf("%u", @filesize($value['tmp_name']));
1245         } else {
1246             return null;
1247         }
1248 
1249         return $result;
1250     }
1251 
1252     /**
1253      * Returns the real mimetype of the file
1254      * Uses fileinfo, when not available mime_magic and as last fallback a manual given mimetype
1255      *
1256      * @param string|array $files Files to get the mimetype from
1257      * @throws Zend_File_Transfer_Exception When the file does not exist
1258      * @return string|array MimeType
1259      */
1260     public function getMimeType($files = null)
1261     {
1262         $files  = $this->_getFiles($files);
1263         $result = array();
1264         foreach($files as $key => $value) {
1265             if (file_exists($value['name']) || file_exists($value['tmp_name'])) {
1266                 $result[$key] = $value['type'];
1267             } else if (empty($value['options']['ignoreNoFile'])) {
1268                 // require_once 'Zend/File/Transfer/Exception.php';
1269                 throw new Zend_File_Transfer_Exception("The file '{$value['name']}' does not exist");
1270             } else {
1271                 continue;
1272             }
1273         }
1274 
1275         if (count($result) == 1) {
1276             return current($result);
1277         }
1278 
1279         return $result;
1280     }
1281 
1282     /**
1283      * Internal method to detect the mime type of a file
1284      *
1285      * @param  array $value File infos
1286      * @return string Mimetype of given file
1287      */
1288     protected function _detectMimeType($value)
1289     {
1290         if (file_exists($value['name'])) {
1291             $file = $value['name'];
1292         } else if (file_exists($value['tmp_name'])) {
1293             $file = $value['tmp_name'];
1294         } else {
1295             return null;
1296         }
1297 
1298         if (class_exists('finfo', false)) {
1299             $const = defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME;
1300             if (!empty($value['options']['magicFile'])) {
1301                 $mime = @finfo_open($const, $value['options']['magicFile']);
1302             }
1303 
1304             if (empty($mime)) {
1305                 $mime = @finfo_open($const);
1306             }
1307 
1308             if (!empty($mime)) {
1309                 $result = finfo_file($mime, $file);
1310             }
1311 
1312             unset($mime);
1313         }
1314 
1315         if (empty($result) && (function_exists('mime_content_type')
1316             && ini_get('mime_magic.magicfile'))) {
1317             $result = mime_content_type($file);
1318         }
1319 
1320         if (empty($result)) {
1321             $result = 'application/octet-stream';
1322         }
1323 
1324         return $result;
1325     }
1326 
1327     /**
1328      * Returns the formatted size
1329      *
1330      * @param  integer $size
1331      * @return string
1332      */
1333     protected static function _toByteString($size)
1334     {
1335         $sizes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
1336         for ($i=0; $size >= 1024 && $i < 9; $i++) {
1337             $size /= 1024;
1338         }
1339 
1340         return round($size, 2) . $sizes[$i];
1341     }
1342 
1343     /**
1344      * Internal function to filter all given files
1345      *
1346      * @param  string|array $files (Optional) Files to check
1347      * @return boolean False on error
1348      */
1349     protected function _filter($files = null)
1350     {
1351         $check           = $this->_getFiles($files);
1352         foreach ($check as $name => $content) {
1353             if (array_key_exists('filters', $content)) {
1354                 foreach ($content['filters'] as $class) {
1355                     $filter = $this->_filters[$class];
1356                     try {
1357                         $result = $filter->filter($this->getFileName($name));
1358 
1359                         $this->_files[$name]['destination'] = dirname($result);
1360                         $this->_files[$name]['name']        = basename($result);
1361                     } catch (Zend_Filter_Exception $e) {
1362                         $this->_messages += array($e->getMessage());
1363                     }
1364                 }
1365             }
1366         }
1367 
1368         if (count($this->_messages) > 0) {
1369             return false;
1370         }
1371 
1372         return true;
1373     }
1374 
1375     /**
1376      * Determine system TMP directory and detect if we have read access
1377      *
1378      * @return string
1379      * @throws Zend_File_Transfer_Exception if unable to determine directory
1380      */
1381     protected function _getTmpDir()
1382     {
1383         if (null === $this->_tmpDir) {
1384             $tmpdir = array();
1385             if (function_exists('sys_get_temp_dir')) {
1386                 $tmpdir[] = sys_get_temp_dir();
1387             }
1388 
1389             if (!empty($_ENV['TMP'])) {
1390                 $tmpdir[] = realpath($_ENV['TMP']);
1391             }
1392 
1393             if (!empty($_ENV['TMPDIR'])) {
1394                 $tmpdir[] = realpath($_ENV['TMPDIR']);
1395             }
1396 
1397             if (!empty($_ENV['TEMP'])) {
1398                 $tmpdir[] = realpath($_ENV['TEMP']);
1399             }
1400 
1401             $upload = ini_get('upload_tmp_dir');
1402             if ($upload) {
1403                 $tmpdir[] = realpath($upload);
1404             }
1405 
1406             foreach($tmpdir as $directory) {
1407                 if ($this->_isPathWriteable($directory)) {
1408                     $this->_tmpDir = $directory;
1409                 }
1410             }
1411 
1412             if (empty($this->_tmpDir)) {
1413                 // Attemp to detect by creating a temporary file
1414                 $tempFile = tempnam(md5(uniqid(rand(), TRUE)), '');
1415                 if ($tempFile) {
1416                     $this->_tmpDir = realpath(dirname($tempFile));
1417                     unlink($tempFile);
1418                 } else {
1419                     // require_once 'Zend/File/Transfer/Exception.php';
1420                     throw new Zend_File_Transfer_Exception('Could not determine a temporary directory');
1421                 }
1422             }
1423 
1424             $this->_tmpDir = rtrim($this->_tmpDir, "/\\");
1425         }
1426         return $this->_tmpDir;
1427     }
1428 
1429     /**
1430      * Tries to detect if we can read and write to the given path
1431      *
1432      * @param string $path
1433      * @return bool
1434      */
1435     protected function _isPathWriteable($path)
1436     {
1437         $tempFile = rtrim($path, "/\\");
1438         $tempFile .= '/' . 'test.1';
1439 
1440         $result = @file_put_contents($tempFile, 'TEST');
1441 
1442         if ($result == false) {
1443             return false;
1444         }
1445 
1446         $result = @unlink($tempFile);
1447 
1448         if ($result == false) {
1449             return false;
1450         }
1451 
1452         return true;
1453     }
1454 
1455     /**
1456      * Returns found files based on internal file array and given files
1457      *
1458      * @param  string|array $files       (Optional) Files to return
1459      * @param  boolean      $names       (Optional) Returns only names on true, else complete info
1460      * @param  boolean      $noexception (Optional) Allows throwing an exception, otherwise returns an empty array
1461      * @return array Found files
1462      * @throws Zend_File_Transfer_Exception On false filename
1463      */
1464     protected function _getFiles($files, $names = false, $noexception = false)
1465     {
1466         $check = array();
1467 
1468         if (is_string($files)) {
1469             $files = array($files);
1470         }
1471 
1472         if (is_array($files)) {
1473             foreach ($files as $find) {
1474                 $found = array();
1475                 foreach ($this->_files as $file => $content) {
1476                     if (!isset($content['name'])) {
1477                         continue;
1478                     }
1479 
1480                     if (($content['name'] === $find) && isset($content['multifiles'])) {
1481                         foreach ($content['multifiles'] as $multifile) {
1482                             $found[] = $multifile;
1483                         }
1484                         break;
1485                     }
1486 
1487                     if ($file === $find) {
1488                         $found[] = $file;
1489                         break;
1490                     }
1491 
1492                     if ($content['name'] === $find) {
1493                         $found[] = $file;
1494                         break;
1495                     }
1496                 }
1497 
1498                 if (empty($found)) {
1499                     if ($noexception !== false) {
1500                         return array();
1501                     }
1502 
1503                     // require_once 'Zend/File/Transfer/Exception.php';
1504                     throw new Zend_File_Transfer_Exception(sprintf('The file transfer adapter can not find "%s"', $find));
1505                 }
1506 
1507                 foreach ($found as $checked) {
1508                     $check[$checked] = $this->_files[$checked];
1509                 }
1510             }
1511         }
1512 
1513         if ($files === null) {
1514             $check = $this->_files;
1515             $keys  = array_keys($check);
1516             foreach ($keys as $key) {
1517                 if (isset($check[$key]['multifiles'])) {
1518                     unset($check[$key]);
1519                 }
1520             }
1521         }
1522 
1523         if ($names) {
1524             $check = array_keys($check);
1525         }
1526 
1527         return $check;
1528     }
1529 
1530     /**
1531      * Retrieve internal identifier for a named validator
1532      *
1533      * @param  string $name
1534      * @return string
1535      */
1536     protected function _getValidatorIdentifier($name)
1537     {
1538         if (array_key_exists($name, $this->_validators)) {
1539             return $name;
1540         }
1541 
1542         foreach (array_keys($this->_validators) as $test) {
1543             if (preg_match('/' . preg_quote($name) . '$/i', $test)) {
1544                 return $test;
1545             }
1546         }
1547 
1548         return false;
1549     }
1550 
1551     /**
1552      * Retrieve internal identifier for a named filter
1553      *
1554      * @param  string $name
1555      * @return string
1556      */
1557     protected function _getFilterIdentifier($name)
1558     {
1559         if (array_key_exists($name, $this->_filters)) {
1560             return $name;
1561         }
1562 
1563         foreach (array_keys($this->_filters) as $test) {
1564             if (preg_match('/' . preg_quote($name) . '$/i', $test)) {
1565                 return $test;
1566             }
1567         }
1568 
1569         return false;
1570     }
1571 }