File indexing completed on 2025-05-04 05:31:20
0001 <?php 0002 /** 0003 * Base class for elFinder volume. 0004 * Provide 2 layers: 0005 * 1. Public API (commands) 0006 * 2. abstract fs API 0007 * 0008 * All abstract methods begin with "_" 0009 * 0010 * @author Dmitry (dio) Levashov 0011 * @author Troex Nevelin 0012 * @author Alexey Sukhotin 0013 **/ 0014 abstract class elFinderVolumeDriver { 0015 0016 /** 0017 * Driver id 0018 * Must be started from letter and contains [a-z0-9] 0019 * Used as part of volume id 0020 * 0021 * @var string 0022 **/ 0023 protected $driverId = 'a'; 0024 0025 /** 0026 * Volume id - used as prefix for files hashes 0027 * 0028 * @var string 0029 **/ 0030 protected $id = ''; 0031 0032 /** 0033 * Flag - volume "mounted" and available 0034 * 0035 * @var bool 0036 **/ 0037 protected $mounted = false; 0038 0039 /** 0040 * Root directory path 0041 * 0042 * @var string 0043 **/ 0044 protected $root = ''; 0045 0046 /** 0047 * Root basename | alias 0048 * 0049 * @var string 0050 **/ 0051 protected $rootName = ''; 0052 0053 /** 0054 * Default directory to open 0055 * 0056 * @var string 0057 **/ 0058 protected $startPath = ''; 0059 0060 /** 0061 * Base URL 0062 * 0063 * @var string 0064 **/ 0065 protected $URL = ''; 0066 0067 /** 0068 * Thumbnails dir path 0069 * 0070 * @var string 0071 **/ 0072 protected $tmbPath = ''; 0073 0074 /** 0075 * Is thumbnails dir writable 0076 * 0077 * @var bool 0078 **/ 0079 protected $tmbPathWritable = false; 0080 0081 /** 0082 * Thumbnails base URL 0083 * 0084 * @var string 0085 **/ 0086 protected $tmbURL = ''; 0087 0088 /** 0089 * Thumbnails size in px 0090 * 0091 * @var int 0092 **/ 0093 protected $tmbSize = 48; 0094 0095 /** 0096 * Image manipulation lib name 0097 * auto|imagick|mogtify|gd 0098 * 0099 * @var string 0100 **/ 0101 protected $imgLib = 'auto'; 0102 0103 /** 0104 * Library to crypt files name 0105 * 0106 * @var string 0107 **/ 0108 protected $cryptLib = ''; 0109 0110 /** 0111 * Archivers config 0112 * 0113 * @var array 0114 **/ 0115 protected $archivers = array( 0116 'create' => array(), 0117 'extract' => array() 0118 ); 0119 0120 /** 0121 * How many subdirs levels return for tree 0122 * 0123 * @var int 0124 **/ 0125 protected $treeDeep = 1; 0126 0127 /** 0128 * Errors from last failed action 0129 * 0130 * @var array 0131 **/ 0132 protected $error = array(); 0133 0134 /** 0135 * Today 24:00 timestamp 0136 * 0137 * @var int 0138 **/ 0139 protected $today = 0; 0140 0141 /** 0142 * Yesterday 24:00 timestamp 0143 * 0144 * @var int 0145 **/ 0146 protected $yesterday = 0; 0147 0148 /** 0149 * Object configuration 0150 * 0151 * @var array 0152 **/ 0153 protected $options = array( 0154 'id' => '', 0155 // root directory path 0156 'path' => '', 0157 // open this path on initial request instead of root path 0158 'startPath' => '', 0159 // how many subdirs levels return per request 0160 'treeDeep' => 1, 0161 // root url, not set to disable sending URL to client (replacement for old "fileURL" option) 0162 'URL' => '', 0163 // directory separator. required by client to show paths correctly 0164 'separator' => DIRECTORY_SEPARATOR, 0165 // library to crypt/uncrypt files names (not implemented) 0166 'cryptLib' => '', 0167 // how to detect files mimetypes. (auto/internal/finfo/mime_content_type) 0168 'mimeDetect' => 'auto', 0169 // mime.types file path (for mimeDetect==internal) 0170 'mimefile' => '', 0171 // directory for thumbnails 0172 'tmbPath' => '.tmb', 0173 // mode to create thumbnails dir 0174 'tmbPathMode' => 0777, 0175 // thumbnails dir URL. Set it if store thumbnails outside root directory 0176 'tmbURL' => '', 0177 // thumbnails size (px) 0178 'tmbSize' => 48, 0179 // thumbnails crop (true - crop, false - scale image to fit thumbnail size) 0180 'tmbCrop' => true, 0181 // thumbnails background color (hex #rrggbb or 'transparent') 0182 'tmbBgColor' => '#ffffff', 0183 // image manipulations library 0184 'imgLib' => 'auto', 0185 // on paste file - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext 0186 'copyOverwrite' => true, 0187 // if true - join new and old directories content on paste 0188 'copyJoin' => true, 0189 // on upload - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext 0190 'uploadOverwrite' => true, 0191 // mimetypes allowed to upload 0192 'uploadAllow' => array(), 0193 // mimetypes not allowed to upload 0194 'uploadDeny' => array(), 0195 // order to proccess uploadAllow and uploadDeny options 0196 'uploadOrder' => array('deny', 'allow'), 0197 // maximum upload file size. NOTE - this is size for every uploaded files 0198 'uploadMaxSize' => 0, 0199 // files dates format 0200 'dateFormat' => 'j M Y H:i', 0201 // files time format 0202 'timeFormat' => 'H:i', 0203 // if true - every folder will be check for children folders, otherwise all folders will be marked as having subfolders 0204 'checkSubfolders' => true, 0205 // allow to copy from this volume to other ones? 0206 'copyFrom' => true, 0207 // allow to copy from other volumes to this one? 0208 'copyTo' => true, 0209 // list of commands disabled on this root 0210 'disabled' => array(), 0211 // regexp or function name to validate new file name 0212 'acceptedName' => '/^\w[\w\s\.\%\-\(\)\[\]]*$/u', 0213 // function/class method to control files permissions 0214 'accessControl' => null, 0215 // some data required by access control 0216 'accessControlData' => null, 0217 // default permissions. not set hidden/locked here - take no effect 0218 'defaults' => array( 0219 'read' => true, 0220 'write' => true 0221 ), 0222 // files attributes 0223 'attributes' => array(), 0224 // Allowed archive's mimetypes to create. Leave empty for all available types. 0225 'archiveMimes' => array(), 0226 // Manual config for archivers. See example below. Leave empty for auto detect 0227 'archivers' => array(), 0228 // required to fix bug on macos 0229 'utf8fix' => false, 0230 // й ё Й Ё Ø Å 0231 'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"), 0232 'utf8replace' => array("\u0439", "\u0451", "\u0419", "\u0401", "\u00d8", "\u00c5") 0233 ); 0234 0235 /** 0236 * Defaults permissions 0237 * 0238 * @var array 0239 **/ 0240 protected $defaults = array( 0241 'read' => true, 0242 'write' => true, 0243 'locked' => false, 0244 'hidden' => false 0245 ); 0246 0247 /** 0248 * Access control function/class 0249 * 0250 * @var mixed 0251 **/ 0252 protected $attributes = array(); 0253 0254 /** 0255 * Access control function/class 0256 * 0257 * @var mixed 0258 **/ 0259 protected $access = null; 0260 0261 /** 0262 * Mime types allowed to upload 0263 * 0264 * @var array 0265 **/ 0266 protected $uploadAllow = array(); 0267 0268 /** 0269 * Mime types denied to upload 0270 * 0271 * @var array 0272 **/ 0273 protected $uploadDeny = array(); 0274 0275 /** 0276 * Order to validate uploadAllow and uploadDeny 0277 * 0278 * @var array 0279 **/ 0280 protected $uploadOrder = array(); 0281 0282 /** 0283 * Maximum allowed upload file size. 0284 * Set as number or string with unit - "10M", "500K", "1G" 0285 * 0286 * @var int|string 0287 **/ 0288 protected $uploadMaxSize = 0; 0289 0290 /** 0291 * Mimetype detect method 0292 * 0293 * @var string 0294 **/ 0295 protected $mimeDetect = 'auto'; 0296 0297 /** 0298 * Flag - mimetypes from externail file was loaded 0299 * 0300 * @var bool 0301 **/ 0302 private static $mimetypesLoaded = false; 0303 0304 /** 0305 * Finfo object for mimeDetect == 'finfo' 0306 * 0307 * @var object 0308 **/ 0309 protected $finfo = null; 0310 0311 /** 0312 * List of disabled client's commands 0313 * 0314 * @var array 0315 **/ 0316 protected $diabled = array(); 0317 0318 /** 0319 * default extensions/mimetypes for mimeDetect == 'internal' 0320 * 0321 * @var array 0322 **/ 0323 protected static $mimetypes = array( 0324 // applications 0325 'ai' => 'application/postscript', 0326 'eps' => 'application/postscript', 0327 'exe' => 'application/x-executable', 0328 'doc' => 'application/vnd.ms-word', 0329 'xls' => 'application/vnd.ms-excel', 0330 'ppt' => 'application/vnd.ms-powerpoint', 0331 'pps' => 'application/vnd.ms-powerpoint', 0332 'pdf' => 'application/pdf', 0333 'xml' => 'application/xml', 0334 'odt' => 'application/vnd.oasis.opendocument.text', 0335 'swf' => 'application/x-shockwave-flash', 0336 'torrent' => 'application/x-bittorrent', 0337 'jar' => 'application/x-jar', 0338 // archives 0339 'gz' => 'application/x-gzip', 0340 'tgz' => 'application/x-gzip', 0341 'bz' => 'application/x-bzip2', 0342 'bz2' => 'application/x-bzip2', 0343 'tbz' => 'application/x-bzip2', 0344 'zip' => 'application/zip', 0345 'rar' => 'application/x-rar', 0346 'tar' => 'application/x-tar', 0347 '7z' => 'application/x-7z-compressed', 0348 // texts 0349 'txt' => 'text/plain', 0350 'php' => 'text/x-php', 0351 'html' => 'text/html', 0352 'htm' => 'text/html', 0353 'js' => 'text/javascript', 0354 'css' => 'text/css', 0355 'rtf' => 'text/rtf', 0356 'rtfd' => 'text/rtfd', 0357 'py' => 'text/x-python', 0358 'java' => 'text/x-java-source', 0359 'rb' => 'text/x-ruby', 0360 'sh' => 'text/x-shellscript', 0361 'pl' => 'text/x-perl', 0362 'xml' => 'text/xml', 0363 'sql' => 'text/x-sql', 0364 'c' => 'text/x-csrc', 0365 'h' => 'text/x-chdr', 0366 'cpp' => 'text/x-c++src', 0367 'hh' => 'text/x-c++hdr', 0368 'log' => 'text/plain', 0369 'csv' => 'text/x-comma-separated-values', 0370 // images 0371 'bmp' => 'image/x-ms-bmp', 0372 'jpg' => 'image/jpeg', 0373 'jpeg' => 'image/jpeg', 0374 'gif' => 'image/gif', 0375 'png' => 'image/png', 0376 'tif' => 'image/tiff', 0377 'tiff' => 'image/tiff', 0378 'tga' => 'image/x-targa', 0379 'psd' => 'image/vnd.adobe.photoshop', 0380 'ai' => 'image/vnd.adobe.photoshop', 0381 'xbm' => 'image/xbm', 0382 'pxm' => 'image/pxm', 0383 //audio 0384 'mp3' => 'audio/mpeg', 0385 'mid' => 'audio/midi', 0386 'ogg' => 'audio/ogg', 0387 'oga' => 'audio/ogg', 0388 'm4a' => 'audio/x-m4a', 0389 'wav' => 'audio/wav', 0390 'wma' => 'audio/x-ms-wma', 0391 // video 0392 'avi' => 'video/x-msvideo', 0393 'dv' => 'video/x-dv', 0394 'mp4' => 'video/mp4', 0395 'mpeg' => 'video/mpeg', 0396 'mpg' => 'video/mpeg', 0397 'mov' => 'video/quicktime', 0398 'wm' => 'video/x-ms-wmv', 0399 'flv' => 'video/x-flv', 0400 'mkv' => 'video/x-matroska', 0401 'webm' => 'video/webm', 0402 'ogv' => 'video/ogg', 0403 'ogm' => 'video/ogg' 0404 ); 0405 0406 /** 0407 * Directory separator - required by client 0408 * 0409 * @var string 0410 **/ 0411 protected $separator = DIRECTORY_SEPARATOR; 0412 0413 /** 0414 * Mimetypes allowed to display 0415 * 0416 * @var array 0417 **/ 0418 protected $onlyMimes = array(); 0419 0420 /** 0421 * Store files moved or overwrited files info 0422 * 0423 * @var array 0424 **/ 0425 protected $removed = array(); 0426 0427 /** 0428 * Cache storage 0429 * 0430 * @var array 0431 **/ 0432 protected $cache = array(); 0433 0434 /** 0435 * Cache by folders 0436 * 0437 * @var array 0438 **/ 0439 protected $dirsCache = array(); 0440 0441 /*********************************************************************/ 0442 /* INITIALIZATION */ 0443 /*********************************************************************/ 0444 0445 /** 0446 * Prepare driver before mount volume. 0447 * Return true if volume is ready. 0448 * 0449 * @return bool 0450 * @author Dmitry (dio) Levashov 0451 **/ 0452 protected function init() { 0453 return true; 0454 } 0455 0456 /** 0457 * Configure after successfull mount. 0458 * By default set thumbnails path and image manipulation library. 0459 * 0460 * @return void 0461 * @author Dmitry (dio) Levashov 0462 **/ 0463 protected function configure() { 0464 // set thumbnails path 0465 $path = $this->options['tmbPath']; 0466 if ($path) { 0467 if (!file_exists($path)) { 0468 if (@mkdir($path)) { 0469 chmod($path, $this->options['tmbPathMode']); 0470 } else { 0471 $path = ''; 0472 } 0473 } 0474 0475 if (is_dir($path) && is_readable($path)) { 0476 $this->tmbPath = $path; 0477 $this->tmbPathWritable = is_writable($path); 0478 } 0479 } 0480 0481 // set image manipulation library 0482 $type = preg_match('/^(imagick|gd|auto)$/i', $this->options['imgLib']) 0483 ? strtolower($this->options['imgLib']) 0484 : 'auto'; 0485 0486 if (($type == 'imagick' || $type == 'auto') && extension_loaded('imagick')) { 0487 $this->imgLib = 'imagick'; 0488 } else { 0489 $this->imgLib = function_exists('gd_info') ? 'gd' : ''; 0490 } 0491 0492 } 0493 0494 0495 /*********************************************************************/ 0496 /* PUBLIC API */ 0497 /*********************************************************************/ 0498 0499 /** 0500 * Return driver id. Used as a part of volume id. 0501 * 0502 * @return string 0503 * @author Dmitry (dio) Levashov 0504 **/ 0505 public function driverId() { 0506 return $this->driverId; 0507 } 0508 0509 /** 0510 * Return volume id 0511 * 0512 * @return string 0513 * @author Dmitry (dio) Levashov 0514 **/ 0515 public function id() { 0516 return $this->id; 0517 } 0518 0519 /** 0520 * Return debug info for client 0521 * 0522 * @return array 0523 * @author Dmitry (dio) Levashov 0524 **/ 0525 public function debug() { 0526 return array( 0527 'id' => $this->id(), 0528 'name' => strtolower(substr(get_class($this), strlen('elfinderdriver'))), 0529 'mimeDetect' => $this->mimeDetect, 0530 'imgLib' => $this->imgLib 0531 ); 0532 } 0533 0534 /** 0535 * "Mount" volume. 0536 * Return true if volume available for read or write, 0537 * false - otherwise 0538 * 0539 * @return bool 0540 * @author Dmitry (dio) Levashov 0541 * @author Alexey Sukhotin 0542 **/ 0543 public function mount(array $opts) { 0544 if (!isset($opts['path']) || $opts['path'] === '') { 0545 return false; 0546 } 0547 0548 $this->options = array_merge($this->options, $opts); 0549 $this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_'; 0550 $this->root = $this->_normpath($this->options['path']); 0551 $this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR; 0552 0553 // default file attribute 0554 $this->defaults = array( 0555 'read' => isset($this->options['defaults']['read']) ? !!$this->options['defaults']['read'] : true, 0556 'write' => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true, 0557 'locked' => false, 0558 'hidden' => false 0559 ); 0560 0561 // root attributes 0562 $this->attributes[] = array( 0563 'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR).'$~', 0564 'locked' => true, 0565 'hidden' => false 0566 ); 0567 // set files attributes 0568 if (!empty($this->options['attributes']) && is_array($this->options['attributes'])) { 0569 0570 foreach ($this->options['attributes'] as $a) { 0571 // attributes must contain pattern and at least one rule 0572 if (!empty($a['pattern']) || count($a) > 1) { 0573 $this->attributes[] = $a; 0574 } 0575 } 0576 } 0577 0578 if (!empty($this->options['accessControl'])) { 0579 if (is_string($this->options['accessControl']) 0580 && function_exists($this->options['accessControl'])) { 0581 $this->access = $this->options['accessControl']; 0582 } elseif (is_array($this->options['accessControl']) 0583 && count($this->options['accessControl']) > 1 0584 && is_object($this->options['accessControl'][0]) 0585 && method_exists($this->options['accessControl'][0], $this->options['accessControl'][1])) { 0586 $this->access = array($this->options['accessControl'][0], $this->options['accessControl'][1]); 0587 } 0588 } 0589 0590 $this->today = mktime(0,0,0, date('m'), date('d'), date('Y')); 0591 $this->yesterday = $this->today-86400; 0592 0593 // debug($this->attributes); 0594 if (!$this->init()) { 0595 return false; 0596 } 0597 0598 // check some options is arrays 0599 $this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow']) 0600 ? $this->options['uploadAllow'] 0601 : array(); 0602 0603 $this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny']) 0604 ? $this->options['uploadDeny'] 0605 : array(); 0606 0607 if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x 0608 $parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow'); 0609 $this->uploadOrder = array(trim($parts[0]), trim($parts[1])); 0610 } else { // telephat_mode off 0611 $this->uploadOrder = $this->options['uploadOrder']; 0612 } 0613 0614 if (!empty($this->options['uploadMaxSize'])) { 0615 $size = ''.$this->options['uploadMaxSize']; 0616 $unit = strtolower(substr($size, strlen($size) - 1)); 0617 $n = 1; 0618 switch ($unit) { 0619 case 'k': 0620 $n = 1024; 0621 break; 0622 case 'm': 0623 $n = 1048576; 0624 break; 0625 case 'g': 0626 $n = 1073741824; 0627 } 0628 $this->uploadMaxSize = intval($size)*$n; 0629 } 0630 0631 $this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled']) 0632 ? $this->options['disabled'] 0633 : array(); 0634 0635 $this->cryptLib = $this->options['cryptLib']; 0636 $this->mimeDetect = $this->options['mimeDetect']; 0637 0638 // find available mimetype detect method 0639 $type = strtolower($this->options['mimeDetect']); 0640 $type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto'; 0641 $regexp = '/text\/x\-(php|c\+\+)/'; 0642 0643 if (($type == 'finfo' || $type == 'auto') 0644 && class_exists('finfo') 0645 && preg_match($regexp, array_shift(explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__))))) { 0646 $type = 'finfo'; 0647 $this->finfo = finfo_open(FILEINFO_MIME); 0648 } elseif (($type == 'mime_content_type' || $type == 'auto') 0649 && function_exists('mime_content_type') 0650 && preg_match($regexp, array_shift(explode(';', mime_content_type(__FILE__))))) { 0651 $type = 'mime_content_type'; 0652 } else { 0653 $type = 'internal'; 0654 } 0655 $this->mimeDetect = $type; 0656 0657 // load mimes from external file for mimeDetect == 'internal' 0658 // based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163 0659 // file must be in file directory or in parent one 0660 if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) { 0661 self::$mimetypesLoaded = true; 0662 $this->mimeDetect = 'internal'; 0663 $file = false; 0664 if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) { 0665 $file = $this->options['mimefile']; 0666 } elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) { 0667 $file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types'; 0668 } elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) { 0669 $file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types'; 0670 } 0671 0672 if ($file && file_exists($file)) { 0673 $mimecf = file($file); 0674 0675 foreach ($mimecf as $line_num => $line) { 0676 if (!preg_match('/^\s*#/', $line)) { 0677 $mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY); 0678 for ($i = 1, $size = count($mime); $i < $size ; $i++) { 0679 if (!isset(self::$mimetypes[$mime[$i]])) { 0680 self::$mimetypes[$mime[$i]] = $mime[0]; 0681 } 0682 } 0683 } 0684 } 0685 } 0686 } 0687 0688 $this->rootName = empty($this->options['alias']) ? $this->_basename($this->root) : $this->options['alias']; 0689 $root = $this->stat($this->root); 0690 0691 if (!$root) { 0692 return $this->setError('Root folder does not exists.'); 0693 } 0694 if (!$root['read'] && !$root['write']) { 0695 return $this->setError('Root folder has not read and write permissions.'); 0696 } 0697 0698 // debug($root); 0699 0700 if ($root['read']) { 0701 // check startPath - path to open by default instead of root 0702 if ($this->options['startPath']) { 0703 $start = $this->stat($this->options['startPath']); 0704 if (!empty($start) 0705 && $start['mime'] == 'directory' 0706 && $start['read'] 0707 && empty($start['hidden']) 0708 && $this->_inpath($this->options['startPath'], $this->root)) { 0709 $this->startPath = $this->options['startPath']; 0710 if (substr($this->startPath, -1, 1) == $this->options['separator']) { 0711 $this->startPath = substr($this->startPath, 0, -1); 0712 } 0713 } 0714 } 0715 } else { 0716 $this->options['URL'] = ''; 0717 $this->options['tmbURL'] = ''; 0718 $this->options['tmbPath'] = ''; 0719 // read only volume 0720 array_unshift($this->attributes, array( 0721 'pattern' => '/.*/', 0722 'read' => false 0723 )); 0724 } 0725 $this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1; 0726 $this->tmbSize = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48; 0727 $this->URL = $this->options['URL']; 0728 if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) { 0729 $this->URL .= '/'; 0730 } 0731 0732 $this->tmbURL = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : ''; 0733 if ($this->tmbURL && preg_match("|[^/?&=]$|", $this->tmbURL)) { 0734 $this->tmbURL .= '/'; 0735 } 0736 0737 $this->nameValidator = is_string($this->options['acceptedName']) && !empty($this->options['acceptedName']) 0738 ? $this->options['acceptedName'] 0739 : ''; 0740 0741 $this->_checkArchivers(); 0742 // manual control archive types to create 0743 if (!empty($this->options['archiveMimes']) && is_array($this->options['archiveMimes'])) { 0744 foreach ($this->archivers['create'] as $mime => $v) { 0745 if (!in_array($mime, $this->options['archiveMimes'])) { 0746 unset($this->archivers['create'][$mime]); 0747 } 0748 } 0749 } 0750 0751 // manualy add archivers 0752 if (!empty($this->options['archivers']['create']) && is_array($this->options['archivers']['create'])) { 0753 foreach ($this->options['archivers']['create'] as $mime => $conf) { 0754 if (strpos($mime, 'application/') === 0 0755 && !empty($conf['cmd']) 0756 && isset($conf['argc']) 0757 && !empty($conf['ext']) 0758 && !isset($this->archivers['create'][$mime])) { 0759 $this->archivers['create'][$mime] = $conf; 0760 } 0761 } 0762 } 0763 0764 if (!empty($this->options['archivers']['extract']) && is_array($this->options['archivers']['extract'])) { 0765 foreach ($this->options['archivers']['extract'] as $mime => $conf) { 0766 if (substr($mime, 'application/') === 0 0767 && !empty($cons['cmd']) 0768 && isset($conf['argc']) 0769 && !empty($conf['ext']) 0770 && !isset($this->archivers['extract'][$mime])) { 0771 $this->archivers['extract'][$mime] = $conf; 0772 } 0773 } 0774 } 0775 0776 $this->configure(); 0777 // echo $this->uploadMaxSize; 0778 // echo $this->options['uploadMaxSize']; 0779 return $this->mounted = true; 0780 } 0781 0782 /** 0783 * Some "unmount" stuffs - may be required by virtual fs 0784 * 0785 * @return void 0786 * @author Dmitry (dio) Levashov 0787 **/ 0788 public function umount() { 0789 } 0790 0791 /** 0792 * Return error message from last failed action 0793 * 0794 * @return array 0795 * @author Dmitry (dio) Levashov 0796 **/ 0797 public function error() { 0798 return $this->error; 0799 } 0800 0801 /** 0802 * Set mimetypes allowed to display to client 0803 * 0804 * @param array $mimes 0805 * @return void 0806 * @author Dmitry (dio) Levashov 0807 **/ 0808 public function setMimesFilter($mimes) { 0809 if (is_array($mimes)) { 0810 $this->onlyMimes = $mimes; 0811 } 0812 } 0813 0814 /** 0815 * Return root folder hash 0816 * 0817 * @return string 0818 * @author Dmitry (dio) Levashov 0819 **/ 0820 public function root() { 0821 return $this->encode($this->root); 0822 } 0823 0824 /** 0825 * Return root or startPath hash 0826 * 0827 * @return string 0828 * @author Dmitry (dio) Levashov 0829 **/ 0830 public function defaultPath() { 0831 return $this->encode($this->startPath ? $this->startPath : $this->root); 0832 } 0833 0834 /** 0835 * Return volume options required by client: 0836 * 0837 * @return array 0838 * @author Dmitry (dio) Levashov 0839 **/ 0840 public function options($hash) { 0841 return array( 0842 'path' => $this->_path($this->decode($hash)), 0843 'url' => $this->URL, 0844 'tmbUrl' => $this->tmbURL, 0845 'disabled' => $this->disabled, 0846 'separator' => $this->separator, 0847 'copyOverwrite' => intval($this->options['copyOverwrite']), 0848 'archivers' => array( 0849 'create' => array_keys($this->archivers['create']), 0850 'extract' => array_keys($this->archivers['extract']) 0851 ) 0852 ); 0853 } 0854 0855 /** 0856 * Return true if command disabled in options 0857 * 0858 * @param string $cmd command name 0859 * @return bool 0860 * @author Dmitry (dio) Levashov 0861 **/ 0862 public function commandDisabled($cmd) { 0863 return in_array($cmd, $this->disabled); 0864 } 0865 0866 /** 0867 * Return true if mime is required mimes list 0868 * 0869 * @param string $mime mime type to check 0870 * @param array $mimes allowed mime types list or not set to use client mimes list 0871 * @param bool|null $empty what to return on empty list 0872 * @return bool|null 0873 * @author Dmitry (dio) Levashov 0874 * @author Troex Nevelin 0875 **/ 0876 public function mimeAccepted($mime, $mimes = array(), $empty = true) { 0877 $mimes = !empty($mimes) ? $mimes : $this->onlyMimes; 0878 if (empty($mimes)) { 0879 return $empty; 0880 } 0881 return $mime == 'directory' 0882 || in_array('all', $mimes) 0883 || in_array('All', $mimes) 0884 || in_array($mime, $mimes) 0885 || in_array(substr($mime, 0, strpos($mime, '/')), $mimes); 0886 } 0887 0888 /** 0889 * Return true if voume is readable. 0890 * 0891 * @return bool 0892 * @author Dmitry (dio) Levashov 0893 **/ 0894 public function isReadable() { 0895 $stat = $this->stat($this->root); 0896 return $stat['read']; 0897 } 0898 0899 /** 0900 * Return true if copy from this volume allowed 0901 * 0902 * @return bool 0903 * @author Dmitry (dio) Levashov 0904 **/ 0905 public function copyFromAllowed() { 0906 return !!$this->options['copyFrom']; 0907 } 0908 0909 /** 0910 * Return file path related to root 0911 * 0912 * @param string $hash file hash 0913 * @return string 0914 * @author Dmitry (dio) Levashov 0915 **/ 0916 public function path($hash) { 0917 return $this->_path($this->decode($hash)); 0918 } 0919 0920 /** 0921 * Return file real path if file exists 0922 * 0923 * @param string $hash file hash 0924 * @return string 0925 * @author Dmitry (dio) Levashov 0926 **/ 0927 public function realpath($hash) { 0928 $path = $this->decode($hash); 0929 return $this->stat($path) ? $path : false; 0930 } 0931 0932 /** 0933 * Return list of moved/overwrited files 0934 * 0935 * @return array 0936 * @author Dmitry (dio) Levashov 0937 **/ 0938 public function removed() { 0939 return $this->removed; 0940 } 0941 0942 /** 0943 * Clean removed files list 0944 * 0945 * @return void 0946 * @author Dmitry (dio) Levashov 0947 **/ 0948 public function resetRemoved() { 0949 $this->removed = array(); 0950 } 0951 0952 /** 0953 * Return file/dir hash or first founded child hash with required attr == $val 0954 * 0955 * @param string $hash file hash 0956 * @param string $attr attribute name 0957 * @param bool $val attribute value 0958 * @return string|false 0959 * @author Dmitry (dio) Levashov 0960 **/ 0961 public function closest($hash, $attr, $val) { 0962 return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false; 0963 } 0964 0965 /** 0966 * Return file info or false on error 0967 * 0968 * @param string $hash file hash 0969 * @param bool $realpath add realpath field to file info 0970 * @return array|false 0971 * @author Dmitry (dio) Levashov 0972 **/ 0973 public function file($hash) { 0974 $path = $this->decode($hash); 0975 0976 return ($file = $this->stat($path)) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND); 0977 0978 if (($file = $this->stat($path)) != false) { 0979 if ($realpath) { 0980 $file['realpath'] = $path; 0981 } 0982 return $file; 0983 } 0984 return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); 0985 } 0986 0987 /** 0988 * Return folder info 0989 * 0990 * @param string $hash folder hash 0991 * @param bool $hidden return hidden file info 0992 * @return array|false 0993 * @author Dmitry (dio) Levashov 0994 **/ 0995 public function dir($hash, $resolveLink=false) { 0996 if (($dir = $this->file($hash)) == false) { 0997 return $this->setError(elFinder::ERROR_DIR_NOT_FOUND); 0998 } 0999 1000 if ($resolveLink && !empty($dir['thash'])) { 1001 $dir = $this->file($dir['thash']); 1002 } 1003 1004 return $dir && $dir['mime'] == 'directory' && empty($dir['hidden']) 1005 ? $dir 1006 : $this->setError(elFinder::ERROR_NOT_DIR); 1007 } 1008 1009 /** 1010 * Return directory content or false on error 1011 * 1012 * @param string $hash file hash 1013 * @return array|false 1014 * @author Dmitry (dio) Levashov 1015 **/ 1016 public function scandir($hash) { 1017 if (($dir = $this->dir($hash)) == false) { 1018 return false; 1019 } 1020 1021 return $dir['read'] 1022 ? $this->getScandir($this->decode($hash)) 1023 : $this->setError(elFinder::ERROR_PERM_DENIED); 1024 } 1025 1026 /** 1027 * Return dir files names list 1028 * 1029 * @param string $hash file hash 1030 * @return array 1031 * @author Dmitry (dio) Levashov 1032 **/ 1033 public function ls($hash) { 1034 if (($dir = $this->dir($hash)) == false || !$dir['read']) { 1035 return false; 1036 } 1037 1038 $list = array(); 1039 $path = $this->decode($hash); 1040 1041 foreach ($this->getScandir($path) as $stat) { 1042 if (empty($stat['hidden']) && $this->mimeAccepted($stat['mime'])) { 1043 $list[] = $stat['name']; 1044 } 1045 } 1046 1047 return $list; 1048 } 1049 1050 /** 1051 * Return subfolders for required folder or false on error 1052 * 1053 * @param string $hash folder hash or empty string to get tree from root folder 1054 * @param int $deep subdir deep 1055 * @param string $exclude dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders 1056 * @return array|false 1057 * @author Dmitry (dio) Levashov 1058 **/ 1059 public function tree($hash='', $deep=0, $exclude='') { 1060 $path = $hash ? $this->decode($hash) : $this->root; 1061 1062 if (($dir = $this->stat($path)) == false || $dir['mime'] != 'directory') { 1063 return false; 1064 } 1065 1066 $dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $this->decode($exclude)); 1067 array_unshift($dirs, $dir); 1068 return $dirs; 1069 } 1070 1071 /** 1072 * Return part of dirs tree from required dir up to root dir 1073 * 1074 * @param string $hash directory hash 1075 * @return array 1076 * @author Dmitry (dio) Levashov 1077 **/ 1078 public function parents($hash) { 1079 if (($current = $this->dir($hash)) == false) { 1080 return false; 1081 } 1082 1083 $path = $this->decode($hash); 1084 $tree = array(); 1085 1086 while ($path && $path != $this->root) { 1087 $path = $this->_dirname($path); 1088 $stat = $this->stat($path); 1089 if (!empty($stat['hidden']) || !$stat['read']) { 1090 return false; 1091 } 1092 1093 array_unshift($tree, $stat); 1094 if ($path != $this->root) { 1095 foreach ($this->gettree($path, 0) as $dir) { 1096 if (!in_array($dir, $tree)) { 1097 $tree[] = $dir; 1098 } 1099 } 1100 } 1101 } 1102 1103 return $tree ? $tree : array($current); 1104 } 1105 1106 /** 1107 * Create thumbnail for required file and return its name of false on failed 1108 * 1109 * @return string|false 1110 * @author Dmitry (dio) Levashov 1111 **/ 1112 public function tmb($hash) { 1113 $path = $this->decode($hash); 1114 $stat = $this->stat($path); 1115 1116 if (isset($stat['tmb'])) { 1117 return $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb']; 1118 } 1119 return false; 1120 } 1121 1122 /** 1123 * Return file size / total directory size 1124 * 1125 * @param string file hash 1126 * @return int 1127 * @author Dmitry (dio) Levashov 1128 **/ 1129 public function size($hash) { 1130 return $this->countSize($this->decode($hash)); 1131 } 1132 1133 /** 1134 * Open file for reading and return file pointer 1135 * 1136 * @param string file hash 1137 * @return Resource 1138 * @author Dmitry (dio) Levashov 1139 **/ 1140 public function open($hash) { 1141 if (($file = $this->file($hash)) == false 1142 || $file['mime'] == 'directory') { 1143 return false; 1144 } 1145 1146 return $this->_fopen($this->decode($hash), 'rb'); 1147 } 1148 1149 /** 1150 * Close file pointer 1151 * 1152 * @param Resource $fp file pointer 1153 * @param string $hash file hash 1154 * @return void 1155 * @author Dmitry (dio) Levashov 1156 **/ 1157 public function close($fp, $hash) { 1158 $this->_fclose($fp, $this->decode($hash)); 1159 } 1160 1161 /** 1162 * Create directory and return dir info 1163 * 1164 * @param string $dst destination directory 1165 * @param string $name directory name 1166 * @return array|false 1167 * @author Dmitry (dio) Levashov 1168 **/ 1169 public function mkdir($dst, $name) { 1170 if ($this->commandDisabled('mkdir')) { 1171 return $this->setError(elFinder::ERROR_PERM_DENIED); 1172 } 1173 1174 if (!$this->nameAccepted($name)) { 1175 return $this->setError(elFinder::ERROR_INVALID_NAME); 1176 } 1177 1178 if (($dir = $this->dir($dst)) == false) { 1179 return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst); 1180 } 1181 1182 if (!$dir['write']) { 1183 return $this->setError(elFinder::ERROR_PERM_DENIED); 1184 } 1185 1186 $path = $this->decode($dst); 1187 $dst = $this->_joinPath($path, $name); 1188 $stat = $this->stat($dst); 1189 if (!empty($stat)) { 1190 return $this->setError(elFinder::ERROR_EXISTS, $name); 1191 } 1192 $this->clearcache(); 1193 return ($path = $this->_mkdir($path, $name)) ? $this->stat($path) : false; 1194 } 1195 1196 /** 1197 * Create empty file and return its info 1198 * 1199 * @param string $dst destination directory 1200 * @param string $name file name 1201 * @return array|false 1202 * @author Dmitry (dio) Levashov 1203 **/ 1204 public function mkfile($dst, $name) { 1205 if ($this->commandDisabled('mkfile')) { 1206 return $this->setError(elFinder::ERROR_PERM_DENIED); 1207 } 1208 1209 if (!$this->nameAccepted($name)) { 1210 return $this->setError(elFinder::ERROR_INVALID_NAME); 1211 } 1212 1213 if (($dir = $this->dir($dst)) == false) { 1214 return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst); 1215 } 1216 1217 if (!$dir['write']) { 1218 return $this->setError(elFinder::ERROR_PERM_DENIED); 1219 } 1220 1221 $path = $this->decode($dst); 1222 1223 if ($this->stat($this->_joinPath($path, $name))) { 1224 return $this->setError(elFinder::ERROR_EXISTS, $name); 1225 } 1226 $this->clearcache(); 1227 return ($path = $this->_mkfile($path, $name)) ? $this->stat($path) : false; 1228 } 1229 1230 /** 1231 * Rename file and return file info 1232 * 1233 * @param string $hash file hash 1234 * @param string $name new file name 1235 * @return array|false 1236 * @author Dmitry (dio) Levashov 1237 **/ 1238 public function rename($hash, $name) { 1239 if ($this->commandDisabled('rename')) { 1240 return $this->setError(elFinder::ERROR_PERM_DENIED); 1241 } 1242 1243 if (!$this->nameAccepted($name)) { 1244 return $this->setError(elFinder::ERROR_INVALID_NAME, $name); 1245 } 1246 1247 if (!($file = $this->file($hash))) { 1248 return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); 1249 } 1250 1251 if ($name == $file['name']) { 1252 return $file; 1253 } 1254 1255 if (!empty($file['locked'])) { 1256 return $this->setError(elFinder::ERROR_LOCKED, $file['name']); 1257 } 1258 1259 $path = $this->decode($hash); 1260 $dir = $this->_dirname($path); 1261 $stat = $this->stat($this->_joinPath($dir, $name)); 1262 if ($stat) { 1263 return $this->setError(elFinder::ERROR_EXISTS, $name); 1264 } 1265 1266 if (!$this->_move($path, $dir, $name)) { 1267 return false; 1268 } 1269 1270 if (!empty($stat['tmb']) && $stat['tmb'] != "1") { 1271 $this->rmTmb($stat['tmb']); 1272 } 1273 1274 $path = $this->_joinPath($dir, $name); 1275 1276 $this->clearcache(); 1277 return $this->stat($path); 1278 } 1279 1280 /** 1281 * Create file copy with suffix "copy number" and return its info 1282 * 1283 * @param string $hash file hash 1284 * @param string $suffix suffix to add to file name 1285 * @return array|false 1286 * @author Dmitry (dio) Levashov 1287 **/ 1288 public function duplicate($hash, $suffix='copy') { 1289 if ($this->commandDisabled('duplicate')) { 1290 return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED); 1291 } 1292 1293 if (($file = $this->file($hash)) == false) { 1294 return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND); 1295 } 1296 1297 $path = $this->decode($hash); 1298 $dir = $this->_dirname($path); 1299 1300 return ($path = $this->copy($path, $dir, $this->uniqueName($dir, $this->_basename($path), ' '.$suffix.' '))) == false 1301 ? false 1302 : $this->stat($path); 1303 } 1304 1305 /** 1306 * Save uploaded file. 1307 * On success return array with new file stat and with removed file hash (if existed file was replaced) 1308 * 1309 * @param Resource $fp file pointer 1310 * @param string $dst destination folder hash 1311 * @param string $src file name 1312 * @param string $tmpname file tmp name - required to detect mime type 1313 * @return array|false 1314 * @author Dmitry (dio) Levashov 1315 **/ 1316 public function upload($fp, $dst, $name, $tmpname) { 1317 if ($this->commandDisabled('upload')) { 1318 return $this->setError(elFinder::ERROR_PERM_DENIED); 1319 } 1320 1321 if (($dir = $this->dir($dst)) == false) { 1322 return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst); 1323 } 1324 1325 if (!$dir['write']) { 1326 return $this->setError(elFinder::ERROR_PERM_DENIED); 1327 } 1328 1329 if (!$this->nameAccepted($name)) { 1330 return $this->setError(elFinder::ERROR_INVALID_NAME); 1331 } 1332 1333 $mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpname); 1334 if ($mime == 'unknown' && $this->mimeDetect == 'internal') { 1335 $mime = elFinderVolumeDriver::mimetypeInternalDetect($name); 1336 } 1337 1338 // logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order 1339 $allow = $this->mimeAccepted($mime, $this->uploadAllow, null); 1340 $deny = $this->mimeAccepted($mime, $this->uploadDeny, null); 1341 $upload = true; // default to allow 1342 if (strtolower($this->uploadOrder[0]) == 'allow') { // array('allow', 'deny'), default is to 'deny' 1343 $upload = false; // default is deny 1344 if (!$deny && ($allow === true)) { // match only allow 1345 $upload = true; 1346 }// else (both match | no match | match only deny) { deny } 1347 } else { // array('deny', 'allow'), default is to 'allow' - this is the default rule 1348 $upload = true; // default is allow 1349 if (($deny === true) && !$allow) { // match only deny 1350 $upload = false; 1351 } // else (both match | no match | match only allow) { allow } 1352 } 1353 if (!$upload) { 1354 return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME); 1355 } 1356 1357 if ($this->uploadMaxSize > 0 && filesize($tmpname) > $this->uploadMaxSize) { 1358 return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE); 1359 } 1360 1361 $dstpath = $this->decode($dst); 1362 $test = $this->_joinPath($dstpath, $name); 1363 1364 $file = $this->stat($test); 1365 $this->clearcache(); 1366 1367 if ($file) { // file exists 1368 if ($this->options['uploadOverwrite']) { 1369 if (!$file['write']) { 1370 return $this->setError(elFinder::ERROR_PERM_DENIED); 1371 } elseif ($file['mime'] == 'directory') { 1372 return $this->setError(elFinder::ERROR_NOT_REPLACE, $name); 1373 } 1374 $this->remove($file); 1375 } else { 1376 $name = $this->uniqueName($dstpath, $name, '-', false); 1377 } 1378 } 1379 1380 $w = $h = 0; 1381 if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) { 1382 $w = $s[0]; 1383 $h = $s[1]; 1384 } 1385 // $this->clearcache(); 1386 if (($path = $this->_save($fp, $dstpath, $name, $mime, $w, $h)) == false) { 1387 return false; 1388 } 1389 1390 1391 1392 return $this->stat($path); 1393 } 1394 1395 /** 1396 * Paste files 1397 * 1398 * @param Object $volume source volume 1399 * @param string $source file hash 1400 * @param string $dst destination dir hash 1401 * @param bool $rmSrc remove source after copy? 1402 * @return array|false 1403 * @author Dmitry (dio) Levashov 1404 **/ 1405 public function paste($volume, $src, $dst, $rmSrc = false) { 1406 $err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY; 1407 1408 if ($this->commandDisabled('paste')) { 1409 return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED); 1410 } 1411 1412 if (($file = $volume->file($src, $rmSrc)) == false) { 1413 return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND); 1414 } 1415 1416 $name = $file['name']; 1417 $errpath = $volume->path($src); 1418 1419 if (($dir = $this->dir($dst)) == false) { 1420 return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst); 1421 } 1422 1423 if (!$dir['write'] || !$file['read']) { 1424 return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED); 1425 } 1426 1427 $destination = $this->decode($dst); 1428 1429 if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) { 1430 return $rmSrc 1431 ? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test)) 1432 : $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED); 1433 } 1434 1435 $test = $this->_joinPath($destination, $name); 1436 $stat = $this->stat($test); 1437 $this->clearcache(); 1438 if ($stat) { 1439 if ($this->options['copyOverwrite']) { 1440 // do not replace file with dir or dir with file 1441 if (!$this->isSameType($file['mime'], $stat['mime'])) { 1442 return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->_path($test)); 1443 } 1444 // existed file is not writable 1445 if (!$stat['write']) { 1446 return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED); 1447 } 1448 // existed file locked or has locked child 1449 if (($locked = $this->closestByAttr($test, 'locked', true))) { 1450 return $this->setError(elFinder::ERROR_LOCKED, $this->_path($locked)); 1451 } 1452 // remove existed file 1453 if (!$this->remove($test)) { 1454 return $this->setError(elFinder::ERROR_REPLACE, $this->_path($test)); 1455 } 1456 } else { 1457 $name = $this->uniqueName($destination, $name, ' ', false); 1458 } 1459 } 1460 1461 // copy/move inside current volume 1462 if ($volume == $this) { 1463 $source = $this->decode($src); 1464 // do not copy into itself 1465 if ($this->_inpath($destination, $source)) { 1466 return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $path); 1467 } 1468 $method = $rmSrc ? 'move' : 'copy'; 1469 1470 return ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false; 1471 } 1472 1473 1474 // copy/move from another volume 1475 if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) { 1476 return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED); 1477 } 1478 1479 if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) { 1480 return false; 1481 } 1482 1483 if ($rmSrc) { 1484 if ($volume->rm($src)) { 1485 $this->removed[] = $file; 1486 } else { 1487 return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC); 1488 } 1489 } 1490 return $this->stat($path); 1491 } 1492 1493 /** 1494 * Return file contents 1495 * 1496 * @param string $hash file hash 1497 * @return string|false 1498 * @author Dmitry (dio) Levashov 1499 **/ 1500 public function getContents($hash) { 1501 $file = $this->file($hash); 1502 1503 if (!$file) { 1504 return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); 1505 } 1506 1507 if ($file['mime'] == 'directory') { 1508 return $this->setError(elFinder::ERROR_NOT_FILE); 1509 } 1510 1511 if (!$file['read']) { 1512 return $this->setError(elFinder::ERROR_PERM_DENIED); 1513 } 1514 1515 return $this->_getContents($this->decode($hash)); 1516 } 1517 1518 /** 1519 * Put content in text file and return file info. 1520 * 1521 * @param string $hash file hash 1522 * @param string $content new file content 1523 * @return array 1524 * @author Dmitry (dio) Levashov 1525 **/ 1526 public function putContents($hash, $content) { 1527 if ($this->commandDisabled('edit')) { 1528 return $this->setError(elFinder::ERROR_PERM_DENIED); 1529 } 1530 1531 $path = $this->decode($hash); 1532 1533 if (!($file = $this->file($hash))) { 1534 return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); 1535 } 1536 1537 if (!$file['write']) { 1538 return $this->setError(elFinder::ERROR_PERM_DENIED); 1539 } 1540 $this->clearcache(); 1541 return $this->_filePutContents($path, $content) ? $this->stat($path) : false; 1542 } 1543 1544 /** 1545 * Extract files from archive 1546 * 1547 * @param string $hash archive hash 1548 * @return array|bool 1549 * @author Dmitry (dio) Levashov, 1550 * @author Alexey Sukhotin 1551 **/ 1552 public function extract($hash) { 1553 if ($this->commandDisabled('extract')) { 1554 return $this->setError(elFinder::ERROR_PERM_DENIED); 1555 } 1556 1557 if (($file = $this->file($hash)) == false) { 1558 return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); 1559 } 1560 1561 $archiver = isset($this->archivers['extract'][$file['mime']]) 1562 ? $this->archivers['extract'][$file['mime']] 1563 : false; 1564 1565 if (!$archiver) { 1566 return $this->setError(elFinder::ERROR_NOT_ARCHIVE); 1567 } 1568 1569 $path = $this->decode($hash); 1570 $parent = $this->stat($this->_dirname($path)); 1571 1572 if (!$file['read'] || !$parent['write']) { 1573 return $this->setError(elFinder::ERROR_PERM_DENIED); 1574 } 1575 $this->clearcache(); 1576 return ($path = $this->_extract($path, $archiver)) ? $this->stat($path) : false; 1577 } 1578 1579 /** 1580 * Add files to archive 1581 * 1582 * @return void 1583 **/ 1584 public function archive($hashes, $mime) { 1585 if ($this->commandDisabled('archive')) { 1586 return $this->setError(elFinder::ERROR_PERM_DENIED); 1587 } 1588 1589 $archiver = isset($this->archivers['create'][$mime]) 1590 ? $this->archivers['create'][$mime] 1591 : false; 1592 1593 if (!$archiver) { 1594 return $this->setError(elFinder::ERROR_ARCHIVE_TYPE); 1595 } 1596 1597 $files = array(); 1598 1599 foreach ($hashes as $hash) { 1600 if (($file = $this->file($hash)) == false) { 1601 return $this->error(elFinder::ERROR_FILE_NOT_FOUND, '#'+$hash); 1602 } 1603 if (!$file['read']) { 1604 return $this->error(elFinder::ERROR_PERM_DENIED); 1605 } 1606 $path = $this->decode($hash); 1607 if (!isset($dir)) { 1608 $dir = $this->_dirname($path); 1609 $stat = $this->stat($dir); 1610 if (!$stat['write']) { 1611 return $this->error(elFinder::ERROR_PERM_DENIED); 1612 } 1613 } 1614 1615 $files[] = $this->_basename($path); 1616 } 1617 1618 $name = (count($files) == 1 ? $files[0] : 'Archive').'.'.$archiver['ext']; 1619 $name = $this->uniqueName($dir, $name, ''); 1620 $this->clearcache(); 1621 return ($path = $this->_archive($dir, $files, $name, $archiver)) ? $this->stat($path) : false; 1622 } 1623 1624 /** 1625 * Resize image 1626 * 1627 * @param string $hash image file 1628 * @param int $width new width 1629 * @param int $height new height 1630 * @param int $x X start poistion for crop 1631 * @param int $y Y start poistion for crop 1632 * @param string $mode action how to mainpulate image 1633 * @return array|false 1634 * @author Dmitry (dio) Levashov 1635 * @author Alexey Sukhotin 1636 * @author nao-pon 1637 * @author Troex Nevelin 1638 **/ 1639 public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) { 1640 if ($this->commandDisabled('resize')) { 1641 return $this->setError(elFinder::ERROR_PERM_DENIED); 1642 } 1643 1644 if (($file = $this->file($hash)) == false) { 1645 return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); 1646 } 1647 1648 if (!$file['write'] || !$file['read']) { 1649 return $this->setError(elFinder::ERROR_PERM_DENIED); 1650 } 1651 1652 $path = $this->decode($hash); 1653 1654 if (!$this->canResize($path, $file)) { 1655 return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE); 1656 } 1657 1658 switch($mode) { 1659 1660 case 'propresize': 1661 $result = $this->imgResize($path, $width, $height, true, true); 1662 break; 1663 1664 case 'crop': 1665 $result = $this->imgCrop($path, $width, $height, $x, $y); 1666 break; 1667 1668 case 'fitsquare': 1669 $result = $this->imgSquareFit($path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor'])); 1670 break; 1671 1672 case 'rotate': 1673 $result = $this->imgRotate($path, $degree, ($bg ? $bg : $this->options['tmbBgColor'])); 1674 break; 1675 1676 default: 1677 $result = $this->imgResize($path, $width, $height, false, true); 1678 break; 1679 } 1680 1681 if ($result) { 1682 if (!empty($file['tmb']) && $file['tmb'] != "1") { 1683 $this->rmTmb($file['tmb']); 1684 } 1685 $this->clearcache(); 1686 return $this->stat($path); 1687 } 1688 1689 return false; 1690 } 1691 1692 /** 1693 * Remove file/dir 1694 * 1695 * @param string $hash file hash 1696 * @return bool 1697 * @author Dmitry (dio) Levashov 1698 **/ 1699 public function rm($hash) { 1700 return $this->commandDisabled('rm') 1701 ? array(elFinder::ERROR_ACCESS_DENIED) 1702 : $this->remove($this->decode($hash)); 1703 } 1704 1705 /** 1706 * Search files 1707 * 1708 * @param string $q search string 1709 * @param array $mimes 1710 * @return array 1711 * @author Dmitry (dio) Levashov 1712 **/ 1713 public function search($q, $mimes) { 1714 return $this->doSearch($this->root, $q, $mimes); 1715 } 1716 1717 /** 1718 * Return image dimensions 1719 * 1720 * @param string $hash file hash 1721 * @return array 1722 * @author Dmitry (dio) Levashov 1723 **/ 1724 public function dimensions($hash) { 1725 if (($file = $this->file($hash)) == false) { 1726 return false; 1727 } 1728 1729 return $this->_dimensions($this->decode($hash), $file['mime']); 1730 } 1731 1732 /** 1733 * Save error message 1734 * 1735 * @param array error 1736 * @return false 1737 * @author Dmitry(dio) Levashov 1738 **/ 1739 protected function setError($error) { 1740 1741 $this->error = array(); 1742 1743 foreach (func_get_args() as $err) { 1744 if (is_array($err)) { 1745 $this->error = array_merge($this->error, $err); 1746 } else { 1747 $this->error[] = $err; 1748 } 1749 } 1750 1751 // $this->error = is_array($error) ? $error : func_get_args(); 1752 return false; 1753 } 1754 1755 /*********************************************************************/ 1756 /* FS API */ 1757 /*********************************************************************/ 1758 1759 /***************** paths *******************/ 1760 1761 /** 1762 * Encode path into hash 1763 * 1764 * @param string file path 1765 * @return string 1766 * @author Dmitry (dio) Levashov 1767 * @author Troex Nevelin 1768 **/ 1769 protected function encode($path) { 1770 if ($path !== '') { 1771 1772 // cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root 1773 $p = $this->_relpath($path); 1774 // if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt 1775 if ($p === '') { 1776 $p = DIRECTORY_SEPARATOR; 1777 } 1778 1779 // TODO crypt path and return hash 1780 $hash = $this->crypt($p); 1781 // hash is used as id in HTML that means it must contain vaild chars 1782 // make base64 html safe and append prefix in begining 1783 $hash = strtr(base64_encode($hash), '+/=', '-_.'); 1784 // remove dots '.' at the end, before it was '=' in base64 1785 $hash = rtrim($hash, '.'); 1786 // append volume id to make hash unique 1787 return $this->id.$hash; 1788 } 1789 } 1790 1791 /** 1792 * Decode path from hash 1793 * 1794 * @param string file hash 1795 * @return string 1796 * @author Dmitry (dio) Levashov 1797 * @author Troex Nevelin 1798 **/ 1799 protected function decode($hash) { 1800 if (strpos($hash, $this->id) === 0) { 1801 // cut volume id after it was prepended in encode 1802 $h = substr($hash, strlen($this->id)); 1803 // replace HTML safe base64 to normal 1804 $h = base64_decode(strtr($h, '-_.', '+/=')); 1805 // TODO uncrypt hash and return path 1806 $path = $this->uncrypt($h); 1807 // append ROOT to path after it was cut in encode 1808 return $this->_abspath($path);//$this->root.($path == DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR.$path); 1809 } 1810 } 1811 1812 /** 1813 * Return crypted path 1814 * Not implemented 1815 * 1816 * @param string path 1817 * @return mixed 1818 * @author Dmitry (dio) Levashov 1819 **/ 1820 protected function crypt($path) { 1821 return $path; 1822 } 1823 1824 /** 1825 * Return uncrypted path 1826 * Not implemented 1827 * 1828 * @param mixed hash 1829 * @return mixed 1830 * @author Dmitry (dio) Levashov 1831 **/ 1832 protected function uncrypt($hash) { 1833 return $hash; 1834 } 1835 1836 /** 1837 * Validate file name based on $this->options['acceptedName'] regexp 1838 * 1839 * @param string $name file name 1840 * @return bool 1841 * @author Dmitry (dio) Levashov 1842 **/ 1843 protected function nameAccepted($name) { 1844 if ($this->nameValidator) { 1845 if (function_exists($this->nameValidator)) { 1846 $f = $this->nameValidator; 1847 return $f($name); 1848 } 1849 return preg_match($this->nameValidator, $name); 1850 } 1851 return true; 1852 } 1853 1854 /** 1855 * Return new unique name based on file name and suffix 1856 * 1857 * @param string $path file path 1858 * @param string $suffix suffix append to name 1859 * @return string 1860 * @author Dmitry (dio) Levashov 1861 **/ 1862 public function uniqueName($dir, $name, $suffix = ' copy', $checkNum=true) { 1863 $ext = ''; 1864 1865 if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) { 1866 $ext = '.'.$m[1]; 1867 $name = substr($name, 0, strlen($name)-strlen($m[0])); 1868 } 1869 1870 if ($checkNum && preg_match('/('.$suffix.')(\d*)$/i', $name, $m)) { 1871 $i = (int)$m[2]; 1872 $name = substr($name, 0, strlen($name)-strlen($m[2])); 1873 } else { 1874 $i = 1; 1875 $name .= $suffix; 1876 } 1877 $max = $i+100000; 1878 1879 while ($i <= $max) { 1880 $n = $name.($i > 0 ? $i : '').$ext; 1881 1882 if (!$this->stat($this->_joinPath($dir, $n))) { 1883 $this->clearcache(); 1884 return $n; 1885 } 1886 $i++; 1887 } 1888 return $name.md5($dir).$ext; 1889 } 1890 1891 /*********************** file stat *********************/ 1892 1893 /** 1894 * Check file attribute 1895 * 1896 * @param string $path file path 1897 * @param string $name attribute name (read|write|locked|hidden) 1898 * @param bool $val attribute value returned by file system 1899 * @return bool 1900 * @author Dmitry (dio) Levashov 1901 **/ 1902 protected function attr($path, $name, $val=false) { 1903 if (!isset($this->defaults[$name])) { 1904 return false; 1905 } 1906 1907 1908 $perm = null; 1909 1910 if ($this->access) { 1911 if (is_array($this->access)) { 1912 $obj = $this->access[0]; 1913 $method = $this->access[1]; 1914 $perm = $obj->{$method}($name, $path, $this->options['accessControlData'], $this); 1915 } else { 1916 $func = $this->access; 1917 $perm = $func($name, $path, $this->options['accessControlData'], $this); 1918 } 1919 1920 if ($perm !== null) { 1921 return !!$perm; 1922 } 1923 } 1924 1925 for ($i = 0, $c = count($this->attributes); $i < $c; $i++) { 1926 $attrs = $this->attributes[$i]; 1927 $p = $this->separator.$this->_relpath($path); 1928 if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $p)) { 1929 $perm = $attrs[$name]; 1930 } 1931 } 1932 1933 return $perm === null ? $this->defaults[$name] : !!$perm; 1934 } 1935 1936 /** 1937 * Return fileinfo 1938 * 1939 * @param string $path file cache 1940 * @return array 1941 * @author Dmitry (dio) Levashov 1942 **/ 1943 protected function stat($path) { 1944 return isset($this->cache[$path]) 1945 ? $this->cache[$path] 1946 : $this->updateCache($path, $this->_stat($path)); 1947 } 1948 1949 /** 1950 * Put file stat in cache and return it 1951 * 1952 * @param string $path file path 1953 * @param array $stat file stat 1954 * @return array 1955 * @author Dmitry (dio) Levashov 1956 **/ 1957 protected function updateCache($path, $stat) { 1958 if (empty($stat) || !is_array($stat)) { 1959 return $this->cache[$path] = array(); 1960 } 1961 1962 $stat['hash'] = $this->encode($path); 1963 1964 $root = $path == $this->root; 1965 1966 if ($root) { 1967 $stat['volumeid'] = $this->id; 1968 if ($this->rootName) { 1969 $stat['name'] = $this->rootName; 1970 } 1971 } else { 1972 if (empty($stat['name'])) { 1973 $stat['name'] = $this->_basename($path); 1974 } 1975 if (empty($stat['phash'])) { 1976 $stat['phash'] = $this->encode($this->_dirname($path)); 1977 } 1978 } 1979 1980 // fix name if required 1981 if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) { 1982 $stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], json_encode($stat['name']))); 1983 } 1984 1985 1986 if (empty($stat['mime'])) { 1987 $stat['mime'] = $this->mimetype($stat['name']); 1988 } 1989 1990 // @todo move dateformat to client 1991 $stat['date'] = isset($stat['ts']) 1992 ? $this->formatDate($stat['ts']) 1993 : 'unknown'; 1994 1995 if (!isset($stat['size'])) { 1996 $stat['size'] = 'unknown'; 1997 } 1998 1999 $stat['read'] = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : false)); 2000 $stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : false)); 2001 if ($root) { 2002 $stat['locked'] = 1; 2003 } elseif ($this->attr($path, 'locked', !empty($stat['locked']))) { 2004 $stat['locked'] = 1; 2005 } else { 2006 unset($stat['locked']); 2007 } 2008 2009 if ($root) { 2010 unset($stat['hidden']); 2011 } elseif ($this->attr($path, 'hidden', !empty($stat['hidden'])) 2012 || !$this->mimeAccepted($stat['mime'])) { 2013 $stat['hidden'] = $root ? 0 : 1; 2014 } else { 2015 unset($stat['hidden']); 2016 } 2017 2018 if ($stat['read'] && empty($stat['hidden'])) { 2019 2020 if ($stat['mime'] == 'directory') { 2021 // for dir - check for subdirs 2022 2023 if ($this->options['checkSubfolders']) { 2024 if (isset($stat['dirs'])) { 2025 if ($stat['dirs']) { 2026 $stat['dirs'] = 1; 2027 } else { 2028 unset($stat['dirs']); 2029 } 2030 } elseif (!empty($stat['alias']) && !empty($stat['target'])) { 2031 $stat['dirs'] = isset($this->cache[$stat['target']]) 2032 ? intval(isset($this->cache[$stat['target']]['dirs'])) 2033 : $this->_subdirs($stat['target']); 2034 2035 } elseif ($this->_subdirs($path)) { 2036 $stat['dirs'] = 1; 2037 } 2038 } else { 2039 $stat['dirs'] = 1; 2040 } 2041 } else { 2042 // for files - check for thumbnails 2043 $p = isset($stat['target']) ? $stat['target'] : $path; 2044 if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) { 2045 $tmb = $this->gettmb($p, $stat); 2046 $stat['tmb'] = $tmb ? $tmb : 1; 2047 } 2048 2049 } 2050 } 2051 2052 if (!empty($stat['alias']) && !empty($stat['target'])) { 2053 $stat['thash'] = $this->encode($stat['target']); 2054 unset($stat['target']); 2055 } 2056 2057 return $this->cache[$path] = $stat; 2058 } 2059 2060 /** 2061 * Get stat for folder content and put in cache 2062 * 2063 * @param string $path 2064 * @return void 2065 * @author Dmitry (dio) Levashov 2066 **/ 2067 protected function cacheDir($path) { 2068 $this->dirsCache[$path] = array(); 2069 2070 foreach ($this->_scandir($path) as $p) { 2071 if (($stat = $this->stat($p)) && empty($stat['hidden'])) { 2072 $this->dirsCache[$path][] = $p; 2073 } 2074 } 2075 } 2076 2077 /** 2078 * Clean cache 2079 * 2080 * @return void 2081 * @author Dmitry (dio) Levashov 2082 **/ 2083 protected function clearcache() { 2084 $this->cache = $this->dirsCache = array(); 2085 } 2086 2087 /** 2088 * Return file mimetype 2089 * 2090 * @param string $path file path 2091 * @return string 2092 * @author Dmitry (dio) Levashov 2093 **/ 2094 protected function mimetype($path) { 2095 $type = ''; 2096 2097 if ($this->mimeDetect == 'finfo') { 2098 $type = @finfo_file($this->finfo, $path); 2099 } elseif ($type == 'mime_content_type') { 2100 $type = mime_content_type($path); 2101 } else { 2102 $type = elFinderVolumeDriver::mimetypeInternalDetect($path); 2103 } 2104 2105 $type = explode(';', $type); 2106 $type = trim($type[0]); 2107 2108 if ($type == 'application/x-empty') { 2109 // finfo return this mime for empty files 2110 $type = 'text/plain'; 2111 } elseif ($type == 'application/x-zip') { 2112 // http://elrte.org/redmine/issues/163 2113 $type = 'application/zip'; 2114 } 2115 2116 return $type == 'unknown' && $this->mimeDetect != 'internal' 2117 ? elFinderVolumeDriver::mimetypeInternalDetect($path) 2118 : $type; 2119 2120 } 2121 2122 /** 2123 * Detect file mimetype using "internal" method 2124 * 2125 * @param string $path file path 2126 * @return string 2127 * @author Dmitry (dio) Levashov 2128 **/ 2129 static protected function mimetypeInternalDetect($path) { 2130 $pinfo = pathinfo($path); 2131 $ext = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : ''; 2132 return isset(elFinderVolumeDriver::$mimetypes[$ext]) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown'; 2133 2134 } 2135 2136 /** 2137 * Return file/total directory size 2138 * 2139 * @param string $path file path 2140 * @return int 2141 * @author Dmitry (dio) Levashov 2142 **/ 2143 protected function countSize($path) { 2144 $stat = $this->stat($path); 2145 2146 if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) { 2147 return 'unknown'; 2148 } 2149 2150 if ($stat['mime'] != 'directory') { 2151 return $stat['size']; 2152 } 2153 2154 $subdirs = $this->options['checkSubfolders']; 2155 $this->options['checkSubfolders'] = true; 2156 $result = 0; 2157 foreach ($this->getScandir($path) as $stat) { 2158 $size = $stat['mime'] == 'directory' && $stat['read'] 2159 ? $this->countSize($this->_joinPath($path, $stat['name'])) 2160 : $stat['size']; 2161 if ($size > 0) { 2162 $result += $size; 2163 } 2164 } 2165 $this->options['checkSubfolders'] = $subdirs; 2166 return $result; 2167 } 2168 2169 /** 2170 * Return true if all mimes is directory or files 2171 * 2172 * @param string $mime1 mimetype 2173 * @param string $mime2 mimetype 2174 * @return bool 2175 * @author Dmitry (dio) Levashov 2176 **/ 2177 protected function isSameType($mime1, $mime2) { 2178 return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory'); 2179 } 2180 2181 /** 2182 * If file has required attr == $val - return file path, 2183 * If dir has child with has required attr == $val - return child path 2184 * 2185 * @param string $path file path 2186 * @param string $attr attribute name 2187 * @param bool $val attribute value 2188 * @return string|false 2189 * @author Dmitry (dio) Levashov 2190 **/ 2191 protected function closestByAttr($path, $attr, $val) { 2192 $stat = $this->stat($path); 2193 2194 if (empty($stat)) { 2195 return false; 2196 } 2197 2198 $v = isset($stat[$attr]) ? $stat[$attr] : false; 2199 2200 if ($v == $val) { 2201 return $path; 2202 } 2203 2204 return $stat['mime'] == 'directory' 2205 ? $this->childsByAttr($path, $attr, $val) 2206 : false; 2207 } 2208 2209 /** 2210 * Return first found children with required attr == $val 2211 * 2212 * @param string $path file path 2213 * @param string $attr attribute name 2214 * @param bool $val attribute value 2215 * @return string|false 2216 * @author Dmitry (dio) Levashov 2217 **/ 2218 protected function childsByAttr($path, $attr, $val) { 2219 foreach ($this->_scandir($path) as $p) { 2220 if (($_p = $this->closestByAttr($p, $attr, $val)) != false) { 2221 return $_p; 2222 } 2223 } 2224 return false; 2225 } 2226 2227 /***************** get content *******************/ 2228 2229 /** 2230 * Return required dir's files info. 2231 * If onlyMimes is set - return only dirs and files of required mimes 2232 * 2233 * @param string $path dir path 2234 * @return array 2235 * @author Dmitry (dio) Levashov 2236 **/ 2237 protected function getScandir($path) { 2238 $files = array(); 2239 2240 !isset($this->dirsCache[$path]) && $this->cacheDir($path); 2241 2242 foreach ($this->dirsCache[$path] as $p) { 2243 if (($stat = $this->stat($p)) && empty($stat['hidden'])) { 2244 $files[] = $stat; 2245 } 2246 } 2247 2248 return $files; 2249 } 2250 2251 2252 /** 2253 * Return subdirs tree 2254 * 2255 * @param string $path parent dir path 2256 * @param int $deep tree deep 2257 * @return array 2258 * @author Dmitry (dio) Levashov 2259 **/ 2260 protected function gettree($path, $deep, $exclude='') { 2261 $dirs = array(); 2262 2263 !isset($this->dirsCache[$path]) && $this->cacheDir($path); 2264 2265 foreach ($this->dirsCache[$path] as $p) { 2266 $stat = $this->stat($p); 2267 2268 if ($stat && empty($stat['hidden']) && $path != $exclude && $stat['mime'] == 'directory') { 2269 $dirs[] = $stat; 2270 if ($deep > 0 && !empty($stat['dirs'])) { 2271 $dirs = array_merge($dirs, $this->gettree($p, $deep-1)); 2272 } 2273 } 2274 } 2275 2276 return $dirs; 2277 } 2278 2279 /** 2280 * Recursive files search 2281 * 2282 * @param string $path dir path 2283 * @param string $q search string 2284 * @param array $mimes 2285 * @return array 2286 * @author Dmitry (dio) Levashov 2287 **/ 2288 protected function doSearch($path, $q, $mimes) { 2289 $result = array(); 2290 2291 foreach($this->_scandir($path) as $p) { 2292 $stat = $this->stat($p); 2293 2294 if (!$stat) { // invalid links 2295 continue; 2296 } 2297 2298 if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'])) { 2299 continue; 2300 } 2301 2302 $name = $stat['name']; 2303 2304 if ($this->stripos($name, $q) !== false) { 2305 $stat['path'] = $this->_path($p); 2306 if ($this->URL && !isset($stat['url'])) { 2307 $stat['url'] = $this->URL . str_replace($this->separator, '/', substr($p, strlen($this->root) + 1)); 2308 } 2309 2310 $result[] = $stat; 2311 } 2312 if ($stat['mime'] == 'directory' && $stat['read'] && !isset($stat['alias'])) { 2313 $result = array_merge($result, $this->doSearch($p, $q, $mimes)); 2314 } 2315 } 2316 2317 return $result; 2318 } 2319 2320 /********************** manuipulations ******************/ 2321 2322 /** 2323 * Copy file/recursive copy dir only in current volume. 2324 * Return new file path or false. 2325 * 2326 * @param string $src source path 2327 * @param string $dst destination dir path 2328 * @param string $name new file name (optionaly) 2329 * @return string|false 2330 * @author Dmitry (dio) Levashov 2331 **/ 2332 protected function copy($src, $dst, $name) { 2333 $srcStat = $this->stat($src); 2334 $this->clearcache(); 2335 2336 if (!empty($srcStat['thash'])) { 2337 $target = $this->decode($srcStat['thash']); 2338 $stat = $this->stat($target); 2339 $this->clearcache(); 2340 return $stat && $this->_symlink($target, $dst, $name) 2341 ? $this->_joinPath($dst, $name) 2342 : $this->setError(elFinder::ERROR_COPY, $this->_path($src)); 2343 } 2344 2345 if ($srcStat['mime'] == 'directory') { 2346 $test = $this->stat($this->_joinPath($dst, $name)); 2347 2348 if (($test && $test['mime'] != 'directory') || !$this->_mkdir($dst, $name)) { 2349 return $this->setError(elFinder::ERROR_COPY, $this->_path($src)); 2350 } 2351 2352 $dst = $this->_joinPath($dst, $name); 2353 2354 foreach ($this->getScandir($src) as $stat) { 2355 if (empty($stat['hidden'])) { 2356 $name = $stat['name']; 2357 if (!$this->copy($this->_joinPath($src, $name), $dst, $name)) { 2358 return false; 2359 } 2360 } 2361 } 2362 $this->clearcache(); 2363 return $dst; 2364 } 2365 2366 return $this->_copy($src, $dst, $name) 2367 ? $this->_joinPath($dst, $name) 2368 : $this->setError(elFinder::ERROR_COPY, $this->_path($src)); 2369 } 2370 2371 /** 2372 * Move file 2373 * Return new file path or false. 2374 * 2375 * @param string $src source path 2376 * @param string $dst destination dir path 2377 * @param string $name new file name 2378 * @return string|false 2379 * @author Dmitry (dio) Levashov 2380 **/ 2381 protected function move($src, $dst, $name) { 2382 $stat = $this->stat($src); 2383 $stat['realpath'] = $src; 2384 $this->clearcache(); 2385 2386 if ($this->_move($src, $dst, $name)) { 2387 $this->removed[] = $stat; 2388 return $this->_joinPath($dst, $name); 2389 } 2390 2391 return $this->setError(elFinder::ERROR_MOVE, $this->_path($src)); 2392 } 2393 2394 /** 2395 * Copy file from another volume. 2396 * Return new file path or false. 2397 * 2398 * @param Object $volume source volume 2399 * @param string $src source file hash 2400 * @param string $destination destination dir path 2401 * @param string $name file name 2402 * @return string|false 2403 * @author Dmitry (dio) Levashov 2404 **/ 2405 protected function copyFrom($volume, $src, $destination, $name) { 2406 2407 if (($source = $volume->file($src)) == false) { 2408 return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error()); 2409 } 2410 2411 $errpath = $volume->path($src); 2412 2413 if (!$this->nameAccepted($source['name'])) { 2414 return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME); 2415 } 2416 2417 if (!$source['read']) { 2418 return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED); 2419 } 2420 2421 if ($source['mime'] == 'directory') { 2422 $stat = $this->stat($this->_joinPath($destination, $name)); 2423 $this->clearcache(); 2424 if ((!$stat || $stat['mime'] != 'directory') && !$this->_mkdir($destination, $name)) { 2425 return $this->setError(elFinder::ERROR_COPY, $errpath); 2426 } 2427 2428 $path = $this->_joinPath($destination, $name); 2429 2430 foreach ($volume->scandir($src) as $entr) { 2431 if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) { 2432 return false; 2433 } 2434 } 2435 2436 } else { 2437 $mime = $source['mime']; 2438 $w = $h = 0; 2439 if (strpos($mime, 'image') === 0 && ($dim = $volume->dimensions($src))) { 2440 $s = explode('x', $dim); 2441 $w = $s[0]; 2442 $h = $s[1]; 2443 } 2444 2445 if (($fp = $volume->open($src)) == false 2446 || ($path = $this->_save($fp, $destination, $name, $mime, $w, $h)) == false) { 2447 $fp && $volume->close($fp, $src); 2448 return $this->setError(elFinder::ERROR_COPY, $errpath); 2449 } 2450 $volume->close($fp, $src); 2451 } 2452 2453 return $path; 2454 } 2455 2456 /** 2457 * Remove file/ recursive remove dir 2458 * 2459 * @param string $path file path 2460 * @param bool $force try to remove even if file locked 2461 * @return bool 2462 * @author Dmitry (dio) Levashov 2463 **/ 2464 protected function remove($path, $force = false) { 2465 $stat = $this->stat($path); 2466 $stat['realpath'] = $path; 2467 if (!empty($stat['tmb']) && $stat['tmb'] != "1") { 2468 $this->rmTmb($stat['tmb']); 2469 } 2470 $this->clearcache(); 2471 2472 if (empty($stat)) { 2473 return $this->setError(elFinder::ERROR_RM, $this->_path($path), elFinder::ERROR_FILE_NOT_FOUND); 2474 } 2475 2476 if (!$force && !empty($stat['locked'])) { 2477 return $this->setError(elFinder::ERROR_LOCKED, $this->_path($path)); 2478 } 2479 2480 if ($stat['mime'] == 'directory') { 2481 foreach ($this->_scandir($path) as $p) { 2482 $name = $this->_basename($p); 2483 if ($name != '.' && $name != '..' && !$this->remove($p)) { 2484 return false; 2485 } 2486 } 2487 if (!$this->_rmdir($path)) { 2488 return $this->setError(elFinder::ERROR_RM, $this->_path($path)); 2489 } 2490 2491 } else { 2492 if (!$this->_unlink($path)) { 2493 return $this->setError(elFinder::ERROR_RM, $this->_path($path)); 2494 } 2495 } 2496 2497 $this->removed[] = $stat; 2498 return true; 2499 } 2500 2501 2502 /************************* thumbnails **************************/ 2503 2504 /** 2505 * Return thumbnail file name for required file 2506 * 2507 * @param array $stat file stat 2508 * @return string 2509 * @author Dmitry (dio) Levashov 2510 **/ 2511 protected function tmbname($stat) { 2512 return $stat['hash'].$stat['ts'].'.png'; 2513 } 2514 2515 /** 2516 * Return thumnbnail name if exists 2517 * 2518 * @param string $path file path 2519 * @param array $stat file stat 2520 * @return string|false 2521 * @author Dmitry (dio) Levashov 2522 **/ 2523 protected function gettmb($path, $stat) { 2524 if ($this->tmbURL && $this->tmbPath) { 2525 // file itself thumnbnail 2526 if (strpos($path, $this->tmbPath) === 0) { 2527 return basename($path); 2528 } 2529 2530 $name = $this->tmbname($stat); 2531 if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) { 2532 return $name; 2533 } 2534 } 2535 return false; 2536 } 2537 2538 /** 2539 * Return true if thumnbnail for required file can be created 2540 * 2541 * @param string $path thumnbnail path 2542 * @param array $stat file stat 2543 * @return string|bool 2544 * @author Dmitry (dio) Levashov 2545 **/ 2546 protected function canCreateTmb($path, $stat) { 2547 return $this->tmbPathWritable 2548 && strpos($path, $this->tmbPath) === false // do not create thumnbnail for thumnbnail 2549 && $this->imgLib 2550 && strpos($stat['mime'], 'image') === 0 2551 && ($this->imgLib == 'gd' ? $stat['mime'] == 'image/jpeg' || $stat['mime'] == 'image/png' || $stat['mime'] == 'image/gif' : true); 2552 } 2553 2554 /** 2555 * Return true if required file can be resized. 2556 * By default - the same as canCreateTmb 2557 * 2558 * @param string $path thumnbnail path 2559 * @param array $stat file stat 2560 * @return string|bool 2561 * @author Dmitry (dio) Levashov 2562 **/ 2563 protected function canResize($path, $stat) { 2564 return $this->canCreateTmb($path, $stat); 2565 } 2566 2567 /** 2568 * Create thumnbnail and return it's URL on success 2569 * 2570 * @param string $path file path 2571 * @param string $mime file mime type 2572 * @return string|false 2573 * @author Dmitry (dio) Levashov 2574 **/ 2575 protected function createTmb($path, $stat) { 2576 if (!$stat || !$this->canCreateTmb($path, $stat)) { 2577 return false; 2578 } 2579 2580 $name = $this->tmbname($stat); 2581 $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$name; 2582 2583 // copy image into tmbPath so some drivers does not store files on local fs 2584 if (($src = $this->_fopen($path, 'rb')) == false) { 2585 return false; 2586 } 2587 2588 if (($trg = fopen($tmb, 'wb')) == false) { 2589 $this->_fclose($src, $path); 2590 return false; 2591 } 2592 2593 while (!feof($src)) { 2594 fwrite($trg, fread($src, 8192)); 2595 } 2596 2597 $this->_fclose($src, $path); 2598 fclose($trg); 2599 2600 $result = false; 2601 2602 $tmbSize = $this->tmbSize; 2603 2604 if (($s = getimagesize($tmb)) == false) { 2605 return false; 2606 } 2607 2608 /* If image smaller or equal thumbnail size - just fitting to thumbnail square */ 2609 if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) { 2610 $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' ); 2611 2612 } else { 2613 2614 if ($this->options['tmbCrop']) { 2615 2616 /* Resize and crop if image bigger than thumbnail */ 2617 if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize) ) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) { 2618 $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png'); 2619 } 2620 2621 if (($s = getimagesize($tmb)) != false) { 2622 $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0; 2623 $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0; 2624 $result = $this->imgCrop($tmb, $tmbSize, $tmbSize, $x, $y, 'png'); 2625 } 2626 2627 } else { 2628 $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, $this->imgLib, 'png'); 2629 $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' ); 2630 } 2631 2632 } 2633 if (!$result) { 2634 unlink($tmb); 2635 return false; 2636 } 2637 2638 return $name; 2639 } 2640 2641 /** 2642 * Resize image 2643 * 2644 * @param string $path image file 2645 * @param int $width new width 2646 * @param int $height new height 2647 * @param bool $keepProportions crop image 2648 * @param bool $resizeByBiggerSide resize image based on bigger side if true 2649 * @param string $destformat image destination format 2650 * @return string|false 2651 * @author Dmitry (dio) Levashov 2652 * @author Alexey Sukhotin 2653 **/ 2654 protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) { 2655 if (($s = @getimagesize($path)) == false) { 2656 return false; 2657 } 2658 2659 $result = false; 2660 2661 list($size_w, $size_h) = array($width, $height); 2662 2663 if ($keepProportions == true) { 2664 2665 list($orig_w, $orig_h, $new_w, $new_h) = array($s[0], $s[1], $width, $height); 2666 2667 /* Calculating image scale width and height */ 2668 $xscale = $orig_w / $new_w; 2669 $yscale = $orig_h / $new_h; 2670 2671 /* Resizing by biggest side */ 2672 2673 if ($resizeByBiggerSide) { 2674 2675 if ($orig_w > $orig_h) { 2676 $size_h = $orig_h * $width / $orig_w; 2677 $size_w = $width; 2678 } else { 2679 $size_w = $orig_w * $height / $orig_h; 2680 $size_h = $height; 2681 } 2682 2683 } else { 2684 if ($orig_w > $orig_h) { 2685 $size_w = $orig_w * $height / $orig_h; 2686 $size_h = $height; 2687 } else { 2688 $size_h = $orig_h * $width / $orig_w; 2689 $size_w = $width; 2690 } 2691 } 2692 } 2693 2694 switch ($this->imgLib) { 2695 case 'imagick': 2696 2697 try { 2698 $img = new imagick($path); 2699 } catch (Exception $e) { 2700 2701 return false; 2702 } 2703 2704 $img->resizeImage($size_w, $size_h, Imagick::FILTER_LANCZOS, true); 2705 2706 $result = $img->writeImage($path); 2707 2708 return $result ? $path : false; 2709 2710 break; 2711 2712 case 'gd': 2713 if ($s['mime'] == 'image/jpeg') { 2714 $img = imagecreatefromjpeg($path); 2715 } elseif ($s['mime'] == 'image/png') { 2716 $img = imagecreatefrompng($path); 2717 } elseif ($s['mime'] == 'image/gif') { 2718 $img = imagecreatefromgif($path); 2719 } elseif ($s['mime'] == 'image/xbm') { 2720 $img = imagecreatefromxbm($path); 2721 } 2722 2723 if ($img && false != ($tmp = imagecreatetruecolor($size_w, $size_h))) { 2724 if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) { 2725 return false; 2726 } 2727 2728 if ($destformat == 'jpg' || ($destformat == null && $s['mime'] == 'image/jpeg')) { 2729 $result = imagejpeg($tmp, $path, 100); 2730 } else if ($destformat == 'gif' || ($destformat == null && $s['mime'] == 'image/gif')) { 2731 $result = imagegif($tmp, $path, 7); 2732 } else { 2733 $result = imagepng($tmp, $path, 7); 2734 } 2735 2736 imagedestroy($img); 2737 imagedestroy($tmp); 2738 2739 return $result ? $path : false; 2740 2741 } 2742 break; 2743 } 2744 2745 return false; 2746 } 2747 2748 /** 2749 * Crop image 2750 * 2751 * @param string $path image file 2752 * @param int $width crop width 2753 * @param int $height crop height 2754 * @param bool $x crop left offset 2755 * @param bool $y crop top offset 2756 * @param string $destformat image destination format 2757 * @return string|false 2758 * @author Dmitry (dio) Levashov 2759 * @author Alexey Sukhotin 2760 **/ 2761 protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) { 2762 if (($s = @getimagesize($path)) == false) { 2763 return false; 2764 } 2765 2766 $result = false; 2767 2768 switch ($this->imgLib) { 2769 case 'imagick': 2770 2771 try { 2772 $img = new imagick($path); 2773 } catch (Exception $e) { 2774 2775 return false; 2776 } 2777 2778 $img->cropImage($width, $height, $x, $y); 2779 2780 $result = $img->writeImage($path); 2781 2782 return $result ? $path : false; 2783 2784 break; 2785 2786 case 'gd': 2787 if ($s['mime'] == 'image/jpeg') { 2788 $img = imagecreatefromjpeg($path); 2789 } elseif ($s['mime'] == 'image/png') { 2790 $img = imagecreatefrompng($path); 2791 } elseif ($s['mime'] == 'image/gif') { 2792 $img = imagecreatefromgif($path); 2793 } elseif ($s['mime'] == 'image/xbm') { 2794 $img = imagecreatefromxbm($path); 2795 } 2796 2797 if ($img && false != ($tmp = imagecreatetruecolor($width, $height))) { 2798 2799 if (!imagecopy($tmp, $img, 0, 0, $x, $y, $width, $height)) { 2800 return false; 2801 } 2802 2803 if ($destformat == 'jpg' || ($destformat == null && $s['mime'] == 'image/jpeg')) { 2804 $result = imagejpeg($tmp, $path, 100); 2805 } else if ($destformat == 'gif' || ($destformat == null && $s['mime'] == 'image/gif')) { 2806 $result = imagegif($tmp, $path, 7); 2807 } else { 2808 $result = imagepng($tmp, $path, 7); 2809 } 2810 2811 imagedestroy($img); 2812 imagedestroy($tmp); 2813 2814 return $result ? $path : false; 2815 2816 } 2817 break; 2818 } 2819 2820 return false; 2821 } 2822 2823 /** 2824 * Put image to square 2825 * 2826 * @param string $path image file 2827 * @param int $width square width 2828 * @param int $height square height 2829 * @param int $align reserved 2830 * @param int $valign reserved 2831 * @param string $bgcolor square background color in #rrggbb format 2832 * @param string $destformat image destination format 2833 * @return string|false 2834 * @author Dmitry (dio) Levashov 2835 * @author Alexey Sukhotin 2836 **/ 2837 protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null) { 2838 if (($s = @getimagesize($path)) == false) { 2839 return false; 2840 } 2841 2842 $result = false; 2843 2844 /* Coordinates for image over square aligning */ 2845 $y = ceil(abs($height - $s[1]) / 2); 2846 $x = ceil(abs($width - $s[0]) / 2); 2847 2848 switch ($this->imgLib) { 2849 case 'imagick': 2850 try { 2851 $img = new imagick($path); 2852 } catch (Exception $e) { 2853 return false; 2854 } 2855 2856 $img1 = new Imagick(); 2857 $img1->newImage($width, $height, new ImagickPixel($bgcolor)); 2858 $img1->setImageColorspace($img->getImageColorspace()); 2859 $img1->setImageFormat($destformat != null ? $destformat : $img->getFormat()); 2860 $img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y ); 2861 $result = $img1->writeImage($path); 2862 return $result ? $path : false; 2863 2864 break; 2865 2866 case 'gd': 2867 if ($s['mime'] == 'image/jpeg') { 2868 $img = imagecreatefromjpeg($path); 2869 } elseif ($s['mime'] == 'image/png') { 2870 $img = imagecreatefrompng($path); 2871 } elseif ($s['mime'] == 'image/gif') { 2872 $img = imagecreatefromgif($path); 2873 } elseif ($s['mime'] == 'image/xbm') { 2874 $img = imagecreatefromxbm($path); 2875 } 2876 2877 if ($img && false != ($tmp = imagecreatetruecolor($width, $height))) { 2878 2879 if ($bgcolor == 'transparent') { 2880 list($r, $g, $b) = array(0, 0, 255); 2881 } else { 2882 list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x"); 2883 } 2884 2885 $bgcolor1 = imagecolorallocate($tmp, $r, $g, $b); 2886 2887 if ($bgcolor == 'transparent') { 2888 $bgcolor1 = imagecolortransparent($tmp, $bgcolor1); 2889 } 2890 2891 imagefill($tmp, 0, 0, $bgcolor1); 2892 2893 if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) { 2894 return false; 2895 } 2896 2897 if ($destformat == 'jpg' || ($destformat == null && $s['mime'] == 'image/jpeg')) { 2898 $result = imagejpeg($tmp, $path, 100); 2899 } else if ($destformat == 'gif' || ($destformat == null && $s['mime'] == 'image/gif')) { 2900 $result = imagegif($tmp, $path, 7); 2901 } else { 2902 $result = imagepng($tmp, $path, 7); 2903 } 2904 2905 imagedestroy($img); 2906 imagedestroy($tmp); 2907 2908 return $result ? $path : false; 2909 } 2910 break; 2911 } 2912 2913 return false; 2914 } 2915 2916 /** 2917 * Rotate image 2918 * 2919 * @param string $path image file 2920 * @param int $degree rotete degrees 2921 * @param string $bgcolor square background color in #rrggbb format 2922 * @param string $destformat image destination format 2923 * @return string|false 2924 * @author nao-pon 2925 * @author Troex Nevelin 2926 **/ 2927 protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) { 2928 if (($s = @getimagesize($path)) == false) { 2929 return false; 2930 } 2931 2932 $result = false; 2933 2934 switch ($this->imgLib) { 2935 case 'imagick': 2936 try { 2937 $img = new imagick($path); 2938 } catch (Exception $e) { 2939 return false; 2940 } 2941 2942 $img->rotateImage(new ImagickPixel($bgcolor), $degree); 2943 $result = $img->writeImage($path); 2944 return $result ? $path : false; 2945 2946 break; 2947 2948 case 'gd': 2949 if ($s['mime'] == 'image/jpeg') { 2950 $img = imagecreatefromjpeg($path); 2951 } elseif ($s['mime'] == 'image/png') { 2952 $img = imagecreatefrompng($path); 2953 } elseif ($s['mime'] == 'image/gif') { 2954 $img = imagecreatefromgif($path); 2955 } elseif ($s['mime'] == 'image/xbm') { 2956 $img = imagecreatefromxbm($path); 2957 } 2958 2959 $degree = 360 - $degree; 2960 list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x"); 2961 $bgcolor = imagecolorallocate($img, $r, $g, $b); 2962 $tmp = imageRotate($img, $degree, (int)$bgcolor); 2963 2964 if ($destformat == 'jpg' || ($destformat == null && $s['mime'] == 'image/jpeg')) { 2965 $result = imagejpeg($tmp, $path, 100); 2966 } else if ($destformat == 'gif' || ($destformat == null && $s['mime'] == 'image/gif')) { 2967 $result = imagegif($tmp, $path, 7); 2968 } else { 2969 $result = imagepng($tmp, $path, 7); 2970 } 2971 2972 imageDestroy($img); 2973 imageDestroy($tmp); 2974 2975 return $result ? $path : false; 2976 2977 break; 2978 } 2979 2980 return false; 2981 } 2982 2983 /** 2984 * Execute shell command 2985 * 2986 * @param string $command command line 2987 * @param array $output stdout strings 2988 * @param array $return_var process exit code 2989 * @param array $error_output stderr strings 2990 * @return int exit code 2991 * @author Alexey Sukhotin 2992 **/ 2993 protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) { 2994 2995 $descriptorspec = array( 2996 0 => array("pipe", "r"), // stdin 2997 1 => array("pipe", "w"), // stdout 2998 2 => array("pipe", "w") // stderr 2999 ); 3000 3001 $process = proc_open($command, $descriptorspec, $pipes, null, null); 3002 3003 if (is_resource($process)) { 3004 3005 fclose($pipes[0]); 3006 3007 $tmpout = ''; 3008 $tmperr = ''; 3009 3010 $output = stream_get_contents($pipes[1]); 3011 $error_output = stream_get_contents($pipes[2]); 3012 3013 fclose($pipes[1]); 3014 fclose($pipes[2]); 3015 $return_var = proc_close($process); 3016 3017 3018 } 3019 3020 return $return_var; 3021 3022 } 3023 3024 /** 3025 * Remove thumbnail 3026 * 3027 * @param string $path file path 3028 * @return void 3029 * @author Dmitry (dio) Levashov 3030 **/ 3031 protected function rmTmb($tmb) { 3032 $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$tmb; 3033 file_exists($tmb) && @unlink($tmb); 3034 clearstatcache(); 3035 } 3036 3037 /*********************** misc *************************/ 3038 3039 /** 3040 * Return smart formatted date 3041 * 3042 * @param int $ts file timestamp 3043 * @return string 3044 * @author Dmitry (dio) Levashov 3045 **/ 3046 protected function formatDate($ts) { 3047 if ($ts > $this->today) { 3048 return 'Today '.date($this->options['timeFormat'], $ts); 3049 } 3050 3051 if ($ts > $this->yesterday) { 3052 return 'Yesterday '.date($this->options['timeFormat'], $ts); 3053 } 3054 3055 return date($this->options['dateFormat'], $ts); 3056 } 3057 3058 /** 3059 * Find position of first occurrence of string in a string with multibyte support 3060 * 3061 * @param string $haystack The string being checked. 3062 * @param string $needle The string to find in haystack. 3063 * @param int $offset The search offset. If it is not specified, 0 is used. 3064 * @return int|bool 3065 * @author Alexey Sukhotin 3066 **/ 3067 protected function stripos($haystack , $needle , $offset = 0) { 3068 if (function_exists('mb_stripos')) { 3069 return mb_stripos($haystack , $needle , $offset); 3070 } else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) { 3071 return mb_strpos(mb_strtolower($haystack), mb_strtolower($needle), $offset); 3072 } 3073 return stripos($haystack , $needle , $offset); 3074 } 3075 3076 /**==================================* abstract methods *====================================**/ 3077 3078 /*********************** paths/urls *************************/ 3079 3080 /** 3081 * Return parent directory path 3082 * 3083 * @param string $path file path 3084 * @return string 3085 * @author Dmitry (dio) Levashov 3086 **/ 3087 abstract protected function _dirname($path); 3088 3089 /** 3090 * Return file name 3091 * 3092 * @param string $path file path 3093 * @return string 3094 * @author Dmitry (dio) Levashov 3095 **/ 3096 abstract protected function _basename($path); 3097 3098 /** 3099 * Join dir name and file name and return full path. 3100 * Some drivers (db) use int as path - so we give to concat path to driver itself 3101 * 3102 * @param string $dir dir path 3103 * @param string $name file name 3104 * @return string 3105 * @author Dmitry (dio) Levashov 3106 **/ 3107 abstract protected function _joinPath($dir, $name); 3108 3109 /** 3110 * Return normalized path 3111 * 3112 * @param string $path file path 3113 * @return string 3114 * @author Dmitry (dio) Levashov 3115 **/ 3116 abstract protected function _normpath($path); 3117 3118 /** 3119 * Return file path related to root dir 3120 * 3121 * @param string $path file path 3122 * @return string 3123 * @author Dmitry (dio) Levashov 3124 **/ 3125 abstract protected function _relpath($path); 3126 3127 /** 3128 * Convert path related to root dir into real path 3129 * 3130 * @param string $path rel file path 3131 * @return string 3132 * @author Dmitry (dio) Levashov 3133 **/ 3134 abstract protected function _abspath($path); 3135 3136 /** 3137 * Return fake path started from root dir. 3138 * Required to show path on client side. 3139 * 3140 * @param string $path file path 3141 * @return string 3142 * @author Dmitry (dio) Levashov 3143 **/ 3144 abstract protected function _path($path); 3145 3146 /** 3147 * Return true if $path is children of $parent 3148 * 3149 * @param string $path path to check 3150 * @param string $parent parent path 3151 * @return bool 3152 * @author Dmitry (dio) Levashov 3153 **/ 3154 abstract protected function _inpath($path, $parent); 3155 3156 /** 3157 * Return stat for given path. 3158 * Stat contains following fields: 3159 * - (int) size file size in b. required 3160 * - (int) ts file modification time in unix time. required 3161 * - (string) mime mimetype. required for folders, others - optionally 3162 * - (bool) read read permissions. required 3163 * - (bool) write write permissions. required 3164 * - (bool) locked is object locked. optionally 3165 * - (bool) hidden is object hidden. optionally 3166 * - (string) alias for symlinks - link target path relative to root path. optionally 3167 * - (string) target for symlinks - link target path. optionally 3168 * 3169 * If file does not exists - returns empty array or false. 3170 * 3171 * @param string $path file path 3172 * @return array|false 3173 * @author Dmitry (dio) Levashov 3174 **/ 3175 abstract protected function _stat($path); 3176 3177 3178 /***************** file stat ********************/ 3179 3180 3181 /** 3182 * Return true if path is dir and has at least one childs directory 3183 * 3184 * @param string $path dir path 3185 * @return bool 3186 * @author Dmitry (dio) Levashov 3187 **/ 3188 abstract protected function _subdirs($path); 3189 3190 /** 3191 * Return object width and height 3192 * Ususaly used for images, but can be realize for video etc... 3193 * 3194 * @param string $path file path 3195 * @param string $mime file mime type 3196 * @return string 3197 * @author Dmitry (dio) Levashov 3198 **/ 3199 abstract protected function _dimensions($path, $mime); 3200 3201 /******************** file/dir content *********************/ 3202 3203 /** 3204 * Return files list in directory 3205 * 3206 * @param string $path dir path 3207 * @return array 3208 * @author Dmitry (dio) Levashov 3209 **/ 3210 abstract protected function _scandir($path); 3211 3212 /** 3213 * Open file and return file pointer 3214 * 3215 * @param string $path file path 3216 * @param bool $write open file for writing 3217 * @return resource|false 3218 * @author Dmitry (dio) Levashov 3219 **/ 3220 abstract protected function _fopen($path, $mode="rb"); 3221 3222 /** 3223 * Close opened file 3224 * 3225 * @param resource $fp file pointer 3226 * @param string $path file path 3227 * @return bool 3228 * @author Dmitry (dio) Levashov 3229 **/ 3230 abstract protected function _fclose($fp, $path=''); 3231 3232 /******************** file/dir manipulations *************************/ 3233 3234 /** 3235 * Create dir and return created dir path or false on failed 3236 * 3237 * @param string $path parent dir path 3238 * @param string $name new directory name 3239 * @return string|bool 3240 * @author Dmitry (dio) Levashov 3241 **/ 3242 abstract protected function _mkdir($path, $name); 3243 3244 /** 3245 * Create file and return it's path or false on failed 3246 * 3247 * @param string $path parent dir path 3248 * @param string $name new file name 3249 * @return string|bool 3250 * @author Dmitry (dio) Levashov 3251 **/ 3252 abstract protected function _mkfile($path, $name); 3253 3254 /** 3255 * Create symlink 3256 * 3257 * @param string $source file to link to 3258 * @param string $targetDir folder to create link in 3259 * @param string $name symlink name 3260 * @return bool 3261 * @author Dmitry (dio) Levashov 3262 **/ 3263 abstract protected function _symlink($source, $targetDir, $name); 3264 3265 /** 3266 * Copy file into another file (only inside one volume) 3267 * 3268 * @param string $source source file path 3269 * @param string $target target dir path 3270 * @param string $name file name 3271 * @return bool 3272 * @author Dmitry (dio) Levashov 3273 **/ 3274 abstract protected function _copy($source, $targetDir, $name); 3275 3276 /** 3277 * Move file into another parent dir. 3278 * Return new file path or false. 3279 * 3280 * @param string $source source file path 3281 * @param string $target target dir path 3282 * @param string $name file name 3283 * @return string|bool 3284 * @author Dmitry (dio) Levashov 3285 **/ 3286 abstract protected function _move($source, $targetDir, $name); 3287 3288 /** 3289 * Remove file 3290 * 3291 * @param string $path file path 3292 * @return bool 3293 * @author Dmitry (dio) Levashov 3294 **/ 3295 abstract protected function _unlink($path); 3296 3297 /** 3298 * Remove dir 3299 * 3300 * @param string $path dir path 3301 * @return bool 3302 * @author Dmitry (dio) Levashov 3303 **/ 3304 abstract protected function _rmdir($path); 3305 3306 /** 3307 * Create new file and write into it from file pointer. 3308 * Return new file path or false on error. 3309 * 3310 * @param resource $fp file pointer 3311 * @param string $dir target dir path 3312 * @param string $name file name 3313 * @return bool|string 3314 * @author Dmitry (dio) Levashov 3315 **/ 3316 abstract protected function _save($fp, $dir, $name, $mime, $w, $h); 3317 3318 /** 3319 * Get file contents 3320 * 3321 * @param string $path file path 3322 * @return string|false 3323 * @author Dmitry (dio) Levashov 3324 **/ 3325 abstract protected function _getContents($path); 3326 3327 /** 3328 * Write a string to a file 3329 * 3330 * @param string $path file path 3331 * @param string $content new file content 3332 * @return bool 3333 * @author Dmitry (dio) Levashov 3334 **/ 3335 abstract protected function _filePutContents($path, $content); 3336 3337 /** 3338 * Extract files from archive 3339 * 3340 * @param string $path file path 3341 * @param array $arc archiver options 3342 * @return bool 3343 * @author Dmitry (dio) Levashov, 3344 * @author Alexey Sukhotin 3345 **/ 3346 abstract protected function _extract($path, $arc); 3347 3348 /** 3349 * Create archive and return its path 3350 * 3351 * @param string $dir target dir 3352 * @param array $files files names list 3353 * @param string $name archive name 3354 * @param array $arc archiver options 3355 * @return string|bool 3356 * @author Dmitry (dio) Levashov, 3357 * @author Alexey Sukhotin 3358 **/ 3359 abstract protected function _archive($dir, $files, $name, $arc); 3360 3361 /** 3362 * Detect available archivers 3363 * 3364 * @return void 3365 * @author Dmitry (dio) Levashov, 3366 * @author Alexey Sukhotin 3367 **/ 3368 abstract protected function _checkArchivers(); 3369 3370 } // END class