This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
0 ignored issues
–
show
|
|||
2 | defined('PH7') or exit('Restricted access'); |
||
3 | if (!\PH7\Admin::auth()) exit('Restricted access'); // Accessible only for admins |
||
4 | |||
5 | /** |
||
6 | * Base class for elFinder volume. |
||
7 | * Provide 2 layers: |
||
8 | * 1. Public API (commands) |
||
9 | * 2. abstract fs API |
||
10 | * |
||
11 | * All abstract methods begin with "_" |
||
12 | * |
||
13 | * @author Dmitry (dio) Levashov |
||
14 | * @author Troex Nevelin |
||
15 | * @author Alexey Sukhotin |
||
16 | **/ |
||
17 | abstract class elFinderVolumeDriver { |
||
18 | |||
19 | /** |
||
20 | * Net mount key |
||
21 | * |
||
22 | * @var string |
||
23 | **/ |
||
24 | public $netMountKey = ''; |
||
25 | |||
26 | /** |
||
27 | * Request args |
||
28 | * $_POST or $_GET values |
||
29 | * |
||
30 | * @var array |
||
31 | */ |
||
32 | protected $ARGS = array(); |
||
33 | |||
34 | /** |
||
35 | * Driver id |
||
36 | * Must be started from letter and contains [a-z0-9] |
||
37 | * Used as part of volume id |
||
38 | * |
||
39 | * @var string |
||
40 | **/ |
||
41 | protected $driverId = 'a'; |
||
42 | |||
43 | /** |
||
44 | * Volume id - used as prefix for files hashes |
||
45 | * |
||
46 | * @var string |
||
47 | **/ |
||
48 | protected $id = ''; |
||
49 | |||
50 | /** |
||
51 | * Flag - volume "mounted" and available |
||
52 | * |
||
53 | * @var bool |
||
54 | **/ |
||
55 | protected $mounted = false; |
||
56 | |||
57 | /** |
||
58 | * Root directory path |
||
59 | * |
||
60 | * @var string |
||
61 | **/ |
||
62 | protected $root = ''; |
||
63 | |||
64 | /** |
||
65 | * Root basename | alias |
||
66 | * |
||
67 | * @var string |
||
68 | **/ |
||
69 | protected $rootName = ''; |
||
70 | |||
71 | /** |
||
72 | * Default directory to open |
||
73 | * |
||
74 | * @var string |
||
75 | **/ |
||
76 | protected $startPath = ''; |
||
77 | |||
78 | /** |
||
79 | * Base URL |
||
80 | * |
||
81 | * @var string |
||
82 | **/ |
||
83 | protected $URL = ''; |
||
84 | |||
85 | /** |
||
86 | * Thumbnails dir path |
||
87 | * |
||
88 | * @var string |
||
89 | **/ |
||
90 | protected $tmbPath = ''; |
||
91 | |||
92 | /** |
||
93 | * Is thumbnails dir writable |
||
94 | * |
||
95 | * @var bool |
||
96 | **/ |
||
97 | protected $tmbPathWritable = false; |
||
98 | |||
99 | /** |
||
100 | * Thumbnails base URL |
||
101 | * |
||
102 | * @var string |
||
103 | **/ |
||
104 | protected $tmbURL = ''; |
||
105 | |||
106 | /** |
||
107 | * Thumbnails size in px |
||
108 | * |
||
109 | * @var int |
||
110 | **/ |
||
111 | protected $tmbSize = 48; |
||
112 | |||
113 | /** |
||
114 | * Image manipulation lib name |
||
115 | * auto|imagick|gd|convert |
||
116 | * |
||
117 | * @var string |
||
118 | **/ |
||
119 | protected $imgLib = 'auto'; |
||
120 | |||
121 | /** |
||
122 | * Video to Image converter |
||
123 | * |
||
124 | * @var array |
||
125 | */ |
||
126 | protected $imgConverter = array(); |
||
127 | |||
128 | /** |
||
129 | * Library to crypt files name |
||
130 | * |
||
131 | * @var string |
||
132 | **/ |
||
133 | protected $cryptLib = ''; |
||
134 | |||
135 | /** |
||
136 | * Archivers config |
||
137 | * |
||
138 | * @var array |
||
139 | **/ |
||
140 | protected $archivers = array( |
||
141 | 'create' => array(), |
||
142 | 'extract' => array() |
||
143 | ); |
||
144 | |||
145 | /** |
||
146 | * Server character encoding |
||
147 | * |
||
148 | * @var string or null |
||
149 | **/ |
||
150 | protected $encoding = null; |
||
151 | |||
152 | /** |
||
153 | * How many subdirs levels return for tree |
||
154 | * |
||
155 | * @var int |
||
156 | **/ |
||
157 | protected $treeDeep = 1; |
||
158 | |||
159 | /** |
||
160 | * Errors from last failed action |
||
161 | * |
||
162 | * @var array |
||
163 | **/ |
||
164 | protected $error = array(); |
||
165 | |||
166 | /** |
||
167 | * Today 24:00 timestamp |
||
168 | * |
||
169 | * @var int |
||
170 | **/ |
||
171 | protected $today = 0; |
||
172 | |||
173 | /** |
||
174 | * Yesterday 24:00 timestamp |
||
175 | * |
||
176 | * @var int |
||
177 | **/ |
||
178 | protected $yesterday = 0; |
||
179 | |||
180 | /** |
||
181 | * Force make dirctory on extract |
||
182 | * |
||
183 | * @var int |
||
184 | **/ |
||
185 | protected $extractToNewdir = 'auto'; |
||
186 | |||
187 | /** |
||
188 | * Object configuration |
||
189 | * |
||
190 | * @var array |
||
191 | **/ |
||
192 | protected $options = array( |
||
193 | 'id' => '', |
||
194 | // revision id of root directory that uses for caching control of root stat |
||
195 | 'rootRev' => '', |
||
196 | // driver type it uses volume root's CSS class name. e.g. 'group' -> Adds 'elfinder-group' to CSS class name. |
||
197 | 'type' => '', |
||
198 | // root directory path |
||
199 | 'path' => '', |
||
200 | // Folder hash value on elFinder to be the parent of this volume |
||
201 | 'phash' => '', |
||
202 | // open this path on initial request instead of root path |
||
203 | 'startPath' => '', |
||
204 | // how many subdirs levels return per request |
||
205 | 'treeDeep' => 1, |
||
206 | // root url, not set to disable sending URL to client (replacement for old "fileURL" option) |
||
207 | 'URL' => '', |
||
208 | // directory link url to own manager url with folder hash (`true`, `false` or default `'auto'`: URL is empty then `true` else `false`) |
||
209 | 'dirUrlOwn' => 'auto', |
||
210 | // directory separator. required by client to show paths correctly |
||
211 | 'separator' => DIRECTORY_SEPARATOR, |
||
212 | // Server character encoding (default is '': UTF-8) |
||
213 | 'encoding' => '', |
||
214 | // for convert character encoding (default is '': Not change locale) |
||
215 | 'locale' => '', |
||
216 | // URL of volume icon (16x16 pixel image file) |
||
217 | 'icon' => '', |
||
218 | // CSS Class of volume root in tree |
||
219 | 'rootCssClass' => '', |
||
220 | // enable i18n folder name that convert name to elFinderInstance.messages['folder_'+name] |
||
221 | 'i18nFolderName' => false, |
||
222 | // Search timeout (sec) |
||
223 | 'searchTimeout' => 30, |
||
224 | // Search exclusion directory regex pattern (require demiliter e.g. '#/path/to/exclude_directory#i') |
||
225 | 'searchExDirReg' => '', |
||
226 | // library to crypt/uncrypt files names (not implemented) |
||
227 | 'cryptLib' => '', |
||
228 | // how to detect files mimetypes. (auto/internal/finfo/mime_content_type) |
||
229 | 'mimeDetect' => 'auto', |
||
230 | // mime.types file path (for mimeDetect==internal) |
||
231 | 'mimefile' => '', |
||
232 | // mime type normalize map : Array '[ext]:[detected mime type]' => '[normalized mime]' |
||
233 | 'mimeMap' => array( |
||
234 | 'md:application/x-genesis-rom' => 'text/x-markdown', |
||
235 | 'md:text/plain' => 'text/x-markdown', |
||
236 | 'markdown:text/plain' => 'text/x-markdown', |
||
237 | 'css:text/x-asm' => 'text/css', |
||
238 | 'ico:image/vnd.microsoft.icon' => 'image/x-icon', |
||
239 | 'csv:text/plain' => 'text/csv', |
||
240 | 'm4a:video/mp4' => 'audio/mp4', |
||
241 | 'oga:application/ogg' => 'audio/ogg', |
||
242 | 'ogv:application/ogg' => 'video/ogg' |
||
243 | ), |
||
244 | // MIME regex of send HTTP header "Content-Disposition: inline" |
||
245 | // '.' is allow inline of all of MIME types |
||
246 | // '$^' is not allow inline of all of MIME types |
||
247 | 'dispInlineRegex' => '^(?:(?:image|text)|application/x-shockwave-flash$)', |
||
248 | // directory for thumbnails |
||
249 | 'tmbPath' => '.tmb', |
||
250 | // mode to create thumbnails dir |
||
251 | 'tmbPathMode' => 0777, |
||
252 | // thumbnails dir URL. Set it if store thumbnails outside root directory |
||
253 | 'tmbURL' => '', |
||
254 | // thumbnails size (px) |
||
255 | 'tmbSize' => 48, |
||
256 | // thumbnails crop (true - crop, false - scale image to fit thumbnail size) |
||
257 | 'tmbCrop' => true, |
||
258 | // thumbnails background color (hex #rrggbb or 'transparent') |
||
259 | 'tmbBgColor' => 'transparent', |
||
260 | // image rotate fallback background color (hex #rrggbb) |
||
261 | 'bgColorFb' => '#ffffff', |
||
262 | // image manipulations library |
||
263 | 'imgLib' => 'auto', |
||
264 | // Fallback self image to thumbnail (nothing imgLib) |
||
265 | 'tmbFbSelf' => true, |
||
266 | // Video to Image converters ['TYPE or MIME' => ['func' => function($file){ /* Converts $file to Image */ return true; }, 'maxlen' => (int)TransferLength]] |
||
267 | 'imgConverter' => array(), |
||
268 | // Max length of transfer to image converter |
||
269 | 'tmbVideoConvLen' => 10000000, |
||
270 | // Captre point seccond |
||
271 | 'tmbVideoConvSec' => 6, |
||
272 | // Resource path of fallback icon images defailt: php/resouces |
||
273 | 'resourcePath' => '', |
||
274 | // Jpeg image saveing quality |
||
275 | 'jpgQuality' => 100, |
||
276 | // on paste file - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext |
||
277 | 'copyOverwrite' => true, |
||
278 | // if true - join new and old directories content on paste |
||
279 | 'copyJoin' => true, |
||
280 | // on upload - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext |
||
281 | 'uploadOverwrite' => true, |
||
282 | // mimetypes allowed to upload |
||
283 | 'uploadAllow' => array(), |
||
284 | // mimetypes not allowed to upload |
||
285 | 'uploadDeny' => array(), |
||
286 | // order to proccess uploadAllow and uploadDeny options |
||
287 | 'uploadOrder' => array('deny', 'allow'), |
||
288 | // maximum upload file size. NOTE - this is size for every uploaded files |
||
289 | 'uploadMaxSize' => 0, |
||
290 | // maximum number of chunked upload connection. `-1` to disable chunked upload |
||
291 | 'uploadMaxConn' => 3, |
||
292 | // files dates format |
||
293 | 'dateFormat' => 'j M Y H:i', |
||
294 | // files time format |
||
295 | 'timeFormat' => 'H:i', |
||
296 | // if true - every folder will be check for children folders, otherwise all folders will be marked as having subfolders |
||
297 | 'checkSubfolders' => true, |
||
298 | // allow to copy from this volume to other ones? |
||
299 | 'copyFrom' => true, |
||
300 | // allow to copy from other volumes to this one? |
||
301 | 'copyTo' => true, |
||
302 | // cmd duplicate suffix format e.g. '_%s_' to without spaces |
||
303 | 'duplicateSuffix' => ' %s ', |
||
304 | // unique name numbar format e.g. '(%d)' to (1), (2)... |
||
305 | 'uniqueNumFormat' => '%d', |
||
306 | // list of commands disabled on this root |
||
307 | 'disabled' => array(), |
||
308 | // enable file owner, group & mode info, `false` to inactivate "chmod" command. |
||
309 | 'statOwner' => false, |
||
310 | // allow exec chmod of read-only files |
||
311 | 'allowChmodReadOnly' => false, |
||
312 | // regexp or function name to validate new file name |
||
313 | 'acceptedName' => '/^[^\.].*/', //<-- DONT touch this! Use constructor options to overwrite it! |
||
314 | // function/class method to control files permissions |
||
315 | 'accessControl' => null, |
||
316 | // some data required by access control |
||
317 | 'accessControlData' => null, |
||
318 | // default permissions. |
||
319 | 'defaults' => array( |
||
320 | 'read' => true, |
||
321 | 'write' => true, |
||
322 | 'locked' => false, |
||
323 | 'hidden' => false |
||
324 | ), |
||
325 | // files attributes |
||
326 | 'attributes' => array(), |
||
327 | // max allowed archive files size (0 - no limit) |
||
328 | 'maxArcFilesSize' => 0, |
||
329 | // Allowed archive's mimetypes to create. Leave empty for all available types. |
||
330 | 'archiveMimes' => array(), |
||
331 | // Manual config for archivers. See example below. Leave empty for auto detect |
||
332 | 'archivers' => array(), |
||
333 | // plugin settings |
||
334 | 'plugin' => array(), |
||
335 | // Is support parent directory time stamp update on add|remove|rename item |
||
336 | // Default `null` is auto detection that is LocalFileSystem, FTP or Dropbox are `true` |
||
337 | 'syncChkAsTs' => null, |
||
338 | // Long pooling sync checker function for syncChkAsTs is true |
||
339 | // Calls with args (TARGET DIRCTORY PATH, STAND-BY(sec), OLD TIMESTAMP, VOLUME DRIVER INSTANCE, ELFINDER INSTANCE) |
||
340 | // This function must return the following values. Changed: New Timestamp or Same: Old Timestamp or Error: false |
||
341 | // Default `null` is try use elFinderVolumeLocalFileSystem::localFileSystemInotify() on LocalFileSystem driver |
||
342 | // another driver use elFinder stat() checker |
||
343 | 'syncCheckFunc'=> null, |
||
344 | // Long polling sync stand-by time (sec) |
||
345 | 'plStandby' => 30, |
||
346 | // Sleep time (sec) for elFinder stat() checker (syncChkAsTs is true) |
||
347 | 'tsPlSleep' => 10, |
||
348 | // Sleep time (sec) for elFinder ls() checker (syncChkAsTs is false) |
||
349 | 'lsPlSleep' => 30, |
||
350 | // Client side sync interval minimum (ms) |
||
351 | // Default `null` is auto set to ('tsPlSleep' or 'lsPlSleep') * 1000 |
||
352 | // `0` to disable auto sync |
||
353 | 'syncMinMs' => null, |
||
354 | // required to fix bug on macos |
||
355 | 'utf8fix' => false, |
||
356 | // й ё Й Ё Ø Å |
||
357 | 'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"), |
||
358 | 'utf8replace' => array("\u0439", "\u0451", "\u0419", "\u0401", "\u00d8", "\u00c5") |
||
359 | ); |
||
360 | |||
361 | /** |
||
362 | * Defaults permissions |
||
363 | * |
||
364 | * @var array |
||
365 | **/ |
||
366 | protected $defaults = array( |
||
367 | 'read' => true, |
||
368 | 'write' => true, |
||
369 | 'locked' => false, |
||
370 | 'hidden' => false |
||
371 | ); |
||
372 | |||
373 | /** |
||
374 | * Access control function/class |
||
375 | * |
||
376 | * @var mixed |
||
377 | **/ |
||
378 | protected $attributes = array(); |
||
379 | |||
380 | /** |
||
381 | * Access control function/class |
||
382 | * |
||
383 | * @var mixed |
||
384 | **/ |
||
385 | protected $access = null; |
||
386 | |||
387 | /** |
||
388 | * Mime types allowed to upload |
||
389 | * |
||
390 | * @var array |
||
391 | **/ |
||
392 | protected $uploadAllow = array(); |
||
393 | |||
394 | /** |
||
395 | * Mime types denied to upload |
||
396 | * |
||
397 | * @var array |
||
398 | **/ |
||
399 | protected $uploadDeny = array(); |
||
400 | |||
401 | /** |
||
402 | * Order to validate uploadAllow and uploadDeny |
||
403 | * |
||
404 | * @var array |
||
405 | **/ |
||
406 | protected $uploadOrder = array(); |
||
407 | |||
408 | /** |
||
409 | * Maximum allowed upload file size. |
||
410 | * Set as number or string with unit - "10M", "500K", "1G" |
||
411 | * |
||
412 | * @var int|string |
||
413 | **/ |
||
414 | protected $uploadMaxSize = 0; |
||
415 | |||
416 | /** |
||
417 | * Mimetype detect method |
||
418 | * |
||
419 | * @var string |
||
420 | **/ |
||
421 | protected $mimeDetect = 'auto'; |
||
422 | |||
423 | /** |
||
424 | * Flag - mimetypes from externail file was loaded |
||
425 | * |
||
426 | * @var bool |
||
427 | **/ |
||
428 | private static $mimetypesLoaded = false; |
||
429 | |||
430 | /** |
||
431 | * Finfo object for mimeDetect == 'finfo' |
||
432 | * |
||
433 | * @var object |
||
434 | **/ |
||
435 | protected $finfo = null; |
||
436 | |||
437 | /** |
||
438 | * List of disabled client's commands |
||
439 | * |
||
440 | * @var array |
||
441 | **/ |
||
442 | protected $disabled = array(); |
||
443 | |||
444 | /** |
||
445 | * default extensions/mimetypes for mimeDetect == 'internal' |
||
446 | * |
||
447 | * @var array |
||
448 | **/ |
||
449 | protected static $mimetypes = array( |
||
450 | // applications |
||
451 | 'ai' => 'application/postscript', |
||
452 | 'eps' => 'application/postscript', |
||
453 | 'exe' => 'application/x-executable', |
||
454 | 'doc' => 'application/msword', |
||
455 | 'dot' => 'application/msword', |
||
456 | 'xls' => 'application/vnd.ms-excel', |
||
457 | 'xlt' => 'application/vnd.ms-excel', |
||
458 | 'xla' => 'application/vnd.ms-excel', |
||
459 | 'ppt' => 'application/vnd.ms-powerpoint', |
||
460 | 'pps' => 'application/vnd.ms-powerpoint', |
||
461 | 'pdf' => 'application/pdf', |
||
462 | 'xml' => 'application/xml', |
||
463 | 'swf' => 'application/x-shockwave-flash', |
||
464 | 'torrent' => 'application/x-bittorrent', |
||
465 | 'jar' => 'application/x-jar', |
||
466 | // open office (finfo detect as application/zip) |
||
467 | 'odt' => 'application/vnd.oasis.opendocument.text', |
||
468 | 'ott' => 'application/vnd.oasis.opendocument.text-template', |
||
469 | 'oth' => 'application/vnd.oasis.opendocument.text-web', |
||
470 | 'odm' => 'application/vnd.oasis.opendocument.text-master', |
||
471 | 'odg' => 'application/vnd.oasis.opendocument.graphics', |
||
472 | 'otg' => 'application/vnd.oasis.opendocument.graphics-template', |
||
473 | 'odp' => 'application/vnd.oasis.opendocument.presentation', |
||
474 | 'otp' => 'application/vnd.oasis.opendocument.presentation-template', |
||
475 | 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', |
||
476 | 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', |
||
477 | 'odc' => 'application/vnd.oasis.opendocument.chart', |
||
478 | 'odf' => 'application/vnd.oasis.opendocument.formula', |
||
479 | 'odb' => 'application/vnd.oasis.opendocument.database', |
||
480 | 'odi' => 'application/vnd.oasis.opendocument.image', |
||
481 | 'oxt' => 'application/vnd.openofficeorg.extension', |
||
482 | // MS office 2007 (finfo detect as application/zip) |
||
483 | 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', |
||
484 | 'docm' => 'application/vnd.ms-word.document.macroEnabled.12', |
||
485 | 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', |
||
486 | 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', |
||
487 | 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', |
||
488 | 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', |
||
489 | 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', |
||
490 | 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', |
||
491 | 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', |
||
492 | 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', |
||
493 | 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', |
||
494 | 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', |
||
495 | 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', |
||
496 | 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', |
||
497 | 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', |
||
498 | 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', |
||
499 | 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', |
||
500 | 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', |
||
501 | 'sldm' => 'application/vnd.ms-powerpoint.slide.macroEnabled.12', |
||
502 | // archives |
||
503 | 'gz' => 'application/x-gzip', |
||
504 | 'tgz' => 'application/x-gzip', |
||
505 | 'bz' => 'application/x-bzip2', |
||
506 | 'bz2' => 'application/x-bzip2', |
||
507 | 'tbz' => 'application/x-bzip2', |
||
508 | 'xz' => 'application/x-xz', |
||
509 | 'zip' => 'application/zip', |
||
510 | 'rar' => 'application/x-rar', |
||
511 | 'tar' => 'application/x-tar', |
||
512 | '7z' => 'application/x-7z-compressed', |
||
513 | // texts |
||
514 | 'txt' => 'text/plain', |
||
515 | 'php' => 'text/x-php', |
||
516 | 'html' => 'text/html', |
||
517 | 'htm' => 'text/html', |
||
518 | 'js' => 'text/javascript', |
||
519 | 'css' => 'text/css', |
||
520 | 'rtf' => 'text/rtf', |
||
521 | 'rtfd' => 'text/rtfd', |
||
522 | 'py' => 'text/x-python', |
||
523 | 'java' => 'text/x-java-source', |
||
524 | 'rb' => 'text/x-ruby', |
||
525 | 'sh' => 'text/x-shellscript', |
||
526 | 'pl' => 'text/x-perl', |
||
527 | //'xml' => 'text/xml', |
||
528 | 'sql' => 'text/x-sql', |
||
529 | 'c' => 'text/x-csrc', |
||
530 | 'h' => 'text/x-chdr', |
||
531 | 'cpp' => 'text/x-c++src', |
||
532 | 'hh' => 'text/x-c++hdr', |
||
533 | 'log' => 'text/plain', |
||
534 | 'csv' => 'text/csv', |
||
535 | 'md' => 'text/x-markdown', |
||
536 | 'markdown' => 'text/x-markdown', |
||
537 | // images |
||
538 | 'bmp' => 'image/x-ms-bmp', |
||
539 | 'jpg' => 'image/jpeg', |
||
540 | 'jpeg' => 'image/jpeg', |
||
541 | 'gif' => 'image/gif', |
||
542 | 'png' => 'image/png', |
||
543 | 'tif' => 'image/tiff', |
||
544 | 'tiff' => 'image/tiff', |
||
545 | 'tga' => 'image/x-targa', |
||
546 | 'psd' => 'image/vnd.adobe.photoshop', |
||
547 | //'ai' => 'image/vnd.adobe.photoshop', |
||
548 | 'xbm' => 'image/xbm', |
||
549 | 'pxm' => 'image/pxm', |
||
550 | //audio |
||
551 | 'mp3' => 'audio/mpeg', |
||
552 | 'mid' => 'audio/midi', |
||
553 | 'ogg' => 'audio/ogg', |
||
554 | 'oga' => 'audio/ogg', |
||
555 | 'm4a' => 'audio/mp4', |
||
556 | 'wav' => 'audio/wav', |
||
557 | 'wma' => 'audio/x-ms-wma', |
||
558 | // video |
||
559 | 'avi' => 'video/x-msvideo', |
||
560 | 'dv' => 'video/x-dv', |
||
561 | 'mp4' => 'video/mp4', |
||
562 | 'mpeg' => 'video/mpeg', |
||
563 | 'mpg' => 'video/mpeg', |
||
564 | 'mov' => 'video/quicktime', |
||
565 | 'wm' => 'video/x-ms-wmv', |
||
566 | 'flv' => 'video/x-flv', |
||
567 | 'mkv' => 'video/x-matroska', |
||
568 | 'webm' => 'video/webm', |
||
569 | 'ogv' => 'video/ogg', |
||
570 | 'ogm' => 'video/ogg' |
||
571 | ); |
||
572 | |||
573 | /** |
||
574 | * Directory separator - required by client |
||
575 | * |
||
576 | * @var string |
||
577 | **/ |
||
578 | protected $separator = DIRECTORY_SEPARATOR; |
||
579 | |||
580 | /** |
||
581 | * System Root path (Unix like: '/', Windows: '\', 'C:\' or 'D:\'...) |
||
582 | * |
||
583 | * @var string |
||
584 | **/ |
||
585 | protected $systemRoot = DIRECTORY_SEPARATOR; |
||
586 | |||
587 | /** |
||
588 | * Mimetypes allowed to display |
||
589 | * |
||
590 | * @var array |
||
591 | **/ |
||
592 | protected $onlyMimes = array(); |
||
593 | |||
594 | /** |
||
595 | * Store files moved or overwrited files info |
||
596 | * |
||
597 | * @var array |
||
598 | **/ |
||
599 | protected $removed = array(); |
||
600 | |||
601 | /** |
||
602 | * Store files added files info |
||
603 | * |
||
604 | * @var array |
||
605 | **/ |
||
606 | protected $added = array(); |
||
607 | |||
608 | /** |
||
609 | * Cache storage |
||
610 | * |
||
611 | * @var array |
||
612 | **/ |
||
613 | protected $cache = array(); |
||
614 | |||
615 | /** |
||
616 | * Cache by folders |
||
617 | * |
||
618 | * @var array |
||
619 | **/ |
||
620 | protected $dirsCache = array(); |
||
621 | |||
622 | /** |
||
623 | * Cache for subdirsCE() |
||
624 | * |
||
625 | * @var array |
||
626 | */ |
||
627 | protected $subdirsCache = array(); |
||
628 | |||
629 | /** |
||
630 | * This volume session cache |
||
631 | * |
||
632 | * @var array |
||
633 | */ |
||
634 | protected $sessionCache; |
||
635 | |||
636 | /** |
||
637 | * elFinder session wrapper object |
||
638 | * |
||
639 | * @var elFinderSessionInterface |
||
640 | */ |
||
641 | protected $session; |
||
642 | |||
643 | /** |
||
644 | * Search start time |
||
645 | * |
||
646 | * @var int |
||
647 | */ |
||
648 | protected $searchStart; |
||
649 | |||
650 | /** |
||
651 | * Current query word on doSearch |
||
652 | * |
||
653 | * @var string |
||
654 | **/ |
||
655 | protected $doSearchCurrentQuery = array(); |
||
656 | |||
657 | /** |
||
658 | * Is root modified (for clear root stat cache) |
||
659 | * |
||
660 | * @var bool |
||
661 | */ |
||
662 | protected $rootModified = false; |
||
663 | |||
664 | /*********************************************************************/ |
||
665 | /* INITIALIZATION */ |
||
666 | /*********************************************************************/ |
||
667 | |||
668 | /** |
||
669 | * Prepare driver before mount volume. |
||
670 | * Return true if volume is ready. |
||
671 | * |
||
672 | * @return bool |
||
673 | * @author Dmitry (dio) Levashov |
||
674 | **/ |
||
675 | protected function init() { |
||
676 | return true; |
||
677 | } |
||
678 | |||
679 | /** |
||
680 | * Configure after successfull mount. |
||
681 | * By default set thumbnails path and image manipulation library. |
||
682 | * |
||
683 | * @return void |
||
684 | * @author Dmitry (dio) Levashov |
||
685 | **/ |
||
686 | protected function configure() { |
||
687 | // set ARGS |
||
688 | $this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST'? $_POST : $_GET; |
||
689 | // set thumbnails path |
||
690 | $path = $this->options['tmbPath']; |
||
691 | if ($path) { |
||
692 | if (!file_exists($path)) { |
||
693 | if (mkdir($path)) { |
||
694 | chmod($path, $this->options['tmbPathMode']); |
||
695 | } else { |
||
696 | $path = ''; |
||
697 | } |
||
698 | } |
||
699 | |||
700 | if (is_dir($path) && is_readable($path)) { |
||
701 | $this->tmbPath = $path; |
||
702 | $this->tmbPathWritable = is_writable($path); |
||
703 | } |
||
704 | } |
||
705 | // set resouce path |
||
706 | if (! is_dir($this->options['resourcePath'])) { |
||
707 | $this->options['resourcePath'] = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'resources'; |
||
708 | } |
||
709 | |||
710 | // set image manipulation library |
||
711 | $type = preg_match('/^(imagick|gd|convert|auto)$/i', $this->options['imgLib']) |
||
712 | ? strtolower($this->options['imgLib']) |
||
713 | : 'auto'; |
||
714 | |||
715 | $imgLibFallback = extension_loaded('imagick')? 'imagick' : (function_exists('gd_info')? 'gd' : ''); |
||
716 | if (($type === 'imagick' || $type === 'auto') && extension_loaded('imagick')) { |
||
717 | $this->imgLib = 'imagick'; |
||
718 | } else if (($type === 'gd' || $type === 'auto') && function_exists('gd_info')) { |
||
719 | $this->imgLib = 'gd'; |
||
720 | } else { |
||
721 | $convertCache = 'imgLibConvert'; |
||
722 | if (($convertCmd = $this->session->get($convertCache, false)) !== false) { |
||
723 | $this->imgLib = $convertCmd; |
||
724 | } else { |
||
725 | $this->imgLib = ($this->procExec('convert -version') === 0)? 'convert' : ''; |
||
726 | $this->session->set($convertCache, $this->imgLib); |
||
727 | } |
||
728 | } |
||
729 | if ($type !== 'auto' && $this->imgLib === '') { |
||
730 | // fallback |
||
731 | $this->imgLib = extension_loaded('imagick')? 'imagick' : (function_exists('gd_info')? 'gd' : ''); |
||
732 | } |
||
733 | |||
734 | // check video to img converter |
||
735 | if (! empty($this->options['imgConverter']) && is_array($this->options['imgConverter'])) { |
||
736 | foreach($this->options['imgConverter'] as $_type => $_converter) { |
||
737 | if (isset($_converter['func'])) { |
||
738 | $this->imgConverter[strtolower($_type)] = $_converter; |
||
739 | } |
||
740 | } |
||
741 | } |
||
742 | if (! isset($this->imgConverter['video'])) { |
||
743 | $videoLibCache = 'videoLib'; |
||
744 | if (($videoLibCmd = $this->session->get($videoLibCache, false)) === false) { |
||
745 | $videoLibCmd = ($this->procExec('ffmpeg -version') === 0)? 'ffmpeg' : ''; |
||
746 | $this->session->set($videoLibCache, $videoLibCmd); |
||
747 | } |
||
748 | if ($videoLibCmd) { |
||
749 | $this->imgConverter['video'] = array( |
||
750 | 'func' => array($this, $videoLibCmd . 'ToImg'), |
||
751 | 'maxlen' => $this->options['tmbVideoConvLen'] |
||
752 | ); |
||
753 | } |
||
754 | } |
||
755 | |||
756 | // check archivers |
||
757 | if (empty($this->archivers['create'])) { |
||
758 | $this->disabled[] ='archive'; |
||
759 | } |
||
760 | if (empty($this->archivers['extract'])) { |
||
761 | $this->disabled[] ='extract'; |
||
762 | } |
||
763 | $_arc = $this->getArchivers(); |
||
764 | if (empty($_arc['create'])) { |
||
765 | $this->disabled[] ='zipdl'; |
||
766 | } |
||
767 | |||
768 | // check 'statOwner' for command `chmod` |
||
769 | if (empty($this->options['statOwner'])) { |
||
770 | $this->disabled[] ='chmod'; |
||
771 | } |
||
772 | |||
773 | // check 'mimeMap' |
||
774 | if (!is_array($this->options['mimeMap'])) { |
||
775 | $this->options['mimeMap'] = array(); |
||
776 | } |
||
777 | } |
||
778 | |||
779 | /** |
||
780 | * @deprecated |
||
781 | */ |
||
782 | protected function sessionRestart() { |
||
783 | $this->sessionCache = $this->session->start()->get($this->id, array()); |
||
784 | return true; |
||
785 | } |
||
786 | |||
787 | /*********************************************************************/ |
||
788 | /* PUBLIC API */ |
||
789 | /*********************************************************************/ |
||
790 | |||
791 | /** |
||
792 | * Return driver id. Used as a part of volume id. |
||
793 | * |
||
794 | * @return string |
||
795 | * @author Dmitry (dio) Levashov |
||
796 | **/ |
||
797 | public function driverId() { |
||
798 | return $this->driverId; |
||
799 | } |
||
800 | |||
801 | /** |
||
802 | * Return volume id |
||
803 | * |
||
804 | * @return string |
||
805 | * @author Dmitry (dio) Levashov |
||
806 | **/ |
||
807 | public function id() { |
||
808 | return $this->id; |
||
809 | } |
||
810 | |||
811 | /** |
||
812 | * Assign elFinder session wrapper object |
||
813 | * |
||
814 | * @param $session elFinderSessionInterface |
||
815 | */ |
||
816 | public function setSession($session) { |
||
817 | $this->session = $session; |
||
818 | } |
||
819 | |||
820 | /** |
||
821 | * Return debug info for client |
||
822 | * |
||
823 | * @return array |
||
824 | * @author Dmitry (dio) Levashov |
||
825 | **/ |
||
826 | public function debug() { |
||
827 | return array( |
||
828 | 'id' => $this->id(), |
||
829 | 'name' => strtolower(substr(get_class($this), strlen('elfinderdriver'))), |
||
830 | 'mimeDetect' => $this->mimeDetect, |
||
831 | 'imgLib' => $this->imgLib |
||
832 | ); |
||
833 | } |
||
834 | |||
835 | /** |
||
836 | * chmod a file or folder |
||
837 | * |
||
838 | * @param string $hash file or folder hash to chmod |
||
839 | * @param string $mode octal string representing new permissions |
||
840 | * @return array|false |
||
841 | * @author David Bartle |
||
842 | **/ |
||
843 | public function chmod($hash, $mode) { |
||
844 | if ($this->commandDisabled('chmod')) { |
||
845 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
846 | } |
||
847 | |||
848 | if (!($file = $this->file($hash))) { |
||
849 | return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); |
||
850 | } |
||
851 | |||
852 | if (!$this->options['allowChmodReadOnly']) { |
||
853 | if (!$this->attr($this->decode($hash), 'write', null, ($file['mime'] === 'directory'))) { |
||
854 | return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']); |
||
855 | } |
||
856 | } |
||
857 | |||
858 | $path = $this->decode($hash); |
||
859 | $write = $file['write']; |
||
860 | |||
861 | if ($this->convEncOut(!$this->_chmod($this->convEncIn($path), $mode))) { |
||
862 | return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']); |
||
863 | } |
||
864 | |||
865 | $this->clearcache(); |
||
866 | if ($path == $this->root) { |
||
867 | $this->rootModified = true; |
||
868 | } |
||
869 | |||
870 | if ($file = $this->stat($path)) { |
||
871 | $files = array($file); |
||
872 | if ($file['mime'] === 'directory' && $write !== $file['write']) { |
||
873 | foreach ($this->getScandir($path) as $stat) { |
||
874 | if ($this->mimeAccepted($stat['mime'])) { |
||
875 | $files[] = $stat; |
||
876 | } |
||
877 | } |
||
878 | } |
||
879 | return $files; |
||
880 | } else { |
||
881 | return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); |
||
882 | } |
||
883 | } |
||
884 | |||
885 | /** |
||
886 | * stat a file or folder for elFinder cmd exec |
||
887 | * |
||
888 | * @param string $hash file or folder hash to chmod |
||
889 | * @return array |
||
890 | * @author Naoki Sawada |
||
891 | **/ |
||
892 | public function fstat($hash) { |
||
893 | $path = $this->decode($hash); |
||
894 | return $this->stat($path); |
||
895 | } |
||
896 | |||
897 | |||
898 | public function clearstatcache() { |
||
899 | clearstatcache(); |
||
900 | $this->clearcache(); |
||
901 | } |
||
902 | |||
903 | /** |
||
904 | * "Mount" volume. |
||
905 | * Return true if volume available for read or write, |
||
906 | * false - otherwise |
||
907 | * |
||
908 | * @param array $opts |
||
909 | * @return bool |
||
910 | * @author Dmitry (dio) Levashov |
||
911 | * @author Alexey Sukhotin |
||
912 | */ |
||
913 | public function mount(array $opts) { |
||
914 | $this->options = array_merge($this->options, $opts); |
||
915 | |||
916 | if (!isset($this->options['path']) || $this->options['path'] === '') { |
||
917 | return $this->setError('Path undefined.'); |
||
918 | } |
||
919 | |||
920 | if (! $this->session) { |
||
921 | return $this->setError('Session wrapper dose not set. Need to `$volume->setSession(elFinderSessionInterface);` before mount.'); |
||
922 | } |
||
923 | if (! ($this->session instanceof elFinderSessionInterface)) { |
||
924 | return $this->setError('Session wrapper instance must be "elFinderSessionInterface".'); |
||
925 | } |
||
926 | |||
927 | $this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_'; |
||
928 | $this->root = $this->normpathCE($this->options['path']); |
||
929 | $this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR; |
||
930 | $this->systemRoot = isset($this->options['systemRoot']) ? $this->options['systemRoot'] : $this->separator; |
||
931 | |||
932 | // set server encoding |
||
933 | if (!empty($this->options['encoding']) && strtoupper($this->options['encoding']) !== 'UTF-8') { |
||
934 | $this->encoding = $this->options['encoding']; |
||
935 | } else { |
||
936 | $this->encoding = null; |
||
937 | } |
||
938 | |||
939 | $argInit = !empty($this->ARGS['init']); |
||
940 | |||
941 | // session cache |
||
942 | if ($argInit) { |
||
943 | $this->session->set($this->id, array()); |
||
944 | } |
||
945 | $this->sessionCache = $this->session->get($this->id, array()); |
||
946 | |||
947 | // default file attribute |
||
948 | $this->defaults = array( |
||
949 | 'read' => isset($this->options['defaults']['read']) ? !!$this->options['defaults']['read'] : true, |
||
950 | 'write' => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true, |
||
951 | 'locked' => isset($this->options['defaults']['locked']) ? !!$this->options['defaults']['locked'] : false, |
||
952 | 'hidden' => isset($this->options['defaults']['hidden']) ? !!$this->options['defaults']['hidden'] : false |
||
953 | ); |
||
954 | |||
955 | // root attributes |
||
956 | $this->attributes[] = array( |
||
957 | 'pattern' => '~^'.preg_quote($this->separator).'$~', |
||
958 | 'locked' => true, |
||
959 | 'hidden' => false |
||
960 | ); |
||
961 | // set files attributes |
||
962 | if (!empty($this->options['attributes']) && is_array($this->options['attributes'])) { |
||
963 | |||
964 | foreach ($this->options['attributes'] as $a) { |
||
965 | // attributes must contain pattern and at least one rule |
||
966 | if (!empty($a['pattern']) || count($a) > 1) { |
||
967 | $this->attributes[] = $a; |
||
968 | } |
||
969 | } |
||
970 | } |
||
971 | |||
972 | if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) { |
||
973 | $this->access = $this->options['accessControl']; |
||
974 | } |
||
975 | |||
976 | $this->today = mktime(0,0,0, date('m'), date('d'), date('Y')); |
||
977 | $this->yesterday = $this->today-86400; |
||
978 | |||
979 | // debug($this->attributes); |
||
980 | if (!$this->init()) { |
||
981 | return false; |
||
982 | } |
||
983 | |||
984 | // check some options is arrays |
||
985 | $this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow']) |
||
986 | ? $this->options['uploadAllow'] |
||
987 | : array(); |
||
988 | |||
989 | $this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny']) |
||
990 | ? $this->options['uploadDeny'] |
||
991 | : array(); |
||
992 | |||
993 | $this->options['uiCmdMap'] = (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap'])) |
||
994 | ? $this->options['uiCmdMap'] |
||
995 | : array(); |
||
996 | |||
997 | if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x |
||
998 | $parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow'); |
||
999 | $this->uploadOrder = array(trim($parts[0]), trim($parts[1])); |
||
1000 | } else { // telephat_mode off |
||
1001 | $this->uploadOrder = ! empty($this->options['uploadOrder'])? $this->options['uploadOrder'] : array('deny', 'allow'); |
||
1002 | } |
||
1003 | |||
1004 | if (!empty($this->options['uploadMaxSize'])) { |
||
1005 | $size = ''.$this->options['uploadMaxSize']; |
||
1006 | $unit = strtolower(substr($size, strlen($size) - 1)); |
||
1007 | $n = 1; |
||
1008 | switch ($unit) { |
||
1009 | case 'k': |
||
1010 | $n = 1024; |
||
1011 | break; |
||
1012 | case 'm': |
||
1013 | $n = 1048576; |
||
1014 | break; |
||
1015 | case 'g': |
||
1016 | $n = 1073741824; |
||
1017 | } |
||
1018 | $this->uploadMaxSize = intval($size)*$n; |
||
1019 | } |
||
1020 | // Set maximum to PHP_INT_MAX |
||
1021 | if (!defined('PHP_INT_MAX')) { |
||
1022 | define('PHP_INT_MAX', 2147483647); |
||
1023 | } |
||
1024 | if ($this->uploadMaxSize < 1 || $this->uploadMaxSize > PHP_INT_MAX) { |
||
1025 | $this->uploadMaxSize = PHP_INT_MAX; |
||
1026 | } |
||
1027 | |||
1028 | $this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled']) |
||
1029 | ? array_values(array_diff($this->options['disabled'], array('open'))) // 'open' is required |
||
1030 | : array(); |
||
1031 | |||
1032 | $this->cryptLib = $this->options['cryptLib']; |
||
1033 | $this->mimeDetect = $this->options['mimeDetect']; |
||
1034 | |||
1035 | // find available mimetype detect method |
||
1036 | $type = strtolower($this->options['mimeDetect']); |
||
1037 | $type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto'; |
||
1038 | $regexp = '/text\/x\-(php|c\+\+)/'; |
||
1039 | |||
1040 | if (($type == 'finfo' || $type == 'auto') |
||
1041 | && class_exists('finfo', false)) { |
||
1042 | $tmpFileInfo = explode(';', finfo_file(finfo_open(FILEINFO_MIME), __FILE__)); |
||
1043 | } else { |
||
1044 | $tmpFileInfo = false; |
||
1045 | } |
||
1046 | |||
1047 | $type = 'internal'; |
||
1048 | if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) { |
||
1049 | $type = 'finfo'; |
||
1050 | $this->finfo = finfo_open(FILEINFO_MIME); |
||
1051 | } elseif (($type == 'mime_content_type' || $type == 'auto') && function_exists('mime_content_type')) { |
||
1052 | $_mimetypes = explode(';', mime_content_type(__FILE__)); |
||
1053 | if (preg_match($regexp, array_shift($_mimetypes))) { |
||
1054 | $type = 'mime_content_type'; |
||
1055 | } |
||
1056 | } |
||
1057 | $this->mimeDetect = $type; |
||
1058 | |||
1059 | // load mimes from external file for mimeDetect == 'internal' |
||
1060 | // based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163 |
||
1061 | // file must be in file directory or in parent one |
||
1062 | if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) { |
||
1063 | self::$mimetypesLoaded = true; |
||
1064 | $this->mimeDetect = 'internal'; |
||
1065 | $file = false; |
||
1066 | if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) { |
||
1067 | $file = $this->options['mimefile']; |
||
1068 | } elseif (elFinder::$defaultMimefile && file_exists(elFinder::$defaultMimefile)) { |
||
1069 | $file = elFinder::$defaultMimefile; |
||
1070 | } elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) { |
||
1071 | $file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types'; |
||
1072 | } elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) { |
||
1073 | $file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types'; |
||
1074 | } |
||
1075 | |||
1076 | if ($file && file_exists($file)) { |
||
1077 | $mimecf = file($file); |
||
1078 | |||
1079 | foreach ($mimecf as $line_num => $line) { |
||
1080 | if (!preg_match('/^\s*#/', $line)) { |
||
1081 | $mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY); |
||
1082 | for ($i = 1, $size = count($mime); $i < $size ; $i++) { |
||
1083 | if (!isset(self::$mimetypes[$mime[$i]])) { |
||
1084 | self::$mimetypes[$mime[$i]] = $mime[0]; |
||
1085 | } |
||
1086 | } |
||
1087 | } |
||
1088 | } |
||
1089 | } |
||
1090 | } |
||
1091 | |||
1092 | $this->rootName = empty($this->options['alias']) ? $this->basenameCE($this->root) : $this->options['alias']; |
||
1093 | |||
1094 | // This get's triggered if $this->root == '/' and alias is empty. |
||
1095 | // Maybe modify _basename instead? |
||
1096 | if ($this->rootName === '') $this->rootName = $this->separator; |
||
1097 | |||
1098 | $root = $this->stat($this->root); |
||
1099 | |||
1100 | if (!$root) { |
||
1101 | return $this->setError('Root folder does not exist.'); |
||
1102 | } |
||
1103 | if (!$root['read'] && !$root['write']) { |
||
1104 | return $this->setError('Root folder has not read and write permissions.'); |
||
1105 | } |
||
1106 | |||
1107 | // debug($root); |
||
1108 | |||
1109 | if ($root['read']) { |
||
1110 | // check startPath - path to open by default instead of root |
||
1111 | $startPath = $this->options['startPath']? $this->normpathCE($this->options['startPath']) : ''; |
||
1112 | if ($startPath) { |
||
1113 | $start = $this->stat($startPath); |
||
1114 | if (!empty($start) |
||
1115 | && $start['mime'] == 'directory' |
||
1116 | && $start['read'] |
||
1117 | && empty($start['hidden']) |
||
1118 | && $this->inpathCE($startPath, $this->root)) { |
||
1119 | $this->startPath = $startPath; |
||
1120 | if (substr($this->startPath, -1, 1) == $this->options['separator']) { |
||
1121 | $this->startPath = substr($this->startPath, 0, -1); |
||
1122 | } |
||
1123 | } |
||
1124 | } |
||
1125 | } else { |
||
1126 | $this->options['URL'] = ''; |
||
1127 | $this->options['tmbURL'] = ''; |
||
1128 | $this->options['tmbPath'] = ''; |
||
1129 | // read only volume |
||
1130 | array_unshift($this->attributes, array( |
||
1131 | 'pattern' => '/.*/', |
||
1132 | 'read' => false |
||
1133 | )); |
||
1134 | } |
||
1135 | $this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1; |
||
1136 | $this->tmbSize = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48; |
||
1137 | $this->URL = $this->options['URL']; |
||
1138 | if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) { |
||
1139 | $this->URL .= '/'; |
||
1140 | } |
||
1141 | if (strtolower($this->options['dirUrlOwn']) === 'auto') { |
||
1142 | $this->options['dirUrlOwn'] = $this->URL? false : true; |
||
1143 | } else { |
||
1144 | $this->options['dirUrlOwn'] = (bool)$this->options['dirUrlOwn']; |
||
1145 | } |
||
1146 | |||
1147 | $this->tmbURL = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : ''; |
||
1148 | if ($this->tmbURL && $this->tmbURL !== 'self' && preg_match("|[^/?&=]$|", $this->tmbURL)) { |
||
1149 | $this->tmbURL .= '/'; |
||
1150 | } |
||
1151 | |||
1152 | $this->nameValidator = !empty($this->options['acceptedName']) && (is_string($this->options['acceptedName']) || is_callable($this->options['acceptedName'])) |
||
1153 | ? $this->options['acceptedName'] |
||
1154 | : ''; |
||
1155 | |||
1156 | $this->_checkArchivers(); |
||
1157 | // manual control archive types to create |
||
1158 | if (!empty($this->options['archiveMimes']) && is_array($this->options['archiveMimes'])) { |
||
1159 | foreach ($this->archivers['create'] as $mime => $v) { |
||
1160 | if (!in_array($mime, $this->options['archiveMimes'])) { |
||
1161 | unset($this->archivers['create'][$mime]); |
||
1162 | } |
||
1163 | } |
||
1164 | } |
||
1165 | |||
1166 | // manualy add archivers |
||
1167 | if (!empty($this->options['archivers']['create']) && is_array($this->options['archivers']['create'])) { |
||
1168 | foreach ($this->options['archivers']['create'] as $mime => $conf) { |
||
1169 | if (strpos($mime, 'application/') === 0 |
||
1170 | && !empty($conf['cmd']) |
||
1171 | && isset($conf['argc']) |
||
1172 | && !empty($conf['ext']) |
||
1173 | && !isset($this->archivers['create'][$mime])) { |
||
1174 | $this->archivers['create'][$mime] = $conf; |
||
1175 | } |
||
1176 | } |
||
1177 | } |
||
1178 | |||
1179 | if (!empty($this->options['archivers']['extract']) && is_array($this->options['archivers']['extract'])) { |
||
1180 | foreach ($this->options['archivers']['extract'] as $mime => $conf) { |
||
1181 | if (strpos($mime, 'application/') === 0 |
||
1182 | && !empty($conf['cmd']) |
||
1183 | && isset($conf['argc']) |
||
1184 | && !empty($conf['ext']) |
||
1185 | && !isset($this->archivers['extract'][$mime])) { |
||
1186 | $this->archivers['extract'][$mime] = $conf; |
||
1187 | } |
||
1188 | } |
||
1189 | } |
||
1190 | |||
1191 | $this->configure(); |
||
1192 | |||
1193 | // Normarize disabled (array_merge`for type array of JSON) |
||
1194 | $this->disabled = array_values(array_unique($this->disabled)); |
||
1195 | |||
1196 | // fix sync interval |
||
1197 | if ($this->options['syncMinMs'] !== 0) { |
||
1198 | $this->options['syncMinMs'] = max($this->options[$this->options['syncChkAsTs']? 'tsPlSleep' : 'lsPlSleep'] * 1000, intval($this->options['syncMinMs'])); |
||
1199 | } |
||
1200 | |||
1201 | return $this->mounted = true; |
||
1202 | } |
||
1203 | |||
1204 | /** |
||
1205 | * Some "unmount" stuffs - may be required by virtual fs |
||
1206 | * |
||
1207 | * @return void |
||
1208 | * @author Dmitry (dio) Levashov |
||
1209 | **/ |
||
1210 | public function umount() { |
||
1211 | } |
||
1212 | |||
1213 | /** |
||
1214 | * Return error message from last failed action |
||
1215 | * |
||
1216 | * @return array |
||
1217 | * @author Dmitry (dio) Levashov |
||
1218 | **/ |
||
1219 | public function error() { |
||
1220 | return $this->error; |
||
1221 | } |
||
1222 | |||
1223 | /** |
||
1224 | * Return is uploadable that given file name |
||
1225 | * |
||
1226 | * @param string $name file name |
||
1227 | * @param bool $allowUnknown |
||
1228 | * @return bool |
||
1229 | * @author Naoki Sawada |
||
1230 | **/ |
||
1231 | public function isUploadableByName($name, $allowUnknown = true) { |
||
1232 | $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name); |
||
1233 | return (($allowUnknown && $mimeByName === 'unknown') || $this->allowPutMime($mimeByName)); |
||
1234 | } |
||
1235 | |||
1236 | /** |
||
1237 | * Return Extention/MIME Table (elFinderVolumeDriver::$mimetypes) |
||
1238 | * |
||
1239 | * @return array |
||
1240 | * @author Naoki Sawada |
||
1241 | */ |
||
1242 | public function getMimeTable() { |
||
1243 | // load mime.types |
||
1244 | ! elFinderVolumeDriver::$mimetypesLoaded && elFinderVolumeDriver::mimetypeInternalDetect(); |
||
1245 | return elFinderVolumeDriver::$mimetypes; |
||
1246 | } |
||
1247 | |||
1248 | /** |
||
1249 | * Return file extention detected by MIME type |
||
1250 | * |
||
1251 | * @param string $mime MIME type |
||
1252 | * @param string $suffix Additional suffix |
||
1253 | * @return string |
||
1254 | * @author Naoki Sawada |
||
1255 | */ |
||
1256 | public function getExtentionByMime($mime, $suffix = '') { |
||
1257 | static $extTable = null; |
||
1258 | |||
1259 | if (is_null($extTable)) { |
||
1260 | $extTable = array_flip(array_unique($this->getMimeTable())); |
||
1261 | foreach(array_keys($this->options['mimeMap']) as $pair) { |
||
1262 | list($ext, $_mime) = explode(':', $pair); |
||
1263 | if (! isset($extTable[$_mime])) { |
||
1264 | $extTable[$_mime] = $ext; |
||
1265 | } |
||
1266 | } |
||
1267 | } |
||
1268 | |||
1269 | if ($mime && isset($extTable[$mime])) { |
||
1270 | return $suffix? ($extTable[$mime] . $suffix) : $extTable[$mime]; |
||
1271 | } |
||
1272 | return ''; |
||
1273 | } |
||
1274 | |||
1275 | /** |
||
1276 | * Set mimetypes allowed to display to client |
||
1277 | * |
||
1278 | * @param array $mimes |
||
1279 | * @return void |
||
1280 | * @author Dmitry (dio) Levashov |
||
1281 | **/ |
||
1282 | public function setMimesFilter($mimes) { |
||
1283 | if (is_array($mimes)) { |
||
1284 | $this->onlyMimes = $mimes; |
||
1285 | } |
||
1286 | } |
||
1287 | |||
1288 | /** |
||
1289 | * Return root folder hash |
||
1290 | * |
||
1291 | * @return string |
||
1292 | * @author Dmitry (dio) Levashov |
||
1293 | **/ |
||
1294 | public function root() { |
||
1295 | return $this->encode($this->root); |
||
1296 | } |
||
1297 | |||
1298 | /** |
||
1299 | * Return target path hash |
||
1300 | * |
||
1301 | * @param string $path |
||
1302 | * @param string $name |
||
1303 | * @author Naoki Sawada |
||
1304 | * @return string |
||
1305 | */ |
||
1306 | public function getHash($path, $name = '') { |
||
1307 | if ($name !== '') { |
||
1308 | $path = $this->joinPathCE($path, $name); |
||
1309 | } |
||
1310 | return $this->encode($path); |
||
1311 | } |
||
1312 | |||
1313 | /** |
||
1314 | * Return decoded path of target hash |
||
1315 | * This method do not check the stat of target |
||
1316 | * Use method `realpath()` to do check of the stat of target |
||
1317 | * |
||
1318 | * @param string $hash |
||
1319 | * @author Naoki Sawada |
||
1320 | * @return string |
||
1321 | */ |
||
1322 | public function getPath($hash) { |
||
1323 | return $this->decode($hash); |
||
1324 | } |
||
1325 | |||
1326 | /** |
||
1327 | * Return root or startPath hash |
||
1328 | * |
||
1329 | * @return string |
||
1330 | * @author Dmitry (dio) Levashov |
||
1331 | **/ |
||
1332 | public function defaultPath() { |
||
1333 | return $this->encode($this->startPath ? $this->startPath : $this->root); |
||
1334 | } |
||
1335 | |||
1336 | /** |
||
1337 | * Return volume options required by client: |
||
1338 | * |
||
1339 | * @param $hash |
||
1340 | * @return array |
||
1341 | * @author Dmitry (dio) Levashov |
||
1342 | */ |
||
1343 | public function options($hash) { |
||
1344 | $create = $createext = array(); |
||
1345 | if (isset($this->archivers['create']) && is_array($this->archivers['create'])) { |
||
1346 | foreach($this->archivers['create'] as $m => $v) { |
||
1347 | $create[] = $m; |
||
1348 | $createext[$m] = $v['ext']; |
||
1349 | } |
||
1350 | } |
||
1351 | $opts = array( |
||
1352 | 'path' => $hash? $this->path($hash) : '', |
||
1353 | 'url' => $this->URL, |
||
1354 | 'tmbUrl' => (! $this->imgLib && $this->options['tmbFbSelf'])? 'self' : $this->tmbURL, |
||
1355 | 'disabled' => $this->disabled, |
||
1356 | 'separator' => $this->separator, |
||
1357 | 'copyOverwrite' => intval($this->options['copyOverwrite']), |
||
1358 | 'uploadOverwrite' => intval($this->options['uploadOverwrite']), |
||
1359 | 'uploadMaxSize' => intval($this->uploadMaxSize), |
||
1360 | 'uploadMaxConn' => intval($this->options['uploadMaxConn']), |
||
1361 | 'uploadMime' => array( |
||
1362 | 'firstOrder' => isset($this->uploadOrder[0])? $this->uploadOrder[0] : 'deny', |
||
1363 | 'allow' => $this->uploadAllow, |
||
1364 | 'deny' => $this->uploadDeny |
||
1365 | ), |
||
1366 | 'dispInlineRegex' => $this->options['dispInlineRegex'], |
||
1367 | 'jpgQuality' => intval($this->options['jpgQuality']), |
||
1368 | 'archivers' => array( |
||
1369 | 'create' => $create, |
||
1370 | 'extract' => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array(), |
||
1371 | 'createext' => $createext |
||
1372 | ), |
||
1373 | 'uiCmdMap' => (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array(), |
||
1374 | 'syncChkAsTs' => intval($this->options['syncChkAsTs']), |
||
1375 | 'syncMinMs' => intval($this->options['syncMinMs']), |
||
1376 | 'i18nFolderName' => intval($this->options['i18nFolderName']) |
||
1377 | ); |
||
1378 | if ($hash === null) { |
||
1379 | // call from getRootStatExtra() |
||
1380 | if (! empty($this->options['icon'])) { |
||
1381 | $opts['icon'] = $this->options['icon']; |
||
1382 | } |
||
1383 | if (! empty($this->options['rootCssClass'])) { |
||
1384 | $opts['csscls'] = $this->options['rootCssClass']; |
||
1385 | } |
||
1386 | if (isset($this->options['netkey'])) { |
||
1387 | $opts['netkey'] = $this->options['netkey']; |
||
1388 | } |
||
1389 | } |
||
1390 | return $opts; |
||
1391 | } |
||
1392 | |||
1393 | /** |
||
1394 | * Get option value of this volume |
||
1395 | * |
||
1396 | * @param string $name target option name |
||
1397 | * @return NULL|mixed target option value |
||
1398 | * @author Naoki Sawada |
||
1399 | */ |
||
1400 | public function getOption($name) { |
||
1401 | return isset($this->options[$name])? $this->options[$name] : null; |
||
1402 | } |
||
1403 | |||
1404 | /** |
||
1405 | * Get plugin values of this options |
||
1406 | * |
||
1407 | * @param string $name Plugin name |
||
1408 | * @return NULL|array Plugin values |
||
1409 | * @author Naoki Sawada |
||
1410 | */ |
||
1411 | public function getOptionsPlugin($name = '') { |
||
1412 | if ($name) { |
||
1413 | return isset($this->options['plugin'][$name])? $this->options['plugin'][$name] : array(); |
||
1414 | } else { |
||
1415 | return $this->options['plugin']; |
||
1416 | } |
||
1417 | } |
||
1418 | |||
1419 | /** |
||
1420 | * Return true if command disabled in options |
||
1421 | * |
||
1422 | * @param string $cmd command name |
||
1423 | * @return bool |
||
1424 | * @author Dmitry (dio) Levashov |
||
1425 | **/ |
||
1426 | public function commandDisabled($cmd) { |
||
1427 | return in_array($cmd, $this->disabled); |
||
1428 | } |
||
1429 | |||
1430 | /** |
||
1431 | * Return true if mime is required mimes list |
||
1432 | * |
||
1433 | * @param string $mime mime type to check |
||
1434 | * @param array $mimes allowed mime types list or not set to use client mimes list |
||
1435 | * @param bool|null $empty what to return on empty list |
||
1436 | * @return bool|null |
||
1437 | * @author Dmitry (dio) Levashov |
||
1438 | * @author Troex Nevelin |
||
1439 | **/ |
||
1440 | public function mimeAccepted($mime, $mimes = null, $empty = true) { |
||
1441 | $mimes = is_array($mimes) ? $mimes : $this->onlyMimes; |
||
1442 | if (empty($mimes)) { |
||
1443 | return $empty; |
||
1444 | } |
||
1445 | return $mime == 'directory' |
||
1446 | || in_array('all', $mimes) |
||
1447 | || in_array('All', $mimes) |
||
1448 | || in_array($mime, $mimes) |
||
1449 | || in_array(substr($mime, 0, strpos($mime, '/')), $mimes); |
||
1450 | } |
||
1451 | |||
1452 | /** |
||
1453 | * Return true if voume is readable. |
||
1454 | * |
||
1455 | * @return bool |
||
1456 | * @author Dmitry (dio) Levashov |
||
1457 | **/ |
||
1458 | public function isReadable() { |
||
1459 | $stat = $this->stat($this->root); |
||
1460 | return $stat['read']; |
||
1461 | } |
||
1462 | |||
1463 | /** |
||
1464 | * Return true if copy from this volume allowed |
||
1465 | * |
||
1466 | * @return bool |
||
1467 | * @author Dmitry (dio) Levashov |
||
1468 | **/ |
||
1469 | public function copyFromAllowed() { |
||
1470 | return !!$this->options['copyFrom']; |
||
1471 | } |
||
1472 | |||
1473 | /** |
||
1474 | * Return file path related to root with convert encoging |
||
1475 | * |
||
1476 | * @param string $hash file hash |
||
1477 | * @return string |
||
1478 | * @author Dmitry (dio) Levashov |
||
1479 | **/ |
||
1480 | public function path($hash) { |
||
1481 | return $this->convEncOut($this->_path($this->convEncIn($this->decode($hash)))); |
||
1482 | } |
||
1483 | |||
1484 | /** |
||
1485 | * Return file real path if file exists |
||
1486 | * |
||
1487 | * @param string $hash file hash |
||
1488 | * @return string | false |
||
1489 | * @author Dmitry (dio) Levashov |
||
1490 | **/ |
||
1491 | public function realpath($hash) { |
||
1492 | $path = $this->decode($hash); |
||
1493 | return $this->stat($path) ? $path : false; |
||
1494 | } |
||
1495 | |||
1496 | /** |
||
1497 | * Return list of moved/overwrited files |
||
1498 | * |
||
1499 | * @return array |
||
1500 | * @author Dmitry (dio) Levashov |
||
1501 | **/ |
||
1502 | public function removed() { |
||
1503 | return $this->removed; |
||
1504 | } |
||
1505 | |||
1506 | /** |
||
1507 | * Return list of added files |
||
1508 | * |
||
1509 | * @deprecated |
||
1510 | * @return array |
||
1511 | * @author Naoki Sawada |
||
1512 | **/ |
||
1513 | public function added() { |
||
1514 | return $this->added; |
||
1515 | } |
||
1516 | |||
1517 | /** |
||
1518 | * Clean removed files list |
||
1519 | * |
||
1520 | * @return void |
||
1521 | * @author Dmitry (dio) Levashov |
||
1522 | **/ |
||
1523 | public function resetRemoved() { |
||
1524 | $this->resetResultStat(); |
||
1525 | } |
||
1526 | |||
1527 | /** |
||
1528 | * Clean added/removed files list |
||
1529 | * |
||
1530 | * @return void |
||
1531 | **/ |
||
1532 | public function resetResultStat() { |
||
1533 | $this->removed = array(); |
||
1534 | $this->added = array(); |
||
1535 | } |
||
1536 | |||
1537 | /** |
||
1538 | * Return file/dir hash or first founded child hash with required attr == $val |
||
1539 | * |
||
1540 | * @param string $hash file hash |
||
1541 | * @param string $attr attribute name |
||
1542 | * @param bool $val attribute value |
||
1543 | * @return string|false |
||
1544 | * @author Dmitry (dio) Levashov |
||
1545 | **/ |
||
1546 | public function closest($hash, $attr, $val) { |
||
1547 | return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false; |
||
1548 | } |
||
1549 | |||
1550 | /** |
||
1551 | * Return file info or false on error |
||
1552 | * |
||
1553 | * @param string $hash file hash |
||
1554 | * @return array|false |
||
1555 | * @internal param bool $realpath add realpath field to file info |
||
1556 | * @author Dmitry (dio) Levashov |
||
1557 | */ |
||
1558 | public function file($hash) { |
||
1559 | $path = $this->decode($hash); |
||
1560 | $isRoot = ($path == $this->root); |
||
1561 | |||
1562 | $file = $this->stat($path); |
||
1563 | |||
1564 | if ($isRoot) { |
||
1565 | $file = array_merge($file, $this->getRootStatExtra()); |
||
1566 | } |
||
1567 | |||
1568 | return ($file) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND); |
||
1569 | } |
||
1570 | |||
1571 | /** |
||
1572 | * Return folder info |
||
1573 | * |
||
1574 | * @param string $hash folder hash |
||
1575 | * @param bool $resolveLink |
||
1576 | * @return array|false |
||
1577 | * @internal param bool $hidden return hidden file info |
||
1578 | * @author Dmitry (dio) Levashov |
||
1579 | */ |
||
1580 | public function dir($hash, $resolveLink=false) { |
||
1581 | if (($dir = $this->file($hash)) == false) { |
||
1582 | return $this->setError(elFinder::ERROR_DIR_NOT_FOUND); |
||
1583 | } |
||
1584 | |||
1585 | if ($resolveLink && !empty($dir['thash'])) { |
||
1586 | $dir = $this->file($dir['thash']); |
||
1587 | } |
||
1588 | |||
1589 | return $dir && $dir['mime'] == 'directory' && empty($dir['hidden']) |
||
1590 | ? $dir |
||
1591 | : $this->setError(elFinder::ERROR_NOT_DIR); |
||
1592 | } |
||
1593 | |||
1594 | /** |
||
1595 | * Return directory content or false on error |
||
1596 | * |
||
1597 | * @param string $hash file hash |
||
1598 | * @return array|false |
||
1599 | * @author Dmitry (dio) Levashov |
||
1600 | **/ |
||
1601 | public function scandir($hash) { |
||
1602 | if (($dir = $this->dir($hash)) == false) { |
||
1603 | return false; |
||
1604 | } |
||
1605 | |||
1606 | return $dir['read'] |
||
1607 | ? $this->getScandir($this->decode($hash)) |
||
1608 | : $this->setError(elFinder::ERROR_PERM_DENIED); |
||
1609 | } |
||
1610 | |||
1611 | /** |
||
1612 | * Return dir files names list |
||
1613 | * |
||
1614 | * @param string $hash file hash |
||
1615 | * @param null $intersect |
||
1616 | * @return array |
||
1617 | * @author Dmitry (dio) Levashov |
||
1618 | */ |
||
1619 | public function ls($hash, $intersect = null) { |
||
1620 | if (($dir = $this->dir($hash)) == false || !$dir['read']) { |
||
1621 | return false; |
||
1622 | } |
||
1623 | |||
1624 | $list = array(); |
||
1625 | $path = $this->decode($hash); |
||
1626 | |||
1627 | $check = array(); |
||
1628 | if ($intersect) { |
||
1629 | $check = array_flip($intersect); |
||
1630 | } |
||
1631 | |||
1632 | foreach ($this->getScandir($path) as $stat) { |
||
1633 | if (empty($stat['hidden']) && (!$check || isset($check[$stat['name']])) && $this->mimeAccepted($stat['mime'])) { |
||
1634 | $list[$stat['hash']] = $stat['name']; |
||
1635 | } |
||
1636 | } |
||
1637 | |||
1638 | return $list; |
||
1639 | } |
||
1640 | |||
1641 | /** |
||
1642 | * Return subfolders for required folder or false on error |
||
1643 | * |
||
1644 | * @param string $hash folder hash or empty string to get tree from root folder |
||
1645 | * @param int $deep subdir deep |
||
1646 | * @param string $exclude dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders |
||
1647 | * @return array|false |
||
1648 | * @author Dmitry (dio) Levashov |
||
1649 | **/ |
||
1650 | public function tree($hash='', $deep=0, $exclude='') { |
||
1651 | $path = $hash ? $this->decode($hash) : $this->root; |
||
1652 | |||
1653 | if (($dir = $this->stat($path)) == false || $dir['mime'] != 'directory') { |
||
1654 | return false; |
||
1655 | } |
||
1656 | |||
1657 | $dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $exclude ? $this->decode($exclude) : null); |
||
1658 | array_unshift($dirs, $dir); |
||
1659 | return $dirs; |
||
1660 | } |
||
1661 | |||
1662 | /** |
||
1663 | * Return part of dirs tree from required dir up to root dir |
||
1664 | * |
||
1665 | * @param string $hash directory hash |
||
1666 | * @param bool|null $lineal only lineal parents |
||
1667 | * @return array |
||
1668 | * @author Dmitry (dio) Levashov |
||
1669 | **/ |
||
1670 | public function parents($hash, $lineal = false) { |
||
1671 | if (($current = $this->dir($hash)) == false) { |
||
1672 | return false; |
||
1673 | } |
||
1674 | |||
1675 | $path = $this->decode($hash); |
||
1676 | $tree = array(); |
||
1677 | |||
1678 | while ($path && $path != $this->root) { |
||
1679 | $path = $this->dirnameCE($path); |
||
1680 | if (!($stat = $this->stat($path)) || !empty($stat['hidden']) || !$stat['read']) { |
||
1681 | return false; |
||
1682 | } |
||
1683 | |||
1684 | array_unshift($tree, $stat); |
||
1685 | if (!$lineal) { |
||
1686 | foreach ($this->gettree($path, 0) as $dir) { |
||
1687 | if (!in_array($dir, $tree)) { |
||
1688 | $tree[] = $dir; |
||
1689 | } |
||
1690 | } |
||
1691 | } |
||
1692 | } |
||
1693 | |||
1694 | return $tree ? $tree : array($current); |
||
1695 | } |
||
1696 | |||
1697 | /** |
||
1698 | * Create thumbnail for required file and return its name of false on failed |
||
1699 | * |
||
1700 | * @param $hash |
||
1701 | * @return false|string |
||
1702 | * @author Dmitry (dio) Levashov |
||
1703 | */ |
||
1704 | public function tmb($hash) { |
||
1705 | $path = $this->decode($hash); |
||
1706 | $stat = $this->stat($path); |
||
1707 | |||
1708 | if (isset($stat['tmb'])) { |
||
1709 | $res = $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb']; |
||
1710 | if (! $res) { |
||
1711 | list($type) = explode('/', $stat['mime']); |
||
1712 | $fallback = $this->options['resourcePath'] . DIRECTORY_SEPARATOR . strtolower($type) . '.png'; |
||
1713 | if (is_file($fallback)) { |
||
1714 | $res = $this->tmbname($stat); |
||
1715 | if (! copy($fallback, $this->tmbPath . DIRECTORY_SEPARATOR . $res)) { |
||
1716 | $res = false; |
||
1717 | } |
||
1718 | } |
||
1719 | } |
||
1720 | return $res; |
||
1721 | } |
||
1722 | return false; |
||
1723 | } |
||
1724 | |||
1725 | /** |
||
1726 | * Return file size / total directory size |
||
1727 | * |
||
1728 | * @param string file hash |
||
1729 | * @return int |
||
1730 | * @author Dmitry (dio) Levashov |
||
1731 | **/ |
||
1732 | public function size($hash) { |
||
1733 | return $this->countSize($this->decode($hash)); |
||
1734 | } |
||
1735 | |||
1736 | /** |
||
1737 | * Open file for reading and return file pointer |
||
1738 | * |
||
1739 | * @param string file hash |
||
1740 | * @return Resource |
||
1741 | * @author Dmitry (dio) Levashov |
||
1742 | **/ |
||
1743 | public function open($hash) { |
||
1744 | if (($file = $this->file($hash)) == false |
||
1745 | || $file['mime'] == 'directory') { |
||
1746 | return false; |
||
1747 | } |
||
1748 | |||
1749 | return $this->fopenCE($this->decode($hash), 'rb'); |
||
1750 | } |
||
1751 | |||
1752 | /** |
||
1753 | * Close file pointer |
||
1754 | * |
||
1755 | * @param Resource $fp file pointer |
||
1756 | * @param string $hash file hash |
||
1757 | * @return void |
||
1758 | * @author Dmitry (dio) Levashov |
||
1759 | **/ |
||
1760 | public function close($fp, $hash) { |
||
1761 | $this->fcloseCE($fp, $this->decode($hash)); |
||
1762 | } |
||
1763 | |||
1764 | /** |
||
1765 | * Create directory and return dir info |
||
1766 | * |
||
1767 | * @param string $dsthash destination directory hash |
||
1768 | * @param string $name directory name |
||
1769 | * @return array|false |
||
1770 | * @author Dmitry (dio) Levashov |
||
1771 | **/ |
||
1772 | public function mkdir($dsthash, $name) { |
||
1773 | if ($this->commandDisabled('mkdir')) { |
||
1774 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
1775 | } |
||
1776 | |||
1777 | if (!$this->nameAccepted($name)) { |
||
1778 | return $this->setError(elFinder::ERROR_INVALID_NAME); |
||
1779 | } |
||
1780 | |||
1781 | if (($dir = $this->dir($dsthash)) == false) { |
||
1782 | return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dsthash); |
||
1783 | } |
||
1784 | |||
1785 | $path = $this->decode($dsthash); |
||
1786 | |||
1787 | if (!$dir['write'] || !$this->allowCreate($path, $name, true)) { |
||
1788 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
1789 | } |
||
1790 | |||
1791 | $dst = $this->joinPathCE($path, $name); |
||
1792 | $stat = $this->stat($dst); |
||
1793 | if (!empty($stat)) { |
||
1794 | return $this->setError(elFinder::ERROR_EXISTS, $name); |
||
1795 | } |
||
1796 | $this->clearcache(); |
||
1797 | return ($path = $this->convEncOut($this->_mkdir($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false; |
||
1798 | } |
||
1799 | |||
1800 | /** |
||
1801 | * Create empty file and return its info |
||
1802 | * |
||
1803 | * @param string $dst destination directory |
||
1804 | * @param string $name file name |
||
1805 | * @return array|false |
||
1806 | * @author Dmitry (dio) Levashov |
||
1807 | **/ |
||
1808 | public function mkfile($dst, $name) { |
||
1809 | if ($this->commandDisabled('mkfile')) { |
||
1810 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
1811 | } |
||
1812 | |||
1813 | if (!$this->nameAccepted($name)) { |
||
1814 | return $this->setError(elFinder::ERROR_INVALID_NAME); |
||
1815 | } |
||
1816 | |||
1817 | $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name); |
||
1818 | if ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName)) { |
||
1819 | return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $name); |
||
1820 | } |
||
1821 | |||
1822 | if (($dir = $this->dir($dst)) == false) { |
||
1823 | return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst); |
||
1824 | } |
||
1825 | |||
1826 | $path = $this->decode($dst); |
||
1827 | |||
1828 | if (!$dir['write'] || !$this->allowCreate($path, $name, false)) { |
||
1829 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
1830 | } |
||
1831 | |||
1832 | if ($this->stat($this->joinPathCE($path, $name))) { |
||
1833 | return $this->setError(elFinder::ERROR_EXISTS, $name); |
||
1834 | } |
||
1835 | |||
1836 | $this->clearcache(); |
||
1837 | return ($path = $this->convEncOut($this->_mkfile($this->convEncIn($path), $this->convEncIn($name)))) ? $this->stat($path) : false; |
||
1838 | } |
||
1839 | |||
1840 | /** |
||
1841 | * Rename file and return file info |
||
1842 | * |
||
1843 | * @param string $hash file hash |
||
1844 | * @param string $name new file name |
||
1845 | * @return array|false |
||
1846 | * @author Dmitry (dio) Levashov |
||
1847 | **/ |
||
1848 | public function rename($hash, $name) { |
||
1849 | if ($this->commandDisabled('rename')) { |
||
1850 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
1851 | } |
||
1852 | |||
1853 | if (!$this->nameAccepted($name)) { |
||
1854 | return $this->setError(elFinder::ERROR_INVALID_NAME, $name); |
||
1855 | } |
||
1856 | |||
1857 | $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name); |
||
1858 | if ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName)) { |
||
1859 | return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $name); |
||
1860 | } |
||
1861 | |||
1862 | if (!($file = $this->file($hash))) { |
||
1863 | return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); |
||
1864 | } |
||
1865 | |||
1866 | if ($name === $file['name']) { |
||
1867 | return $file; |
||
1868 | } |
||
1869 | |||
1870 | if (!empty($file['locked'])) { |
||
1871 | return $this->setError(elFinder::ERROR_LOCKED, $file['name']); |
||
1872 | } |
||
1873 | |||
1874 | $path = $this->decode($hash); |
||
1875 | $dir = $this->dirnameCE($path); |
||
1876 | $stat = $this->stat($this->joinPathCE($dir, $name)); |
||
1877 | if ($stat) { |
||
1878 | return $this->setError(elFinder::ERROR_EXISTS, $name); |
||
1879 | } |
||
1880 | |||
1881 | if (!$this->allowCreate($dir, $name, ($file['mime'] === 'directory'))) { |
||
1882 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
1883 | } |
||
1884 | |||
1885 | $this->rmTmb($file); // remove old name tmbs, we cannot do this after dir move |
||
1886 | |||
1887 | |||
1888 | if ($path = $this->convEncOut($this->_move($this->convEncIn($path), $this->convEncIn($dir), $this->convEncIn($name)))) { |
||
1889 | $this->clearcache(); |
||
1890 | return $this->stat($path); |
||
1891 | } |
||
1892 | return false; |
||
1893 | } |
||
1894 | |||
1895 | /** |
||
1896 | * Create file copy with suffix "copy number" and return its info |
||
1897 | * |
||
1898 | * @param string $hash file hash |
||
1899 | * @param string $suffix suffix to add to file name |
||
1900 | * @return array|false |
||
1901 | * @author Dmitry (dio) Levashov |
||
1902 | **/ |
||
1903 | public function duplicate($hash, $suffix='copy') { |
||
1904 | if ($this->commandDisabled('duplicate')) { |
||
1905 | return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED); |
||
1906 | } |
||
1907 | |||
1908 | if (($file = $this->file($hash)) == false) { |
||
1909 | return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND); |
||
1910 | } |
||
1911 | |||
1912 | $path = $this->decode($hash); |
||
1913 | $dir = $this->dirnameCE($path); |
||
1914 | $name = $this->uniqueName($dir, $file['name'], sprintf($this->options['duplicateSuffix'], $suffix)); |
||
1915 | |||
1916 | if (!$this->allowCreate($dir, $name, ($file['mime'] === 'directory'))) { |
||
1917 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
1918 | } |
||
1919 | |||
1920 | return ($path = $this->copy($path, $dir, $name)) == false |
||
1921 | ? false |
||
1922 | : $this->stat($path); |
||
1923 | } |
||
1924 | |||
1925 | /** |
||
1926 | * Save uploaded file. |
||
1927 | * On success return array with new file stat and with removed file hash (if existed file was replaced) |
||
1928 | * |
||
1929 | * @param Resource $fp file pointer |
||
1930 | * @param string $dst destination folder hash |
||
1931 | * @param $name |
||
1932 | * @param string $tmpname file tmp name - required to detect mime type |
||
1933 | * @param array $hashes exists files hash array with filename as key |
||
1934 | * @return array|false |
||
1935 | * @internal param string $src file name |
||
1936 | * @author Dmitry (dio) Levashov |
||
1937 | */ |
||
1938 | public function upload($fp, $dst, $name, $tmpname, $hashes = array()) { |
||
1939 | if ($this->commandDisabled('upload')) { |
||
1940 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
1941 | } |
||
1942 | |||
1943 | if (($dir = $this->dir($dst)) == false) { |
||
1944 | return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst); |
||
1945 | } |
||
1946 | |||
1947 | if (!$dir['write']) { |
||
1948 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
1949 | } |
||
1950 | |||
1951 | if (!$this->nameAccepted($name)) { |
||
1952 | return $this->setError(elFinder::ERROR_INVALID_NAME); |
||
1953 | } |
||
1954 | |||
1955 | $mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpname, $name); |
||
1956 | $mimeByName = ''; |
||
1957 | if ($this->mimeDetect !== 'internal') { |
||
1958 | $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name); |
||
1959 | if ($mime == 'unknown') { |
||
1960 | $mime = $mimeByName; |
||
1961 | } |
||
1962 | } |
||
1963 | |||
1964 | if (!$this->allowPutMime($mime) || ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) { |
||
1965 | return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME); |
||
1966 | } |
||
1967 | |||
1968 | $tmpsize = sprintf('%u', filesize($tmpname)); |
||
1969 | if ($this->uploadMaxSize > 0 && $tmpsize > $this->uploadMaxSize) { |
||
1970 | return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE); |
||
1971 | } |
||
1972 | |||
1973 | $dstpath = $this->decode($dst); |
||
1974 | if (isset($hashes[$name])) { |
||
1975 | $test = $this->decode($hashes[$name]); |
||
1976 | } else { |
||
1977 | $test = $this->joinPathCE($dstpath, $name); |
||
1978 | } |
||
1979 | |||
1980 | $file = $this->stat($test); |
||
1981 | $this->clearcache(); |
||
1982 | |||
1983 | if ($file && $file['name'] === $name) { // file exists and check filename for item ID based filesystem |
||
1984 | // check POST data `overwrite` for 3rd party uploader |
||
1985 | $overwrite = isset($_POST['overwrite'])? (bool)$_POST['overwrite'] : $this->options['uploadOverwrite']; |
||
1986 | if ($overwrite) { |
||
1987 | if (!$file['write']) { |
||
1988 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
1989 | } elseif ($file['mime'] == 'directory') { |
||
1990 | return $this->setError(elFinder::ERROR_NOT_REPLACE, $name); |
||
1991 | } |
||
1992 | $this->remove($test); |
||
1993 | } else { |
||
1994 | $name = $this->uniqueName($dstpath, $name, '-', false); |
||
1995 | } |
||
1996 | } |
||
1997 | |||
1998 | $stat = array( |
||
1999 | 'mime' => $mime, |
||
2000 | 'width' => 0, |
||
2001 | 'height' => 0, |
||
2002 | 'size' => $tmpsize); |
||
2003 | |||
2004 | // $w = $h = 0; |
||
2005 | if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) { |
||
2006 | $stat['width'] = $s[0]; |
||
2007 | $stat['height'] = $s[1]; |
||
2008 | } |
||
2009 | // $this->clearcache(); |
||
2010 | if (($path = $this->saveCE($fp, $dstpath, $name, $stat)) == false) { |
||
2011 | return false; |
||
2012 | } |
||
2013 | |||
2014 | $stat = $this->stat($path); |
||
2015 | // Try get URL |
||
2016 | if (empty($stat['url']) && ($url = $this->getContentUrl($stat['hash']))) { |
||
2017 | $stat['url'] = $url; |
||
2018 | } |
||
2019 | |||
2020 | return $stat; |
||
2021 | } |
||
2022 | |||
2023 | /** |
||
2024 | * Paste files |
||
2025 | * |
||
2026 | * @param Object $volume source volume |
||
2027 | * @param $src |
||
2028 | * @param string $dst destination dir hash |
||
2029 | * @param bool $rmSrc remove source after copy? |
||
2030 | * @param array $hashes |
||
2031 | * @return array|false |
||
2032 | * @internal param string $source file hash |
||
2033 | * @author Dmitry (dio) Levashov |
||
2034 | */ |
||
2035 | public function paste($volume, $src, $dst, $rmSrc = false, $hashes = array()) { |
||
2036 | $err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY; |
||
2037 | |||
2038 | if ($this->commandDisabled('paste')) { |
||
2039 | return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED); |
||
2040 | } |
||
2041 | |||
2042 | if (($file = $volume->file($src, $rmSrc)) == false) { |
||
2043 | return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND); |
||
2044 | } |
||
2045 | |||
2046 | $name = $file['name']; |
||
2047 | $errpath = $volume->path($file['hash']); |
||
2048 | |||
2049 | if (($dir = $this->dir($dst)) == false) { |
||
2050 | return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst); |
||
2051 | } |
||
2052 | |||
2053 | if (!$dir['write'] || !$file['read']) { |
||
2054 | return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED); |
||
2055 | } |
||
2056 | |||
2057 | $destination = $this->decode($dst); |
||
2058 | |||
2059 | if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) { |
||
2060 | return $rmSrc |
||
2061 | ? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test)) |
||
2062 | : $this->setError($err, $errpath, !empty($file['thash'])? elFinder::ERROR_PERM_DENIED : elFinder::ERROR_MKOUTLINK); |
||
2063 | } |
||
2064 | |||
2065 | if (isset($hashes[$name])) { |
||
2066 | $test = $this->decode($hashes[$name]); |
||
2067 | } else { |
||
2068 | $test = $this->joinPathCE($destination, $name); |
||
2069 | } |
||
2070 | $stat = $this->stat($test); |
||
2071 | $this->clearcache(); |
||
2072 | $dstDirExists = false; |
||
2073 | if ($stat && $stat['name'] === $name) { // file exists and check filename for item ID based filesystem |
||
2074 | if ($this->options['copyOverwrite']) { |
||
2075 | // do not replace file with dir or dir with file |
||
2076 | if (!$this->isSameType($file['mime'], $stat['mime'])) { |
||
2077 | return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->path($stat['hash'])); |
||
2078 | } |
||
2079 | // existed file is not writable |
||
2080 | if (!$stat['write']) { |
||
2081 | return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED); |
||
2082 | } |
||
2083 | // existed file locked or has locked child |
||
2084 | if (($locked = $this->closestByAttr($test, 'locked', true))) { |
||
2085 | $stat = $this->stat($locked); |
||
2086 | return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash'])); |
||
2087 | } |
||
2088 | // target is entity file of alias |
||
2089 | if ($volume === $this && ((isset($file['target']) && $test == $file['target']) || $test == $this->decode($src))) { |
||
2090 | return $this->setError(elFinder::ERROR_REPLACE, $errpath); |
||
2091 | } |
||
2092 | // remove existed file |
||
2093 | if (! $this->options['copyJoin'] || $stat['mime'] !== 'directory') { |
||
2094 | if (! $this->remove($test)) { |
||
2095 | return $this->setError(elFinder::ERROR_REPLACE, $this->path($stat['hash'])); |
||
2096 | } |
||
2097 | } else if ($stat['mime'] === 'directory') { |
||
2098 | $dstDirExists = true; |
||
2099 | } |
||
2100 | } else { |
||
2101 | $name = $this->uniqueName($destination, $name, ' ', false); |
||
2102 | } |
||
2103 | } |
||
2104 | |||
2105 | // copy/move inside current volume |
||
2106 | if ($volume === $this) { // changing == operand to === fixes issue #1285 - Paul Canning 24/03/2016 |
||
2107 | $source = $this->decode($src); |
||
2108 | // do not copy into itself |
||
2109 | if ($this->inpathCE($destination, $source)) { |
||
2110 | return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $errpath); |
||
2111 | } |
||
2112 | $rmDir = false; |
||
2113 | if ($rmSrc) { |
||
2114 | if ($dstDirExists) { |
||
2115 | $rmDir = true; |
||
2116 | $method = 'copy'; |
||
2117 | } else { |
||
2118 | $method = 'move'; |
||
2119 | } |
||
2120 | } else { |
||
2121 | $method = 'copy'; |
||
2122 | } |
||
2123 | $this->clearcache(); |
||
2124 | if ($res = ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false) { |
||
2125 | if ($rmDir) { |
||
2126 | $this->remove($source); |
||
2127 | } |
||
2128 | } else { |
||
2129 | return false; |
||
2130 | } |
||
2131 | } else { |
||
2132 | // copy/move from another volume |
||
2133 | if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) { |
||
2134 | return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED); |
||
2135 | } |
||
2136 | |||
2137 | if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) { |
||
2138 | return false; |
||
2139 | } |
||
2140 | |||
2141 | if ($rmSrc) { |
||
2142 | if (!$volume->rm($src)) { |
||
2143 | return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC); |
||
2144 | } |
||
2145 | } |
||
2146 | $res = $this->stat($path); |
||
2147 | } |
||
2148 | return $res; |
||
2149 | } |
||
2150 | |||
2151 | /** |
||
2152 | * Return path to archive of target items |
||
2153 | * |
||
2154 | * @param array $hashes |
||
2155 | * @return string archive path |
||
2156 | * @author Naoki Sawada |
||
2157 | */ |
||
2158 | public function zipdl($hashes) { |
||
2159 | if ($this->commandDisabled('zipdl')) { |
||
2160 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
2161 | } |
||
2162 | |||
2163 | $archivers = $this->getArchivers(); |
||
2164 | $cmd = null; |
||
2165 | if (!$archivers || empty($archivers['create'])) { |
||
2166 | return false; |
||
2167 | } |
||
2168 | $archivers = $archivers['create']; |
||
2169 | foreach(array('zip', 'tgz') as $ext) { |
||
2170 | $mime = self::$mimetypes[$ext]; |
||
2171 | if (isset($archivers[$mime])) { |
||
2172 | $cmd = $archivers[$mime]; |
||
2173 | break; |
||
2174 | } |
||
2175 | } |
||
2176 | if (!$cmd) { |
||
2177 | $cmd = $archivers[0]; |
||
2178 | $ext = $cmd['ext']; |
||
2179 | $mime = elFinderVolumeDriver::mimetypeInternalDetect('file.'.$ext); |
||
2180 | } |
||
2181 | $res = false; |
||
2182 | $mixed = false; |
||
2183 | $hashes = array_values($hashes); |
||
2184 | $dirname = dirname(str_replace($this->separator, DIRECTORY_SEPARATOR, $this->path($hashes[0]))); |
||
2185 | $cnt = count($hashes); |
||
2186 | if ($cnt > 1) { |
||
2187 | for($i = 1; $i < $cnt; $i++) { |
||
2188 | if ($dirname !== dirname(str_replace($this->separator, DIRECTORY_SEPARATOR, $this->path($hashes[$i])))) { |
||
2189 | $mixed = true; |
||
2190 | break; |
||
2191 | } |
||
2192 | } |
||
2193 | } |
||
2194 | if ($mixed || $this->root == $this->dirnameCE($this->decode($hashes[0]))) { |
||
2195 | $prefix = $this->rootName; |
||
2196 | } else { |
||
2197 | $prefix = basename($dirname); |
||
2198 | } |
||
2199 | if ($dir = $this->getItemsInHand($hashes)) { |
||
2200 | $tmppre = (substr(PHP_OS, 0, 3) === 'WIN')? 'zdl' : 'elfzdl'; |
||
2201 | $pdir = dirname($dir); |
||
2202 | // garbage collection |
||
2203 | $ttl = 7200; // expire 2h |
||
2204 | $time = time(); |
||
2205 | foreach(glob($pdir.DIRECTORY_SEPARATOR.$tmppre.'*') as $_file) { |
||
2206 | if (filemtime($_file) + $ttl < $time) { |
||
2207 | unlink($_file); |
||
2208 | } |
||
2209 | } |
||
2210 | $files = self::localScandir($dir); |
||
2211 | if ($files && ($arc = tempnam($dir, $tmppre))) { |
||
2212 | unlink($arc); |
||
2213 | $arc = $arc.'.'.$ext; |
||
2214 | $name = basename($arc); |
||
2215 | if ($arc = $this->makeArchive($dir, $files, $name, $cmd)) { |
||
2216 | $file = tempnam($pdir, $tmppre); |
||
2217 | unlink($file); |
||
2218 | $res = rename($arc, $file); |
||
2219 | $this->rmdirRecursive($dir); |
||
2220 | } |
||
2221 | } |
||
2222 | } |
||
2223 | return $res? array('path' => $file, 'ext' => $ext, 'mime' => $mime, 'prefix' => $prefix) : false; |
||
2224 | } |
||
2225 | |||
2226 | /** |
||
2227 | * Return file contents |
||
2228 | * |
||
2229 | * @param string $hash file hash |
||
2230 | * @return string|false |
||
2231 | * @author Dmitry (dio) Levashov |
||
2232 | **/ |
||
2233 | public function getContents($hash) { |
||
2234 | $file = $this->file($hash); |
||
2235 | |||
2236 | if (!$file) { |
||
2237 | return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); |
||
2238 | } |
||
2239 | |||
2240 | if ($file['mime'] == 'directory') { |
||
2241 | return $this->setError(elFinder::ERROR_NOT_FILE); |
||
2242 | } |
||
2243 | |||
2244 | if (!$file['read']) { |
||
2245 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
2246 | } |
||
2247 | |||
2248 | return $this->convEncOut($this->_getContents($this->convEncIn($this->decode($hash)))); |
||
2249 | } |
||
2250 | |||
2251 | /** |
||
2252 | * Put content in text file and return file info. |
||
2253 | * |
||
2254 | * @param string $hash file hash |
||
2255 | * @param string $content new file content |
||
2256 | * @return array |
||
2257 | * @author Dmitry (dio) Levashov |
||
2258 | **/ |
||
2259 | public function putContents($hash, $content) { |
||
2260 | if ($this->commandDisabled('edit')) { |
||
2261 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
2262 | } |
||
2263 | |||
2264 | $path = $this->decode($hash); |
||
2265 | |||
2266 | if (!($file = $this->file($hash))) { |
||
2267 | return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); |
||
2268 | } |
||
2269 | |||
2270 | if (!$file['write']) { |
||
2271 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
2272 | } |
||
2273 | |||
2274 | // check MIME |
||
2275 | $name = $this->basenameCE($path); |
||
2276 | $mime = ''; |
||
2277 | $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name); |
||
2278 | if ($this->mimeDetect !== 'internal') { |
||
2279 | if ($tp = tmpfile()) { |
||
2280 | fwrite($tp, $content); |
||
2281 | $info = stream_get_meta_data($tp); |
||
2282 | $filepath = $info['uri']; |
||
2283 | $mime = $this->mimetype($filepath, $name); |
||
2284 | fclose($tp); |
||
2285 | } |
||
2286 | } |
||
2287 | if (!$this->allowPutMime($mimeByName) || ($mime && $mime !== 'unknown' && !$this->allowPutMime($mime))) { |
||
2288 | return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME); |
||
2289 | } |
||
2290 | |||
2291 | $this->clearcache(); |
||
2292 | return $this->convEncOut($this->_filePutContents($this->convEncIn($path), $content)) ? $this->stat($path) : false; |
||
2293 | } |
||
2294 | |||
2295 | /** |
||
2296 | * Extract files from archive |
||
2297 | * |
||
2298 | * @param string $hash archive hash |
||
2299 | * @param null $makedir |
||
2300 | * @return array|bool |
||
2301 | * @author Dmitry (dio) Levashov, |
||
2302 | * @author Alexey Sukhotin |
||
2303 | */ |
||
2304 | public function extract($hash, $makedir = null) { |
||
2305 | if ($this->commandDisabled('extract')) { |
||
2306 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
2307 | } |
||
2308 | |||
2309 | if (($file = $this->file($hash)) == false) { |
||
2310 | return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); |
||
2311 | } |
||
2312 | |||
2313 | $archiver = isset($this->archivers['extract'][$file['mime']]) |
||
2314 | ? $this->archivers['extract'][$file['mime']] |
||
2315 | : false; |
||
2316 | |||
2317 | if (!$archiver) { |
||
2318 | return $this->setError(elFinder::ERROR_NOT_ARCHIVE); |
||
2319 | } |
||
2320 | |||
2321 | $path = $this->decode($hash); |
||
2322 | $parent = $this->stat($this->dirnameCE($path)); |
||
2323 | |||
2324 | if (!$file['read'] || !$parent['write']) { |
||
2325 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
2326 | } |
||
2327 | $this->clearcache(); |
||
2328 | $this->extractToNewdir = is_null($makedir)? 'auto' : (bool)$makedir; |
||
2329 | |||
2330 | if ($path = $this->convEncOut($this->_extract($this->convEncIn($path), $archiver))) { |
||
2331 | if (is_array($path)) { |
||
2332 | foreach ($path as $_k => $_p) { |
||
2333 | $path[$_k] = $this->stat($_p); |
||
2334 | } |
||
2335 | } else { |
||
2336 | $path = $this->stat($path); |
||
2337 | } |
||
2338 | return $path; |
||
2339 | } else { |
||
2340 | return false; |
||
2341 | } |
||
2342 | } |
||
2343 | |||
2344 | /** |
||
2345 | * Add files to archive |
||
2346 | * |
||
2347 | * @param $hashes |
||
2348 | * @param $mime |
||
2349 | * @param string $name |
||
2350 | * @return array|bool |
||
2351 | */ |
||
2352 | public function archive($hashes, $mime, $name = '') { |
||
2353 | if ($this->commandDisabled('archive')) { |
||
2354 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
2355 | } |
||
2356 | |||
2357 | if ($name !== '' && !$this->nameAccepted($name)) { |
||
2358 | return $this->setError(elFinder::ERROR_INVALID_NAME); |
||
2359 | } |
||
2360 | |||
2361 | $archiver = isset($this->archivers['create'][$mime]) |
||
2362 | ? $this->archivers['create'][$mime] |
||
2363 | : false; |
||
2364 | |||
2365 | if (!$archiver) { |
||
2366 | return $this->setError(elFinder::ERROR_ARCHIVE_TYPE); |
||
2367 | } |
||
2368 | |||
2369 | $files = array(); |
||
2370 | |||
2371 | foreach ($hashes as $hash) { |
||
2372 | if (($file = $this->file($hash)) == false) { |
||
2373 | return $this->setError(elFinder::ERROR_FILE_NOT_FOUND, '#'+$hash); |
||
2374 | } |
||
2375 | if (!$file['read']) { |
||
2376 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
2377 | } |
||
2378 | $path = $this->decode($hash); |
||
2379 | if (!isset($dir)) { |
||
2380 | $dir = $this->dirnameCE($path); |
||
2381 | $stat = $this->stat($dir); |
||
2382 | if (!$stat['write']) { |
||
2383 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
2384 | } |
||
2385 | } |
||
2386 | |||
2387 | $files[] = $this->basenameCE($path); |
||
2388 | } |
||
2389 | |||
2390 | if ($name === '') { |
||
2391 | $name = count($files) == 1 ? $files[0] : 'Archive'; |
||
2392 | } else { |
||
2393 | $name = str_replace(array('/', '\\'), '_', preg_replace('/\.' . preg_quote($archiver['ext'], '/') . '$/i', '', $name)); |
||
2394 | } |
||
2395 | $name .='.' . $archiver['ext']; |
||
2396 | $name = $this->uniqueName($dir, $name, ''); |
||
2397 | $this->clearcache(); |
||
2398 | return ($path = $this->convEncOut($this->_archive($this->convEncIn($dir), $this->convEncIn($files), $this->convEncIn($name), $archiver))) ? $this->stat($path) : false; |
||
2399 | } |
||
2400 | |||
2401 | /** |
||
2402 | * Resize image |
||
2403 | * |
||
2404 | * @param string $hash image file |
||
2405 | * @param int $width new width |
||
2406 | * @param int $height new height |
||
2407 | * @param int $x X start poistion for crop |
||
2408 | * @param int $y Y start poistion for crop |
||
2409 | * @param string $mode action how to mainpulate image |
||
2410 | * @param string $bg background color |
||
2411 | * @param int $degree rotete degree |
||
2412 | * @param int $jpgQuality JEPG quality (1-100) |
||
2413 | * @return array|false |
||
2414 | * @author Dmitry (dio) Levashov |
||
2415 | * @author Alexey Sukhotin |
||
2416 | * @author nao-pon |
||
2417 | * @author Troex Nevelin |
||
2418 | **/ |
||
2419 | public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0, $jpgQuality = null) { |
||
2420 | if ($this->commandDisabled('resize')) { |
||
2421 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
2422 | } |
||
2423 | |||
2424 | if (($file = $this->file($hash)) == false) { |
||
2425 | return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); |
||
2426 | } |
||
2427 | |||
2428 | if (!$file['write'] || !$file['read']) { |
||
2429 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
2430 | } |
||
2431 | |||
2432 | $path = $this->decode($hash); |
||
2433 | |||
2434 | $work_path = $this->getWorkFile($this->encoding? $this->convEncIn($path, true) : $path); |
||
2435 | |||
2436 | if (!$work_path || !is_writable($work_path)) { |
||
2437 | if ($work_path && $path !== $work_path && is_file($work_path)) { |
||
2438 | unlink($work_path); |
||
2439 | } |
||
2440 | return $this->setError(elFinder::ERROR_PERM_DENIED); |
||
2441 | } |
||
2442 | |||
2443 | if ($this->imgLib !== 'imagick' && $this->imgLib !== 'convert') { |
||
2444 | if (elFinder::isAnimationGif($work_path)) { |
||
2445 | return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE); |
||
2446 | } |
||
2447 | } |
||
2448 | |||
2449 | switch($mode) { |
||
2450 | |||
2451 | case 'propresize': |
||
2452 | $result = $this->imgResize($work_path, $width, $height, true, true, null, $jpgQuality); |
||
2453 | break; |
||
2454 | |||
2455 | case 'crop': |
||
2456 | $result = $this->imgCrop($work_path, $width, $height, $x, $y, null, $jpgQuality); |
||
2457 | break; |
||
2458 | |||
2459 | case 'fitsquare': |
||
2460 | $result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']), null, $jpgQuality); |
||
2461 | break; |
||
2462 | |||
2463 | case 'rotate': |
||
2464 | $result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['bgColorFb']), null, $jpgQuality); |
||
2465 | break; |
||
2466 | |||
2467 | default: |
||
2468 | $result = $this->imgResize($work_path, $width, $height, false, true, null, $jpgQuality); |
||
2469 | break; |
||
2470 | } |
||
2471 | |||
2472 | $ret = false; |
||
2473 | if ($result) { |
||
2474 | $stat = $this->stat($path); |
||
2475 | clearstatcache(); |
||
2476 | $fstat = stat($work_path); |
||
2477 | $stat['size'] = $fstat['size']; |
||
2478 | $stat['ts'] = $fstat['mtime']; |
||
2479 | if ($imgsize = getimagesize($work_path)) { |
||
2480 | $stat['width'] = $imgsize[0]; |
||
2481 | $stat['height'] = $imgsize[1]; |
||
2482 | $stat['mime'] = $imgsize['mime']; |
||
2483 | } |
||
2484 | if ($path !== $work_path) { |
||
2485 | if ($fp = fopen($work_path, 'rb')) { |
||
2486 | $ret = $this->saveCE($fp, $this->dirnameCE($path), $this->basenameCE($path), $stat); |
||
2487 | fclose($fp); |
||
2488 | } |
||
2489 | } else { |
||
2490 | $ret = true; |
||
2491 | } |
||
2492 | if ($ret) { |
||
2493 | $this->rmTmb($file); |
||
2494 | $this->clearcache(); |
||
2495 | $ret = $this->stat($path); |
||
2496 | $ret['width'] = $stat['width']; |
||
2497 | $ret['height'] = $stat['height']; |
||
2498 | } |
||
2499 | } |
||
2500 | if ($path !== $work_path) { |
||
2501 | is_file($work_path) && unlink($work_path); |
||
2502 | } |
||
2503 | |||
2504 | return $ret; |
||
2505 | } |
||
2506 | |||
2507 | /** |
||
2508 | * Remove file/dir |
||
2509 | * |
||
2510 | * @param string $hash file hash |
||
2511 | * @return bool |
||
2512 | * @author Dmitry (dio) Levashov |
||
2513 | **/ |
||
2514 | public function rm($hash) { |
||
2515 | return $this->commandDisabled('rm') |
||
2516 | ? $this->setError(elFinder::ERROR_PERM_DENIED) |
||
2517 | : $this->remove($this->decode($hash)); |
||
2518 | } |
||
2519 | |||
2520 | /** |
||
2521 | * Search files |
||
2522 | * |
||
2523 | * @param string $q search string |
||
2524 | * @param array $mimes |
||
2525 | * @param null $hash |
||
2526 | * @return array |
||
2527 | * @author Dmitry (dio) Levashov |
||
2528 | */ |
||
2529 | public function search($q, $mimes, $hash = null) { |
||
2530 | $dir = null; |
||
2531 | if ($hash) { |
||
2532 | $dir = $this->decode($hash); |
||
2533 | $stat = $this->stat($dir); |
||
2534 | if (!$stat || $stat['mime'] !== 'directory' || !$stat['read']) { |
||
2535 | $q = ''; |
||
2536 | } |
||
2537 | } |
||
2538 | if ($mimes && $this->onlyMimes) { |
||
2539 | $mimes = array_intersect($mimes, $this->onlyMimes); |
||
2540 | if (!$mimes) { |
||
2541 | $q = ''; |
||
2542 | } |
||
2543 | } |
||
2544 | $this->searchStart = time(); |
||
2545 | |||
2546 | $qs = preg_split('/"([^"]+)"| +/', $q, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE); |
||
2547 | $query = $excludes = array(); |
||
2548 | foreach($qs as $_q) { |
||
2549 | $_q = trim($_q); |
||
2550 | if ($_q !== '') { |
||
2551 | if ($_q[0] === '-') { |
||
2552 | if (isset($_q[1])) { |
||
2553 | $excludes[] = substr($_q, 1); |
||
2554 | } |
||
2555 | } else { |
||
2556 | $query[] = $_q; |
||
2557 | } |
||
2558 | } |
||
2559 | } |
||
2560 | if (! $query) { |
||
2561 | $q = ''; |
||
2562 | } else { |
||
2563 | $q = join(' ', $query); |
||
2564 | $this->doSearchCurrentQuery = array( |
||
2565 | 'q' => $q, |
||
2566 | 'excludes' => $excludes |
||
2567 | ); |
||
2568 | } |
||
2569 | |||
2570 | // valided regex $this->options['searchExDirReg'] |
||
2571 | if ($this->options['searchExDirReg']) { |
||
2572 | if (false === preg_match($this->options['searchExDirReg'], '')) { |
||
2573 | $this->options['searchExDirReg'] = ''; |
||
2574 | } |
||
2575 | } |
||
2576 | |||
2577 | return ($q === '' || $this->commandDisabled('search')) |
||
2578 | ? array() |
||
2579 | : $this->doSearch(is_null($dir)? $this->root : $dir, $q, $mimes); |
||
2580 | } |
||
2581 | |||
2582 | /** |
||
2583 | * Return image dimensions |
||
2584 | * |
||
2585 | * @param string $hash file hash |
||
2586 | * @return array |
||
2587 | * @author Dmitry (dio) Levashov |
||
2588 | **/ |
||
2589 | public function dimensions($hash) { |
||
2590 | if (($file = $this->file($hash)) == false) { |
||
2591 | return false; |
||
2592 | } |
||
2593 | |||
2594 | return $this->convEncOut($this->_dimensions($this->convEncIn($this->decode($hash)), $file['mime'])); |
||
2595 | } |
||
2596 | |||
2597 | /** |
||
2598 | * Return content URL (for netmout volume driver) |
||
2599 | * If file.url == 1 requests from JavaScript client with XHR |
||
2600 | * |
||
2601 | * @param string $hash file hash |
||
2602 | * @param array $options options array |
||
2603 | * @return boolean|string |
||
2604 | * @author Naoki Sawada |
||
2605 | */ |
||
2606 | public function getContentUrl($hash, $options = array()) { |
||
2607 | if (($file = $this->file($hash)) == false || (isset($file['url']) && $file['url'] == 1)) { |
||
2608 | return false; |
||
2609 | } |
||
2610 | if (empty($file['url'])) { |
||
2611 | if ($this->URL) { |
||
2612 | $path = str_replace($this->separator, '/', substr($this->decode($hash), strlen($this->root) + 1)); |
||
2613 | if ($this->encoding) { |
||
2614 | $path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true))); |
||
2615 | } |
||
2616 | return $this->URL . $path; |
||
2617 | } |
||
2618 | return false; |
||
2619 | } else { |
||
2620 | return $file['url']; |
||
2621 | } |
||
2622 | } |
||
2623 | |||
2624 | /** |
||
2625 | * Return temp path |
||
2626 | * |
||
2627 | * @return string |
||
2628 | * @author Naoki Sawada |
||
2629 | */ |
||
2630 | public function getTempPath() { |
||
2631 | $tempPath = null; |
||
2632 | if (isset($this->tmpPath) && $this->tmpPath && is_writable($this->tmpPath)) { |
||
2633 | $tempPath = $this->tmpPath; |
||
2634 | } else if (isset($this->tmp) && $this->tmp && is_writable($this->tmp)) { |
||
2635 | $tempPath = $this->tmp; |
||
2636 | } else if (function_exists('sys_get_temp_dir')) { |
||
2637 | $tempPath = sys_get_temp_dir(); |
||
2638 | } else if (isset($this->tmbPath) && $this->tmbPath && is_writable($this->tmbPath)) { |
||
2639 | $tempPath = $this->tmbPath; |
||
2640 | } |
||
2641 | if ($tempPath && DIRECTORY_SEPARATOR !== '/') { |
||
2642 | $tempPath = str_replace('/', DIRECTORY_SEPARATOR, $tempPath); |
||
2643 | } |
||
2644 | return $tempPath; |
||
2645 | } |
||
2646 | |||
2647 | /** |
||
2648 | * (Make &) Get upload taget dirctory hash |
||
2649 | * |
||
2650 | * @param string $baseTargetHash |
||
2651 | * @param string $path |
||
2652 | * @param array $result |
||
2653 | * @return boolean|string |
||
2654 | * @author Naoki Sawada |
||
2655 | */ |
||
2656 | public function getUploadTaget($baseTargetHash, $path, & $result) { |
||
2657 | $base = $this->decode($baseTargetHash); |
||
2658 | $targetHash = $baseTargetHash; |
||
2659 | $path = ltrim($path, $this->separator); |
||
2660 | $dirs = explode($this->separator, $path); |
||
2661 | array_pop($dirs); |
||
2662 | foreach($dirs as $dir) { |
||
2663 | $targetPath = $this->joinPathCE($base, $dir); |
||
2664 | if (! $_realpath = $this->realpath($this->encode($targetPath))) { |
||
2665 | if ($stat = $this->mkdir($targetHash, $dir)) { |
||
2666 | $result['added'][] = $stat; |
||
2667 | $targetHash = $stat['hash']; |
||
2668 | $base = $this->decode($targetHash); |
||
2669 | } else { |
||
2670 | return false; |
||
2671 | } |
||
2672 | } else { |
||
2673 | $targetHash = $this->encode($_realpath); |
||
2674 | if ($this->dir($targetHash)) { |
||
2675 | $base = $this->decode($targetHash); |
||
2676 | } else { |
||
2677 | return false; |
||
2678 | } |
||
2679 | } |
||
2680 | } |
||
2681 | return $targetHash; |
||
2682 | } |
||
2683 | |||
2684 | /** |
||
2685 | * Return this uploadMaxSize value |
||
2686 | * |
||
2687 | * @return integer |
||
2688 | * @author Naoki Sawada |
||
2689 | */ |
||
2690 | public function getUploadMaxSize() { |
||
2691 | return $this->uploadMaxSize; |
||
2692 | } |
||
2693 | |||
2694 | /** |
||
2695 | * Image file utility |
||
2696 | * |
||
2697 | * @param string $mode 'resize', 'rotate', 'propresize', 'crop', 'fitsquare' |
||
2698 | * @param string $src Image file local path |
||
2699 | * @param array $options excute options |
||
2700 | * @return bool |
||
2701 | * @author Naoki Sawada |
||
2702 | */ |
||
2703 | public function imageUtil($mode, $src, $options = array()) { |
||
2704 | if (! isset($options['jpgQuality'])) { |
||
2705 | $options['jpgQuality'] = intval($this->options['jpgQuality']); |
||
2706 | } |
||
2707 | if (! isset($options['bgcolor'])) { |
||
2708 | $options['bgcolor'] = '#ffffff'; |
||
2709 | } |
||
2710 | if (! isset($options['bgColorFb'])) { |
||
2711 | $options['bgColorFb'] = $this->options['bgColorFb']; |
||
2712 | } |
||
2713 | switch($mode) { |
||
2714 | case 'rotate': |
||
2715 | if (empty($options['degree'])) { |
||
2716 | return true; |
||
2717 | } |
||
2718 | return (bool)$this->imgRotate($src, $options['degree'], $options['bgColorFb'], null, $options['jpgQuality']); |
||
2719 | |||
2720 | case 'resize': |
||
2721 | case 'propresize': |
||
2722 | case 'crop': |
||
2723 | case 'fitsquare': |
||
2724 | if (empty($options['width']) || empty($options['height'])) { |
||
2725 | return false; |
||
2726 | } |
||
2727 | |||
2728 | case 'resize': |
||
2729 | return (bool)$this->imgResize($src, $options['width'], $options['height'], false, true, null, $options['jpgQuality'], $options); |
||
2730 | |||
2731 | case 'propresize': |
||
2732 | return (bool)$this->imgResize($src, $options['width'], $options['height'], true, true, null, $options['jpgQuality'], $options); |
||
2733 | |||
2734 | case 'crop': |
||
2735 | if (isset($options['x']) && isset($options['y'])) { |
||
2736 | return (bool)$this->imgCrop($src, $options['width'], $options['height'], $options['x'], $options['y'], null, $options['jpgQuality']); |
||
2737 | } |
||
2738 | |||
2739 | case 'fitsquare': |
||
2740 | return (bool)$this->imgSquareFit($src, $options['width'], $options['height'], 'center', 'middle', $options['bgcolor'], null, $options['jpgQuality']); |
||
2741 | |||
2742 | } |
||
2743 | return false; |
||
2744 | } |
||
2745 | |||
2746 | /** |
||
2747 | * Convert Video To Image by ffmpeg |
||
2748 | * |
||
2749 | * @param $file video source file path |
||
2750 | * @param $stat file stat array |
||
2751 | * @return bool |
||
2752 | * @author Naoki Sawada |
||
2753 | */ |
||
2754 | public function ffmpegToImg($file, $stat) { |
||
2755 | $name = basename($file); |
||
2756 | $path = dirname($file); |
||
2757 | $tmp = $path . DIRECTORY_SEPARATOR . md5($name); |
||
2758 | $GLOBALS['elFinderTempFiles'][] = $tmp; // regist to remove at the end |
||
2759 | if (rename($file, $tmp)) { |
||
2760 | // specific start time by file name (xxx^[sec].[extention] - video^3.mp4) |
||
2761 | if (preg_match('/\^(\d+(?:\.\d+)?)\.[^.]+$/', $stat['name'], $_m)) { |
||
2762 | $ss = $_m[1]; |
||
2763 | } else { |
||
2764 | $ss = $this->options['tmbVideoConvSec']; |
||
2765 | } |
||
2766 | $cmd = sprintf('ffmpeg -ss 00:00:%.3f -vframes 1 -i %s -f image2 %s', $ss, escapeshellarg($tmp), escapeshellarg($file)); |
||
2767 | $r = $this->procExec($cmd); |
||
2768 | unlink($tmp); |
||
2769 | return ($r === 0); |
||
2770 | } |
||
2771 | return false; |
||
2772 | } |
||
2773 | |||
2774 | /** |
||
2775 | * Save error message |
||
2776 | * |
||
2777 | * @param array error |
||
2778 | * @return false |
||
2779 | * @author Dmitry(dio) Levashov |
||
2780 | **/ |
||
2781 | protected function setError($error) { |
||
2782 | |||
2783 | $this->error = array(); |
||
2784 | |||
2785 | foreach (func_get_args() as $err) { |
||
2786 | if (is_array($err)) { |
||
2787 | $this->error = array_merge($this->error, $err); |
||
2788 | } else { |
||
2789 | $this->error[] = $err; |
||
2790 | } |
||
2791 | } |
||
2792 | |||
2793 | // $this->error = is_array($error) ? $error : func_get_args(); |
||
2794 | return false; |
||
2795 | } |
||
2796 | |||
2797 | /*********************************************************************/ |
||
2798 | /* FS API */ |
||
2799 | /*********************************************************************/ |
||
2800 | |||
2801 | /***************** server encoding support *******************/ |
||
2802 | |||
2803 | /** |
||
2804 | * Return parent directory path (with convert encording) |
||
2805 | * |
||
2806 | * @param string $path file path |
||
2807 | * @return string |
||
2808 | * @author Naoki Sawada |
||
2809 | **/ |
||
2810 | protected function dirnameCE($path) { |
||
2811 | return (!$this->encoding)? $this->_dirname($path) : $this->convEncOut($this->_dirname($this->convEncIn($path))); |
||
2812 | } |
||
2813 | |||
2814 | /** |
||
2815 | * Return file name (with convert encording) |
||
2816 | * |
||
2817 | * @param string $path file path |
||
2818 | * @return string |
||
2819 | * @author Naoki Sawada |
||
2820 | **/ |
||
2821 | protected function basenameCE($path) { |
||
2822 | return (!$this->encoding)? $this->_basename($path) : $this->convEncOut($this->_basename($this->convEncIn($path))); |
||
2823 | } |
||
2824 | |||
2825 | /** |
||
2826 | * Join dir name and file name and return full path. (with convert encording) |
||
2827 | * Some drivers (db) use int as path - so we give to concat path to driver itself |
||
2828 | * |
||
2829 | * @param string $dir dir path |
||
2830 | * @param string $name file name |
||
2831 | * @return string |
||
2832 | * @author Naoki Sawada |
||
2833 | **/ |
||
2834 | protected function joinPathCE($dir, $name) { |
||
2835 | return (!$this->encoding)? $this->_joinPath($dir, $name) : $this->convEncOut($this->_joinPath($this->convEncIn($dir), $this->convEncIn($name))); |
||
2836 | } |
||
2837 | |||
2838 | /** |
||
2839 | * Return normalized path (with convert encording) |
||
2840 | * |
||
2841 | * @param string $path file path |
||
2842 | * @return string |
||
2843 | * @author Naoki Sawada |
||
2844 | **/ |
||
2845 | protected function normpathCE($path) { |
||
2846 | return (!$this->encoding)? $this->_normpath($path) : $this->convEncOut($this->_normpath($this->convEncIn($path))); |
||
2847 | } |
||
2848 | |||
2849 | /** |
||
2850 | * Return file path related to root dir (with convert encording) |
||
2851 | * |
||
2852 | * @param string $path file path |
||
2853 | * @return string |
||
2854 | * @author Naoki Sawada |
||
2855 | **/ |
||
2856 | protected function relpathCE($path) { |
||
2857 | return (!$this->encoding)? $this->_relpath($path) : $this->convEncOut($this->_relpath($this->convEncIn($path))); |
||
2858 | } |
||
2859 | |||
2860 | /** |
||
2861 | * Convert path related to root dir into real path (with convert encording) |
||
2862 | * |
||
2863 | * @param string $path rel file path |
||
2864 | * @return string |
||
2865 | * @author Naoki Sawada |
||
2866 | **/ |
||
2867 | protected function abspathCE($path) { |
||
2868 | return (!$this->encoding)? $this->_abspath($path): $this->convEncOut($this->_abspath($this->convEncIn($path))); |
||
2869 | } |
||
2870 | |||
2871 | /** |
||
2872 | * Return true if $path is children of $parent (with convert encording) |
||
2873 | * |
||
2874 | * @param string $path path to check |
||
2875 | * @param string $parent parent path |
||
2876 | * @return bool |
||
2877 | * @author Naoki Sawada |
||
2878 | **/ |
||
2879 | protected function inpathCE($path, $parent) { |
||
2880 | return (!$this->encoding)? $this->_inpath($path, $parent) : $this->convEncOut($this->_inpath($this->convEncIn($path), $this->convEncIn($parent))); |
||
2881 | } |
||
2882 | |||
2883 | /** |
||
2884 | * Open file and return file pointer (with convert encording) |
||
2885 | * |
||
2886 | * @param string $path file path |
||
2887 | * @param string $mode |
||
2888 | * @return false|resource |
||
2889 | * @internal param bool $write open file for writing |
||
2890 | * @author Naoki Sawada |
||
2891 | */ |
||
2892 | protected function fopenCE($path, $mode='rb') { |
||
2893 | return (!$this->encoding)? $this->_fopen($path, $mode) : $this->convEncOut($this->_fopen($this->convEncIn($path), $mode)); |
||
2894 | } |
||
2895 | |||
2896 | /** |
||
2897 | * Close opened file (with convert encording) |
||
2898 | * |
||
2899 | * @param resource $fp file pointer |
||
2900 | * @param string $path file path |
||
2901 | * @return bool |
||
2902 | * @author Naoki Sawada |
||
2903 | **/ |
||
2904 | protected function fcloseCE($fp, $path='') { |
||
2905 | return (!$this->encoding)? $this->_fclose($fp, $path) : $this->convEncOut($this->_fclose($fp, $this->convEncIn($path))); |
||
2906 | } |
||
2907 | |||
2908 | /** |
||
2909 | * Create new file and write into it from file pointer. (with convert encording) |
||
2910 | * Return new file path or false on error. |
||
2911 | * |
||
2912 | * @param resource $fp file pointer |
||
2913 | * @param string $dir target dir path |
||
2914 | * @param string $name file name |
||
2915 | * @param array $stat file stat (required by some virtual fs) |
||
2916 | * @return bool|string |
||
2917 | * @author Naoki Sawada |
||
2918 | **/ |
||
2919 | protected function saveCE($fp, $dir, $name, $stat) { |
||
2920 | return (!$this->encoding)? $this->_save($fp, $dir, $name, $stat) : $this->convEncOut($this->_save($fp, $this->convEncIn($dir), $this->convEncIn($name), $this->convEncIn($stat))); |
||
2921 | } |
||
2922 | |||
2923 | /** |
||
2924 | * Return true if path is dir and has at least one childs directory (with convert encording) |
||
2925 | * |
||
2926 | * @param string $path dir path |
||
2927 | * @return bool |
||
2928 | * @author Naoki Sawada |
||
2929 | **/ |
||
2930 | protected function subdirsCE($path) { |
||
2931 | if (!isset($this->subdirsCache[$path])) { |
||
2932 | $this->subdirsCache[$path] = (!$this->encoding)? $this->_subdirs($path) : $this->convEncOut($this->_subdirs($this->convEncIn($path))); |
||
2933 | } |
||
2934 | return $this->subdirsCache[$path]; |
||
2935 | } |
||
2936 | |||
2937 | /** |
||
2938 | * Return files list in directory (with convert encording) |
||
2939 | * |
||
2940 | * @param string $path dir path |
||
2941 | * @return array |
||
2942 | * @author Naoki Sawada |
||
2943 | **/ |
||
2944 | protected function scandirCE($path) { |
||
2945 | return (!$this->encoding)? $this->_scandir($path) : $this->convEncOut($this->_scandir($this->convEncIn($path))); |
||
2946 | } |
||
2947 | |||
2948 | /** |
||
2949 | * Create symlink (with convert encording) |
||
2950 | * |
||
2951 | * @param string $source file to link to |
||
2952 | * @param string $targetDir folder to create link in |
||
2953 | * @param string $name symlink name |
||
2954 | * @return bool |
||
2955 | * @author Naoki Sawada |
||
2956 | **/ |
||
2957 | protected function symlinkCE($source, $targetDir, $name) { |
||
2958 | return (!$this->encoding)? $this->_symlink($source, $targetDir, $name) : $this->convEncOut($this->_symlink($this->convEncIn($source), $this->convEncIn($targetDir), $this->convEncIn($name))); |
||
2959 | } |
||
2960 | |||
2961 | /***************** paths *******************/ |
||
2962 | |||
2963 | /** |
||
2964 | * Encode path into hash |
||
2965 | * |
||
2966 | * @param string file path |
||
2967 | * @return string |
||
2968 | * @author Dmitry (dio) Levashov |
||
2969 | * @author Troex Nevelin |
||
2970 | **/ |
||
2971 | protected function encode($path) { |
||
2972 | if ($path !== '') { |
||
2973 | |||
2974 | // cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root |
||
2975 | $p = $this->relpathCE($path); |
||
2976 | // if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt |
||
2977 | if ($p === '') { |
||
2978 | $p = $this->separator; |
||
2979 | } |
||
2980 | |||
2981 | // TODO crypt path and return hash |
||
2982 | $hash = $this->crypt($p); |
||
2983 | // hash is used as id in HTML that means it must contain vaild chars |
||
2984 | // make base64 html safe and append prefix in begining |
||
2985 | $hash = strtr(base64_encode($hash), '+/=', '-_.'); |
||
2986 | // remove dots '.' at the end, before it was '=' in base64 |
||
2987 | $hash = rtrim($hash, '.'); |
||
2988 | // append volume id to make hash unique |
||
2989 | return $this->id.$hash; |
||
2990 | } |
||
2991 | //TODO: Add return statement here |
||
2992 | } |
||
2993 | |||
2994 | /** |
||
2995 | * Decode path from hash |
||
2996 | * |
||
2997 | * @param string file hash |
||
2998 | * @return string |
||
2999 | * @author Dmitry (dio) Levashov |
||
3000 | * @author Troex Nevelin |
||
3001 | **/ |
||
3002 | protected function decode($hash) { |
||
3003 | if (strpos($hash, $this->id) === 0) { |
||
3004 | // cut volume id after it was prepended in encode |
||
3005 | $h = substr($hash, strlen($this->id)); |
||
3006 | // replace HTML safe base64 to normal |
||
3007 | $h = base64_decode(strtr($h, '-_.', '+/=')); |
||
3008 | // TODO uncrypt hash and return path |
||
3009 | $path = $this->uncrypt($h); |
||
3010 | // append ROOT to path after it was cut in encode |
||
3011 | return $this->abspathCE($path);//$this->root.($path === $this->separator ? '' : $this->separator.$path); |
||
3012 | } |
||
3013 | //TODO: Add return statement here |
||
3014 | } |
||
3015 | |||
3016 | /** |
||
3017 | * Return crypted path |
||
3018 | * Not implemented |
||
3019 | * |
||
3020 | * @param string path |
||
3021 | * @return mixed |
||
3022 | * @author Dmitry (dio) Levashov |
||
3023 | **/ |
||
3024 | protected function crypt($path) { |
||
3025 | return $path; |
||
3026 | } |
||
3027 | |||
3028 | /** |
||
3029 | * Return uncrypted path |
||
3030 | * Not implemented |
||
3031 | * |
||
3032 | * @param mixed hash |
||
3033 | * @return mixed |
||
3034 | * @author Dmitry (dio) Levashov |
||
3035 | **/ |
||
3036 | protected function uncrypt($hash) { |
||
3037 | return $hash; |
||
3038 | } |
||
3039 | |||
3040 | /** |
||
3041 | * Validate file name based on $this->options['acceptedName'] regexp or function |
||
3042 | * |
||
3043 | * @param string $name file name |
||
3044 | * @return bool |
||
3045 | * @author Dmitry (dio) Levashov |
||
3046 | **/ |
||
3047 | protected function nameAccepted($name) { |
||
3048 | if (json_encode($name)===false) { |
||
3049 | return false; |
||
3050 | } |
||
3051 | if ($this->nameValidator) { |
||
3052 | if (is_callable($this->nameValidator)) { |
||
3053 | $res = call_user_func($this->nameValidator, $name); |
||
3054 | return $res; |
||
3055 | } |
||
3056 | if (preg_match($this->nameValidator, '') !== false) { |
||
3057 | return preg_match($this->nameValidator, $name); |
||
3058 | } |
||
3059 | } |
||
3060 | return true; |
||
3061 | } |
||
3062 | |||
3063 | /** |
||
3064 | * Return new unique name based on file name and suffix |
||
3065 | * |
||
3066 | * @param $dir |
||
3067 | * @param $name |
||
3068 | * @param string $suffix suffix append to name |
||
3069 | * @param bool $checkNum |
||
3070 | * @param int $start |
||
3071 | * @return string |
||
3072 | * @internal param string $path file path |
||
3073 | * @author Dmitry (dio) Levashov |
||
3074 | */ |
||
3075 | public function uniqueName($dir, $name, $suffix = ' copy', $checkNum = true, $start = 1) { |
||
3076 | $ext = ''; |
||
3077 | |||
3078 | if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) { |
||
3079 | $ext = '.'.$m[1]; |
||
3080 | $name = substr($name, 0, strlen($name)-strlen($m[0])); |
||
3081 | } |
||
3082 | |||
3083 | if ($checkNum && preg_match('/('.preg_quote($suffix, '/').')(\d*)$/i', $name, $m)) { |
||
3084 | $i = (int)$m[2]; |
||
3085 | $name = substr($name, 0, strlen($name)-strlen($m[2])); |
||
3086 | } else { |
||
3087 | $i = $start; |
||
3088 | $name .= $suffix; |
||
3089 | } |
||
3090 | $max = $i+100000; |
||
3091 | |||
3092 | while ($i <= $max) { |
||
3093 | $n = $name.($i > 0 ? sprintf($this->options['uniqueNumFormat'], $i) : '').$ext; |
||
3094 | |||
3095 | if (!$this->stat($this->joinPathCE($dir, $n))) { |
||
3096 | $this->clearcache(); |
||
3097 | return $n; |
||
3098 | } |
||
3099 | $i++; |
||
3100 | } |
||
3101 | return $name.md5($dir).$ext; |
||
3102 | } |
||
3103 | |||
3104 | /** |
||
3105 | * Converts character encoding from UTF-8 to server's one |
||
3106 | * |
||
3107 | * @param mixed $var target string or array var |
||
3108 | * @param bool $restoreLocale do retore global locale, default is false |
||
3109 | * @param string $unknown replaces character for unknown |
||
3110 | * @return mixed |
||
3111 | * @author Naoki Sawada |
||
3112 | */ |
||
3113 | public function convEncIn($var = null, $restoreLocale = false, $unknown = '_') { |
||
3114 | return (!$this->encoding)? $var : $this->convEnc($var, 'UTF-8', $this->encoding, $this->options['locale'], $restoreLocale, $unknown); |
||
3115 | } |
||
3116 | |||
3117 | /** |
||
3118 | * Converts character encoding from server's one to UTF-8 |
||
3119 | * |
||
3120 | * @param mixed $var target string or array var |
||
3121 | * @param bool $restoreLocale do retore global locale, default is true |
||
3122 | * @param string $unknown replaces character for unknown |
||
3123 | * @return mixed |
||
3124 | * @author Naoki Sawada |
||
3125 | */ |
||
3126 | public function convEncOut($var = null, $restoreLocale = true, $unknown = '_') { |
||
3127 | return (!$this->encoding)? $var : $this->convEnc($var, $this->encoding, 'UTF-8', $this->options['locale'], $restoreLocale, $unknown); |
||
3128 | } |
||
3129 | |||
3130 | /** |
||
3131 | * Converts character encoding (base function) |
||
3132 | * |
||
3133 | * @param mixed $var target string or array var |
||
3134 | * @param string $from from character encoding |
||
3135 | * @param string $to to character encoding |
||
3136 | * @param string $locale local locale |
||
3137 | * @param $restoreLocale |
||
3138 | * @param string $unknown replaces character for unknown |
||
3139 | * @return mixed |
||
3140 | */ |
||
3141 | protected function convEnc($var, $from, $to, $locale, $restoreLocale, $unknown = '_') { |
||
3142 | if (strtoupper($from) !== strtoupper($to)) { |
||
3143 | if ($locale) { |
||
3144 | setlocale(LC_ALL, $locale); |
||
3145 | } |
||
3146 | if (is_array($var)) { |
||
3147 | $_ret = array(); |
||
3148 | foreach($var as $_k => $_v) { |
||
3149 | $_ret[$_k] = $this->convEnc($_v, $from, $to, '', false, $unknown = '_'); |
||
3150 | } |
||
3151 | $var = $_ret; |
||
3152 | } else { |
||
3153 | $_var = false; |
||
3154 | if (is_string($var)) { |
||
3155 | $_var = $var; |
||
3156 | if (false !== ($_var = iconv($from, $to.'//TRANSLIT', $_var))) { |
||
3157 | $_var = str_replace('?', $unknown, $_var); |
||
3158 | } |
||
3159 | } |
||
3160 | if ($_var !== false) { |
||
3161 | $var = $_var; |
||
3162 | } |
||
3163 | } |
||
3164 | if ($restoreLocale) { |
||
3165 | setlocale(LC_ALL, elFinder::$locale); |
||
3166 | } |
||
3167 | } |
||
3168 | return $var; |
||
3169 | } |
||
3170 | |||
3171 | /*********************** util mainly for inheritance class *********************/ |
||
3172 | |||
3173 | /** |
||
3174 | * Get temporary filename. Tempfile will be removed when after script execution finishes or exit() is called. |
||
3175 | * When needing the unique file to a path, give $path to parameter. |
||
3176 | * |
||
3177 | * @param string $path for get unique file to a path |
||
3178 | * @return string|false |
||
3179 | * @author Naoki Sawada |
||
3180 | */ |
||
3181 | protected function getTempFile($path = '') { |
||
3182 | static $cache = array(); |
||
3183 | static $rmfunc; |
||
3184 | |||
3185 | $key = ''; |
||
3186 | if ($path !== '') { |
||
3187 | $key = $this->id . '#' . $path; |
||
3188 | if (isset($cache[$key])) { |
||
3189 | return $cache[$key]; |
||
3190 | } |
||
3191 | } |
||
3192 | |||
3193 | if ($tmpdir = $this->getTempPath()) { |
||
3194 | if (!$rmfunc) { |
||
3195 | $rmfunc = create_function('$f', 'is_file($f) && unlink($f);'); |
||
3196 | } |
||
3197 | $name = tempnam($tmpdir, 'ELF'); |
||
3198 | if ($key) { |
||
3199 | $cache[$key] = $name; |
||
3200 | } |
||
3201 | register_shutdown_function($rmfunc, $name); |
||
3202 | return $name; |
||
3203 | } |
||
3204 | |||
3205 | return false; |
||
3206 | } |
||
3207 | |||
3208 | /** |
||
3209 | * File path of local server side work file path |
||
3210 | * |
||
3211 | * @param string $path path need convert encoding to server encoding |
||
3212 | * @return string |
||
3213 | * @author Naoki Sawada |
||
3214 | */ |
||
3215 | protected function getWorkFile($path) { |
||
3216 | if ($work = $this->getTempFile()) { |
||
3217 | if ($wfp = fopen($work, 'wb')) { |
||
3218 | if ($fp = $this->_fopen($path)) { |
||
3219 | while(!feof($fp)) { |
||
3220 | fwrite($wfp, fread($fp, 8192)); |
||
3221 | } |
||
3222 | $this->_fclose($fp, $path); |
||
3223 | fclose($wfp); |
||
3224 | return $work; |
||
3225 | } |
||
3226 | } |
||
3227 | } |
||
3228 | return false; |
||
3229 | } |
||
3230 | |||
3231 | /** |
||
3232 | * Get image size array with `dimensions` |
||
3233 | * |
||
3234 | * @param string $path path need convert encoding to server encoding |
||
3235 | * @param string $mime file mime type |
||
3236 | * @return array|false |
||
3237 | */ |
||
3238 | public function getImageSize($path, $mime = '') { |
||
3239 | $size = false; |
||
3240 | if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') { |
||
3241 | if ($work = $this->getWorkFile($path)) { |
||
3242 | if ($size = getimagesize($work)) { |
||
3243 | $size['dimensions'] = $size[0].'x'.$size[1]; |
||
3244 | } |
||
3245 | } |
||
3246 | is_file($work) && unlink($work); |
||
3247 | } |
||
3248 | return $size; |
||
3249 | } |
||
3250 | |||
3251 | /** |
||
3252 | * Delete dirctory trees |
||
3253 | * |
||
3254 | * @param string $localpath path need convert encoding to server encoding |
||
3255 | * @return boolean |
||
3256 | * @author Naoki Sawada |
||
3257 | */ |
||
3258 | protected function delTree($localpath) { |
||
3259 | foreach ($this->_scandir($localpath) as $p) { |
||
3260 | elFinder::extendTimeLimit(); |
||
3261 | $stat = $this->stat($this->convEncOut($p)); |
||
3262 | $this->convEncIn(); |
||
3263 | ($stat['mime'] === 'directory')? $this->delTree($p) : $this->_unlink($p); |
||
3264 | } |
||
3265 | return $this->_rmdir($localpath); |
||
3266 | } |
||
3267 | |||
3268 | /** |
||
3269 | * Copy items to a new temporary directory on the local server |
||
3270 | * |
||
3271 | * @param array $hashes target hashes |
||
3272 | * @param string $dir destination directory (for recurcive) |
||
3273 | * @return string|false saved path name |
||
3274 | * @author Naoki Sawada |
||
3275 | */ |
||
3276 | protected function getItemsInHand($hashes, $dir = null) { |
||
3277 | static $totalSize = 0; |
||
3278 | if (is_null($dir)) { |
||
3279 | $totalSize = 0; |
||
3280 | if (! $tmpDir = $this->getTempPath()) { |
||
3281 | return false; |
||
3282 | } |
||
3283 | $dir = tempnam($tmpDir, 'elf'); |
||
3284 | if (!unlink($dir) || !mkdir($dir, 0700, true)) { |
||
3285 | return false; |
||
3286 | } |
||
3287 | register_shutdown_function(array($this, 'rmdirRecursive'), $dir); |
||
3288 | } |
||
3289 | $res = true; |
||
3290 | $files = array(); |
||
3291 | foreach ($hashes as $hash) { |
||
3292 | if (($file = $this->file($hash)) == false) { |
||
3293 | continue; |
||
3294 | } |
||
3295 | if (!$file['read']) { |
||
3296 | continue; |
||
3297 | } |
||
3298 | |||
3299 | $name = $file['name']; |
||
3300 | // for call from search results |
||
3301 | if (isset($files[$name])) { |
||
3302 | $name = preg_replace('/^(.*?)(\..*)?$/', '$1_'.$files[$name]++.'$2', $name); |
||
3303 | } else { |
||
3304 | $files[$name] = 1; |
||
3305 | } |
||
3306 | $target = $dir.DIRECTORY_SEPARATOR.$name; |
||
3307 | |||
3308 | if ($file['mime'] === 'directory') { |
||
3309 | $chashes = array(); |
||
3310 | $_files = $this->scandir($hash); |
||
3311 | foreach($_files as $_file) { |
||
3312 | if ($file['read']) { |
||
3313 | $chashes[] = $_file['hash']; |
||
3314 | } |
||
3315 | } |
||
3316 | if (($res = mkdir($target, 0700, true)) && $chashes) { |
||
3317 | $res = $this->getItemsInHand($chashes, $target); |
||
3318 | } |
||
3319 | if (!$res) { |
||
3320 | break; |
||
3321 | } |
||
3322 | !empty($file['ts']) && touch($target, $file['ts']); |
||
3323 | } else { |
||
3324 | $path = $this->decode($hash); |
||
3325 | if ($fp = $this->fopenCE($path)) { |
||
3326 | if ($tfp = fopen($target, 'wb')) { |
||
3327 | $totalSize += stream_copy_to_stream($fp, $tfp); |
||
3328 | fclose($tfp); |
||
3329 | } |
||
3330 | !empty($file['ts']) && touch($target, $file['ts']); |
||
3331 | $this->fcloseCE($fp, $path); |
||
3332 | } |
||
3333 | if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $totalSize) { |
||
3334 | $res = $this->setError(elFinder::ERROR_ARC_MAXSIZE); |
||
3335 | } |
||
3336 | } |
||
3337 | } |
||
3338 | return $res? $dir : false; |
||
3339 | } |
||
3340 | |||
3341 | /*********************** file stat *********************/ |
||
3342 | |||
3343 | /** |
||
3344 | * Check file attribute |
||
3345 | * |
||
3346 | * @param string $path file path |
||
3347 | * @param string $name attribute name (read|write|locked|hidden) |
||
3348 | * @param bool $val attribute value returned by file system |
||
3349 | * @param bool $isDir path is directory (true: directory, false: file) |
||
3350 | * @return bool |
||
3351 | * @author Dmitry (dio) Levashov |
||
3352 | **/ |
||
3353 | protected function attr($path, $name, $val=null, $isDir=null) { |
||
3354 | if (!isset($this->defaults[$name])) { |
||
3355 | return false; |
||
3356 | } |
||
3357 | |||
3358 | |||
3359 | $perm = null; |
||
3360 | |||
3361 | if ($this->access) { |
||
3362 | $perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this, $isDir); |
||
3363 | |||
3364 | if ($perm !== null) { |
||
3365 | return !!$perm; |
||
3366 | } |
||
3367 | } |
||
3368 | |||
3369 | if ($this->separator != '/') { |
||
3370 | $path = str_replace($this->separator, '/', $this->relpathCE($path)); |
||
3371 | } else { |
||
3372 | $path = $this->relpathCE($path); |
||
3373 | } |
||
3374 | |||
3375 | $path = '/'.$path; |
||
3376 | |||
3377 | for ($i = 0, $c = count($this->attributes); $i < $c; $i++) { |
||
3378 | $attrs = $this->attributes[$i]; |
||
3379 | |||
3380 | if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $path)) { |
||
3381 | $perm = $attrs[$name]; |
||
3382 | } |
||
3383 | } |
||
3384 | |||
3385 | return $perm === null ? (is_null($val)? $this->defaults[$name] : $val) : !!$perm; |
||
3386 | } |
||
3387 | |||
3388 | /** |
||
3389 | * Return true if file with given name can be created in given folder. |
||
3390 | * |
||
3391 | * @param string $dir parent dir path |
||
3392 | * @param string $name new file name |
||
3393 | * @param null $isDir |
||
3394 | * @return bool |
||
3395 | * @author Dmitry (dio) Levashov |
||
3396 | */ |
||
3397 | protected function allowCreate($dir, $name, $isDir = null) { |
||
3398 | $path = $this->joinPathCE($dir, $name); |
||
3399 | $perm = null; |
||
3400 | |||
3401 | if ($this->access) { |
||
3402 | $perm = call_user_func($this->access, 'write', $path, $this->options['accessControlData'], $this, $isDir); |
||
3403 | if ($perm !== null) { |
||
3404 | return !!$perm; |
||
3405 | } |
||
3406 | } |
||
3407 | |||
3408 | $testPath = $this->separator.$this->relpathCE($path); |
||
3409 | |||
3410 | for ($i = 0, $c = count($this->attributes); $i < $c; $i++) { |
||
3411 | $attrs = $this->attributes[$i]; |
||
3412 | |||
3413 | if (isset($attrs['write']) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $testPath)) { |
||
3414 | $perm = $attrs['write']; |
||
3415 | } |
||
3416 | } |
||
3417 | |||
3418 | return $perm === null ? true : $perm; |
||
3419 | } |
||
3420 | |||
3421 | /** |
||
3422 | * Return true if file MIME type can save with check uploadOrder config. |
||
3423 | * |
||
3424 | * @param string $mime |
||
3425 | * @return boolean |
||
3426 | */ |
||
3427 | protected function allowPutMime($mime) { |
||
3428 | // logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order |
||
3429 | $allow = $this->mimeAccepted($mime, $this->uploadAllow, null); |
||
3430 | $deny = $this->mimeAccepted($mime, $this->uploadDeny, null); |
||
3431 | $res = true; // default to allow |
||
3432 | if (strtolower($this->uploadOrder[0]) == 'allow') { // array('allow', 'deny'), default is to 'deny' |
||
3433 | $res = false; // default is deny |
||
3434 | if (!$deny && ($allow === true)) { // match only allow |
||
3435 | $res = true; |
||
3436 | }// else (both match | no match | match only deny) { deny } |
||
3437 | } else { // array('deny', 'allow'), default is to 'allow' - this is the default rule |
||
3438 | $res = true; // default is allow |
||
3439 | if (($deny === true) && !$allow) { // match only deny |
||
3440 | $res = false; |
||
3441 | } // else (both match | no match | match only allow) { allow } |
||
3442 | } |
||
3443 | return $res; |
||
3444 | } |
||
3445 | |||
3446 | /** |
||
3447 | * Return fileinfo |
||
3448 | * |
||
3449 | * @param string $path file cache |
||
3450 | * @return array |
||
3451 | * @author Dmitry (dio) Levashov |
||
3452 | **/ |
||
3453 | protected function stat($path) { |
||
3454 | if ($path === false || is_null($path)) { |
||
3455 | return false; |
||
3456 | } |
||
3457 | $is_root = ($path == $this->root); |
||
3458 | if ($is_root) { |
||
3459 | $rootKey = md5($path.(isset($this->options['alias'])? $this->options['alias'] : '')); |
||
3460 | if (!isset($this->sessionCache['rootstat'])) { |
||
3461 | $this->sessionCache['rootstat'] = array(); |
||
3462 | } |
||
3463 | if (! $this->isMyReload()) { |
||
3464 | // need $path as key for netmount/netunmount |
||
3465 | if (isset($this->sessionCache['rootstat'][$rootKey])) { |
||
3466 | if ($ret = $this->sessionCache['rootstat'][$rootKey]) { |
||
3467 | if ($this->options['rootRev'] === $ret['rootRev']) { |
||
3468 | if (isset($this->options['phash'])) { |
||
3469 | $ret['isroot'] = 1; |
||
3470 | $ret['phash'] = $this->options['phash']; |
||
3471 | } |
||
3472 | return $ret; |
||
3473 | } |
||
3474 | } |
||
3475 | } |
||
3476 | } |
||
3477 | } |
||
3478 | $ret = isset($this->cache[$path]) |
||
3479 | ? $this->cache[$path] |
||
3480 | : $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path)))); |
||
3481 | if ($is_root) { |
||
3482 | $this->rootModified = false; |
||
3483 | $this->sessionCache['rootstat'][$rootKey] = $ret; |
||
3484 | $this->session->set($this->id, $this->sessionCache); |
||
3485 | if (isset($this->options['phash'])) { |
||
3486 | $ret['isroot'] = 1; |
||
3487 | $ret['phash'] = $this->options['phash']; |
||
3488 | } |
||
3489 | } |
||
3490 | return $ret; |
||
3491 | } |
||
3492 | |||
3493 | /** |
||
3494 | * Get root stat extra key values |
||
3495 | * |
||
3496 | * @return array stat extras |
||
3497 | * @author Naoki Sawada |
||
3498 | */ |
||
3499 | protected function getRootStatExtra() { |
||
3500 | $stat = array(); |
||
3501 | if ($this->rootName) { |
||
3502 | $stat['name'] = $this->rootName; |
||
3503 | } |
||
3504 | $stat['rootRev'] = $this->options['rootRev']; |
||
3505 | $stat['options'] = $this->options(null); |
||
3506 | return $stat; |
||
3507 | } |
||
3508 | |||
3509 | /** |
||
3510 | * Put file stat in cache and return it |
||
3511 | * |
||
3512 | * @param string $path file path |
||
3513 | * @param array $stat file stat |
||
3514 | * @return array |
||
3515 | * @author Dmitry (dio) Levashov |
||
3516 | **/ |
||
3517 | protected function updateCache($path, $stat) { |
||
3518 | if (empty($stat) || !is_array($stat)) { |
||
3519 | return $this->cache[$path] = array(); |
||
3520 | } |
||
3521 | |||
3522 | $stat['hash'] = $this->encode($path); |
||
3523 | |||
3524 | $root = $path == $this->root; |
||
3525 | $parent = ''; |
||
3526 | |||
3527 | if ($root) { |
||
3528 | $stat = array_merge($stat, $this->getRootStatExtra()); |
||
3529 | } else { |
||
3530 | if (!isset($stat['name']) || $stat['name'] === '') { |
||
3531 | $stat['name'] = $this->basenameCE($path); |
||
3532 | } |
||
3533 | if (empty($stat['phash'])) { |
||
3534 | $parent = $this->dirnameCE($path); |
||
3535 | $stat['phash'] = $this->encode($parent); |
||
3536 | } else { |
||
3537 | $parent = $this->getPath($stat['phash']); |
||
3538 | } |
||
3539 | } |
||
3540 | |||
3541 | // name check |
||
3542 | if (!$jeName = json_encode($stat['name'])) { |
||
3543 | return $this->cache[$path] = array(); |
||
3544 | } |
||
3545 | // fix name if required |
||
3546 | if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) { |
||
3547 | $stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], $jeName)); |
||
3548 | } |
||
3549 | |||
3550 | |||
3551 | if (empty($stat['mime'])) { |
||
3552 | $stat['mime'] = $this->mimetype($stat['name']); |
||
3553 | } |
||
3554 | |||
3555 | // @todo move dateformat to client |
||
3556 | // $stat['date'] = isset($stat['ts']) |
||
3557 | // ? $this->formatDate($stat['ts']) |
||
3558 | // : 'unknown'; |
||
3559 | |||
3560 | if (!isset($stat['size'])) { |
||
3561 | $stat['size'] = 'unknown'; |
||
3562 | } |
||
3563 | |||
3564 | if ($isDir = ($stat['mime'] === 'directory')) { |
||
3565 | $stat['volumeid'] = $this->id; |
||
3566 | } |
||
3567 | |||
3568 | $stat['read'] = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null, $isDir)); |
||
3569 | $stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null, $isDir)); |
||
3570 | if ($root) { |
||
3571 | $stat['locked'] = 1; |
||
3572 | if ($this->options['type'] !== '') { |
||
3573 | $stat['type'] = $this->options['type']; |
||
3574 | } |
||
3575 | } else { |
||
3576 | // lock when parent directory is not writable |
||
3577 | if (!isset($stat['locked'])) { |
||
3578 | $pstat = $this->stat($parent); |
||
3579 | if (isset($pstat['write']) && !$pstat['write']) { |
||
3580 | $stat['locked'] = true; |
||
3581 | } |
||
3582 | } |
||
3583 | if ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null, $isDir)) { |
||
3584 | $stat['locked'] = 1; |
||
3585 | } else { |
||
3586 | unset($stat['locked']); |
||
3587 | } |
||
3588 | } |
||
3589 | |||
3590 | if ($root) { |
||
3591 | unset($stat['hidden']); |
||
3592 | } elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null, $isDir) |
||
3593 | || !$this->mimeAccepted($stat['mime'])) { |
||
3594 | $stat['hidden'] = 1; |
||
3595 | } else { |
||
3596 | unset($stat['hidden']); |
||
3597 | } |
||
3598 | |||
3599 | if ($stat['read'] && empty($stat['hidden'])) { |
||
3600 | |||
3601 | if ($isDir) { |
||
3602 | // caching parent's subdirs |
||
3603 | if ($parent) { |
||
3604 | $this->subdirsCache[$parent] = true; |
||
3605 | } |
||
3606 | // for dir - check for subdirs |
||
3607 | if ($this->options['checkSubfolders']) { |
||
3608 | if (isset($stat['dirs'])) { |
||
3609 | if ($stat['dirs']) { |
||
3610 | $stat['dirs'] = 1; |
||
3611 | } else { |
||
3612 | unset($stat['dirs']); |
||
3613 | } |
||
3614 | } elseif (!empty($stat['alias']) && !empty($stat['target'])) { |
||
3615 | $stat['dirs'] = isset($this->cache[$stat['target']]) |
||
3616 | ? intval(isset($this->cache[$stat['target']]['dirs'])) |
||
3617 | : $this->subdirsCE($stat['target']); |
||
3618 | |||
3619 | } elseif ($this->subdirsCE($path)) { |
||
3620 | $stat['dirs'] = 1; |
||
3621 | } |
||
3622 | } else { |
||
3623 | $stat['dirs'] = 1; |
||
3624 | } |
||
3625 | if ($this->options['dirUrlOwn'] === true) { |
||
3626 | $stat['url'] = '#elf_' . $stat['hash']; |
||
3627 | } |
||
3628 | } else { |
||
3629 | // for files - check for thumbnails |
||
3630 | $p = isset($stat['target']) ? $stat['target'] : $path; |
||
3631 | if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) { |
||
3632 | $tmb = $this->gettmb($p, $stat); |
||
3633 | $stat['tmb'] = $tmb ? $tmb : 1; |
||
3634 | } |
||
3635 | |||
3636 | } |
||
3637 | if (!isset($stat['url']) && $this->URL && $this->encoding) { |
||
3638 | $_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1)); |
||
3639 | $stat['url'] = rtrim($this->URL, '/') . '/' . str_replace('%2F', '/', rawurlencode((substr(PHP_OS, 0, 3) === 'WIN')? $_path : $this->convEncIn($_path, true))); |
||
3640 | } |
||
3641 | } else { |
||
3642 | if ($isDir) { |
||
3643 | unset($stat['dirs']); |
||
3644 | } |
||
3645 | } |
||
3646 | |||
3647 | if (!empty($stat['alias']) && !empty($stat['target'])) { |
||
3648 | $stat['thash'] = $this->encode($stat['target']); |
||
3649 | //$this->cache[$stat['target']] = $stat; |
||
3650 | unset($stat['target']); |
||
3651 | } |
||
3652 | |||
3653 | return $this->cache[$path] = $stat; |
||
3654 | } |
||
3655 | |||
3656 | /** |
||
3657 | * Get stat for folder content and put in cache |
||
3658 | * |
||
3659 | * @param string $path |
||
3660 | * @return void |
||
3661 | * @author Dmitry (dio) Levashov |
||
3662 | **/ |
||
3663 | protected function cacheDir($path) { |
||
3664 | $this->dirsCache[$path] = array(); |
||
3665 | $this->subdirsCache[$path] = false; |
||
3666 | |||
3667 | foreach ($this->scandirCE($path) as $p) { |
||
3668 | if (($stat = $this->stat($p)) && empty($stat['hidden'])) { |
||
3669 | if ($stat['mime'] === 'directory') { |
||
3670 | $this->subdirsCache[$path] = true; |
||
3671 | } |
||
3672 | $this->dirsCache[$path][] = $p; |
||
3673 | } |
||
3674 | } |
||
3675 | } |
||
3676 | |||
3677 | /** |
||
3678 | * Clean cache |
||
3679 | * |
||
3680 | * @return void |
||
3681 | * @author Dmitry (dio) Levashov |
||
3682 | **/ |
||
3683 | protected function clearcache() { |
||
3684 | $this->cache = $this->dirsCache = $this->subdirsCache = array(); |
||
3685 | } |
||
3686 | |||
3687 | /** |
||
3688 | * Return file mimetype |
||
3689 | * |
||
3690 | * @param string $path file path |
||
3691 | * @param string $name |
||
3692 | * @return string |
||
3693 | * @author Dmitry (dio) Levashov |
||
3694 | */ |
||
3695 | protected function mimetype($path, $name = '') { |
||
3696 | $type = ''; |
||
3697 | |||
3698 | if ($name === '') { |
||
3699 | $name = $path; |
||
3700 | } |
||
3701 | $ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1); |
||
3702 | if (is_readable($path)) { |
||
3703 | if ($this->mimeDetect == 'finfo') { |
||
3704 | if ($type = finfo_file($this->finfo, $path)) { |
||
3705 | if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) { |
||
3706 | if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext]; |
||
3707 | } else if ($ext === 'js' && preg_match('~^text/~', $type)) { |
||
3708 | $type = 'text/javascript'; |
||
3709 | } |
||
3710 | } else { |
||
3711 | $type = 'unknown'; |
||
3712 | } |
||
3713 | } else if ($this->mimeDetect == 'mime_content_type') { |
||
3714 | $type = mime_content_type($path); |
||
3715 | } |
||
3716 | } |
||
3717 | if (! $type) { |
||
3718 | $type = elFinderVolumeDriver::mimetypeInternalDetect($path); |
||
3719 | } |
||
3720 | |||
3721 | $type = explode(';', $type); |
||
3722 | $type = trim($type[0]); |
||
3723 | |||
3724 | if (in_array($type, array('application/x-empty', 'inode/x-empty'))) { |
||
3725 | // finfo return this mime for empty files |
||
3726 | $type = 'text/plain'; |
||
3727 | } elseif ($type == 'application/x-zip') { |
||
3728 | // http://elrte.org/redmine/issues/163 |
||
3729 | $type = 'application/zip'; |
||
3730 | } |
||
3731 | |||
3732 | // mime type normalization |
||
3733 | $_checkKey = strtolower($ext.':'.$type); |
||
3734 | if (isset($this->options['mimeMap'][$_checkKey])) { |
||
3735 | $type = $this->options['mimeMap'][$_checkKey]; |
||
3736 | } |
||
3737 | |||
3738 | return $type == 'unknown' && $this->mimeDetect != 'internal' |
||
3739 | ? elFinderVolumeDriver::mimetypeInternalDetect($path) |
||
3740 | : $type; |
||
3741 | |||
3742 | } |
||
3743 | |||
3744 | /** |
||
3745 | * Detect file mimetype using "internal" method or Loading mime.types with $path = '' |
||
3746 | * |
||
3747 | * @param string $path file path |
||
3748 | * @return string |
||
3749 | * @author Dmitry (dio) Levashov |
||
3750 | **/ |
||
3751 | static protected function mimetypeInternalDetect($path = '') { |
||
3752 | // load default MIME table file "mime.types" |
||
3753 | if (!elFinderVolumeDriver::$mimetypesLoaded) { |
||
3754 | elFinderVolumeDriver::$mimetypesLoaded = true; |
||
3755 | $file = elFinder::$defaultMimefile; |
||
3756 | if ($file === '' || ! is_readable($file)) { |
||
3757 | $file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types'; |
||
3758 | } |
||
3759 | if (is_readable($file)) { |
||
3760 | $mimecf = file($file); |
||
3761 | foreach ($mimecf as $line_num => $line) { |
||
3762 | if (!preg_match('/^\s*#/', $line)) { |
||
3763 | $mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY); |
||
3764 | for ($i = 1, $size = count($mime); $i < $size ; $i++) { |
||
3765 | if (!isset(elFinderVolumeDriver::$mimetypes[$mime[$i]])) { |
||
3766 | elFinderVolumeDriver::$mimetypes[$mime[$i]] = $mime[0]; |
||
3767 | } |
||
3768 | } |
||
3769 | } |
||
3770 | } |
||
3771 | } |
||
3772 | } |
||
3773 | $ext = ''; |
||
3774 | if ($path) { |
||
3775 | $pinfo = pathinfo($path); |
||
3776 | $ext = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : ''; |
||
3777 | } |
||
3778 | return ($ext && isset(elFinderVolumeDriver::$mimetypes[$ext])) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown'; |
||
3779 | } |
||
3780 | |||
3781 | /** |
||
3782 | * Return file/total directory size |
||
3783 | * |
||
3784 | * @param string $path file path |
||
3785 | * @return int |
||
3786 | * @author Dmitry (dio) Levashov |
||
3787 | **/ |
||
3788 | protected function countSize($path) { |
||
3789 | |||
3790 | elFinder::extendTimeLimit(); |
||
3791 | |||
3792 | $stat = $this->stat($path); |
||
3793 | |||
3794 | if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) { |
||
3795 | return 'unknown'; |
||
3796 | } |
||
3797 | |||
3798 | if ($stat['mime'] != 'directory') { |
||
3799 | return $stat['size']; |
||
3800 | } |
||
3801 | |||
3802 | $subdirs = $this->options['checkSubfolders']; |
||
3803 | $this->options['checkSubfolders'] = true; |
||
3804 | $result = 0; |
||
3805 | foreach ($this->getScandir($path) as $stat) { |
||
3806 | $size = $stat['mime'] == 'directory' && $stat['read'] |
||
3807 | ? $this->countSize($this->joinPathCE($path, $stat['name'])) |
||
3808 | : (isset($stat['size']) ? intval($stat['size']) : 0); |
||
3809 | if ($size > 0) { |
||
3810 | $result += $size; |
||
3811 | } |
||
3812 | } |
||
3813 | $this->options['checkSubfolders'] = $subdirs; |
||
3814 | return $result; |
||
3815 | } |
||
3816 | |||
3817 | /** |
||
3818 | * Return true if all mimes is directory or files |
||
3819 | * |
||
3820 | * @param string $mime1 mimetype |
||
3821 | * @param string $mime2 mimetype |
||
3822 | * @return bool |
||
3823 | * @author Dmitry (dio) Levashov |
||
3824 | **/ |
||
3825 | protected function isSameType($mime1, $mime2) { |
||
3826 | return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory'); |
||
3827 | } |
||
3828 | |||
3829 | /** |
||
3830 | * If file has required attr == $val - return file path, |
||
3831 | * If dir has child with has required attr == $val - return child path |
||
3832 | * |
||
3833 | * @param string $path file path |
||
3834 | * @param string $attr attribute name |
||
3835 | * @param bool $val attribute value |
||
3836 | * @return string|false |
||
3837 | * @author Dmitry (dio) Levashov |
||
3838 | **/ |
||
3839 | protected function closestByAttr($path, $attr, $val) { |
||
3840 | $stat = $this->stat($path); |
||
3841 | |||
3842 | if (empty($stat)) { |
||
3843 | return false; |
||
3844 | } |
||
3845 | |||
3846 | $v = isset($stat[$attr]) ? $stat[$attr] : false; |
||
3847 | |||
3848 | if ($v == $val) { |
||
3849 | return $path; |
||
3850 | } |
||
3851 | |||
3852 | return $stat['mime'] == 'directory' |
||
3853 | ? $this->childsByAttr($path, $attr, $val) |
||
3854 | : false; |
||
3855 | } |
||
3856 | |||
3857 | /** |
||
3858 | * Return first found children with required attr == $val |
||
3859 | * |
||
3860 | * @param string $path file path |
||
3861 | * @param string $attr attribute name |
||
3862 | * @param bool $val attribute value |
||
3863 | * @return string|false |
||
3864 | * @author Dmitry (dio) Levashov |
||
3865 | **/ |
||
3866 | protected function childsByAttr($path, $attr, $val) { |
||
3867 | foreach ($this->scandirCE($path) as $p) { |
||
3868 | if (($_p = $this->closestByAttr($p, $attr, $val)) != false) { |
||
3869 | return $_p; |
||
3870 | } |
||
3871 | } |
||
3872 | return false; |
||
3873 | } |
||
3874 | |||
3875 | protected function isMyReload($target = '', $ARGtarget = '') { |
||
3876 | if ($this->rootModified || (! empty($this->ARGS['cmd']) && $this->ARGS['cmd'] === 'parents')) { |
||
3877 | return true; |
||
3878 | } |
||
3879 | if (! empty($this->ARGS['reload'])) { |
||
3880 | if ($ARGtarget === '') { |
||
3881 | $ARGtarget = isset($this->ARGS['target'])? $this->ARGS['target'] |
||
3882 | : ((isset($this->ARGS['targets']) && is_array($this->ARGS['targets']) && count($this->ARGS['targets']) === 1)? |
||
3883 | $this->ARGS['targets'][0] : ''); |
||
3884 | } |
||
3885 | if ($ARGtarget !== '') { |
||
3886 | $ARGtarget = strval($ARGtarget); |
||
3887 | if ($target === '') { |
||
3888 | return (strpos($ARGtarget, $this->id) === 0); |
||
3889 | } else { |
||
3890 | $target = strval($target); |
||
3891 | return ($target === $ARGtarget); |
||
3892 | } |
||
3893 | } |
||
3894 | } |
||
3895 | return false; |
||
3896 | } |
||
3897 | |||
3898 | /***************** get content *******************/ |
||
3899 | |||
3900 | /** |
||
3901 | * Return required dir's files info. |
||
3902 | * If onlyMimes is set - return only dirs and files of required mimes |
||
3903 | * |
||
3904 | * @param string $path dir path |
||
3905 | * @return array |
||
3906 | * @author Dmitry (dio) Levashov |
||
3907 | **/ |
||
3908 | protected function getScandir($path) { |
||
3909 | $files = array(); |
||
3910 | |||
3911 | !isset($this->dirsCache[$path]) && $this->cacheDir($path); |
||
3912 | |||
3913 | foreach ($this->dirsCache[$path] as $p) { |
||
3914 | if (($stat = $this->stat($p)) && empty($stat['hidden'])) { |
||
3915 | $files[] = $stat; |
||
3916 | } |
||
3917 | } |
||
3918 | |||
3919 | return $files; |
||
3920 | } |
||
3921 | |||
3922 | |||
3923 | /** |
||
3924 | * Return subdirs tree |
||
3925 | * |
||
3926 | * @param string $path parent dir path |
||
3927 | * @param int $deep tree deep |
||
3928 | * @param string $exclude |
||
3929 | * @return array |
||
3930 | * @author Dmitry (dio) Levashov |
||
3931 | */ |
||
3932 | protected function gettree($path, $deep, $exclude='') { |
||
3933 | $dirs = array(); |
||
3934 | |||
3935 | !isset($this->dirsCache[$path]) && $this->cacheDir($path); |
||
3936 | |||
3937 | foreach ($this->dirsCache[$path] as $p) { |
||
3938 | $stat = $this->stat($p); |
||
3939 | |||
3940 | if ($stat && empty($stat['hidden']) && $p != $exclude && $stat['mime'] == 'directory') { |
||
3941 | $dirs[] = $stat; |
||
3942 | if ($deep > 0 && !empty($stat['dirs'])) { |
||
3943 | $dirs = array_merge($dirs, $this->gettree($p, $deep-1)); |
||
3944 | } |
||
3945 | } |
||
3946 | } |
||
3947 | |||
3948 | return $dirs; |
||
3949 | } |
||
3950 | |||
3951 | /** |
||
3952 | * Recursive files search |
||
3953 | * |
||
3954 | * @param string $path dir path |
||
3955 | * @param string $q search string |
||
3956 | * @param array $mimes |
||
3957 | * @return array |
||
3958 | * @author Dmitry (dio) Levashov |
||
3959 | **/ |
||
3960 | protected function doSearch($path, $q, $mimes) { |
||
3961 | $result = array(); |
||
3962 | |||
3963 | $timeout = $this->options['searchTimeout']? $this->searchStart + $this->options['searchTimeout'] : 0; |
||
3964 | if ($timeout && $timeout < time()) { |
||
3965 | $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path))); |
||
3966 | return $result; |
||
3967 | } |
||
3968 | |||
3969 | foreach($this->scandirCE($path) as $p) { |
||
3970 | elFinder::extendTimeLimit($this->options['searchTimeout'] + 30); |
||
3971 | |||
3972 | if ($timeout && ($this->error || $timeout < time())) { |
||
3973 | !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path))); |
||
3974 | break; |
||
3975 | } |
||
3976 | |||
3977 | |||
3978 | $stat = $this->stat($p); |
||
3979 | |||
3980 | if (!$stat) { // invalid links |
||
3981 | continue; |
||
3982 | } |
||
3983 | |||
3984 | if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) { |
||
3985 | continue; |
||
3986 | } |
||
3987 | |||
3988 | $name = $stat['name']; |
||
3989 | |||
3990 | if ($this->doSearchCurrentQuery['excludes']) { |
||
3991 | foreach($this->doSearchCurrentQuery['excludes'] as $exclude) { |
||
3992 | if ($this->stripos($name, $exclude) !== false) { |
||
3993 | continue 2; |
||
3994 | } |
||
3995 | } |
||
3996 | } |
||
3997 | |||
3998 | if ((!$mimes || $stat['mime'] !== 'directory') && $this->stripos($name, $q) !== false) { |
||
3999 | $stat['path'] = $this->path($stat['hash']); |
||
4000 | if ($this->URL && !isset($stat['url'])) { |
||
4001 | $path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1)); |
||
4002 | if ($this->encoding) { |
||
4003 | $path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true))); |
||
4004 | } |
||
4005 | $stat['url'] = $this->URL . $path; |
||
4006 | } |
||
4007 | |||
4008 | $result[] = $stat; |
||
4009 | } |
||
4010 | if ($stat['mime'] == 'directory' && $stat['read'] && !isset($stat['alias'])) { |
||
4011 | if (! $this->options['searchExDirReg'] || ! preg_match($this->options['searchExDirReg'], $p)) { |
||
4012 | $result = array_merge($result, $this->doSearch($p, $q, $mimes)); |
||
4013 | } |
||
4014 | } |
||
4015 | } |
||
4016 | |||
4017 | return $result; |
||
4018 | } |
||
4019 | |||
4020 | /********************** manuipulations ******************/ |
||
4021 | |||
4022 | /** |
||
4023 | * Copy file/recursive copy dir only in current volume. |
||
4024 | * Return new file path or false. |
||
4025 | * |
||
4026 | * @param string $src source path |
||
4027 | * @param string $dst destination dir path |
||
4028 | * @param string $name new file name (optionaly) |
||
4029 | * @return string|false |
||
4030 | * @author Dmitry (dio) Levashov |
||
4031 | **/ |
||
4032 | protected function copy($src, $dst, $name) { |
||
4033 | |||
4034 | elFinder::extendTimeLimit(); |
||
4035 | |||
4036 | $srcStat = $this->stat($src); |
||
4037 | $this->clearcache(); |
||
4038 | |||
4039 | if (!empty($srcStat['thash'])) { |
||
4040 | $target = $this->decode($srcStat['thash']); |
||
4041 | if (!$this->inpathCE($target, $this->root)) { |
||
4042 | return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK); |
||
4043 | } |
||
4044 | $stat = $this->stat($target); |
||
4045 | $this->clearcache(); |
||
4046 | return $stat && $this->symlinkCE($target, $dst, $name) |
||
4047 | ? $this->joinPathCE($dst, $name) |
||
4048 | : $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash'])); |
||
4049 | } |
||
4050 | |||
4051 | if ($srcStat['mime'] === 'directory') { |
||
4052 | $testStat = $this->stat($this->joinPathCE($dst, $name)); |
||
4053 | $this->clearcache(); |
||
4054 | |||
4055 | if (($testStat && $testStat['mime'] != 'directory') || (! $testStat && ! $testStat = $this->mkdir($this->encode($dst), $name))) { |
||
4056 | return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash'])); |
||
4057 | } |
||
4058 | |||
4059 | $dst = $this->decode($testStat['hash']); |
||
4060 | |||
4061 | foreach ($this->getScandir($src) as $stat) { |
||
4062 | if (empty($stat['hidden'])) { |
||
4063 | $name = $stat['name']; |
||
4064 | $_src = $this->decode($stat['hash']); |
||
4065 | if (! $this->copy($_src, $dst, $name)) { |
||
4066 | $this->remove($dst, true); // fall back |
||
4067 | return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src)); |
||
4068 | } |
||
4069 | } |
||
4070 | } |
||
4071 | |||
4072 | $this->added[] = $testStat; |
||
4073 | |||
4074 | return $dst; |
||
4075 | } |
||
4076 | |||
4077 | if ($this->options['copyJoin']) { |
||
4078 | $test = $this->joinPathCE($dst, $name); |
||
4079 | if ($testStat = $this->stat($test)) { |
||
4080 | $this->remove($test); |
||
4081 | } |
||
4082 | } else { |
||
4083 | $testStat = false; |
||
4084 | } |
||
4085 | if ($res = $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))) { |
||
4086 | $path = is_string($res)? $res : $this->joinPathCE($dst, $name); |
||
4087 | $this->clearcache(); |
||
4088 | $this->added[] = $this->stat($path); |
||
4089 | return $path; |
||
4090 | } |
||
4091 | |||
4092 | return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash'])); |
||
4093 | } |
||
4094 | |||
4095 | /** |
||
4096 | * Move file |
||
4097 | * Return new file path or false. |
||
4098 | * |
||
4099 | * @param string $src source path |
||
4100 | * @param string $dst destination dir path |
||
4101 | * @param string $name new file name |
||
4102 | * @return string|false |
||
4103 | * @author Dmitry (dio) Levashov |
||
4104 | **/ |
||
4105 | protected function move($src, $dst, $name) { |
||
4106 | $stat = $this->stat($src); |
||
4107 | $stat['realpath'] = $src; |
||
4108 | $this->rmTmb($stat); // can not do rmTmb() after _move() |
||
4109 | $this->clearcache(); |
||
4110 | |||
4111 | if ($res = $this->convEncOut($this->_move($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))) { |
||
4112 | $this->removed[] = $stat; |
||
4113 | return is_string($res)? $res : $this->joinPathCE($dst, $name); |
||
4114 | } |
||
4115 | |||
4116 | return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash'])); |
||
4117 | } |
||
4118 | |||
4119 | /** |
||
4120 | * Copy file from another volume. |
||
4121 | * Return new file path or false. |
||
4122 | * |
||
4123 | * @param Object $volume source volume |
||
4124 | * @param string $src source file hash |
||
4125 | * @param string $destination destination dir path |
||
4126 | * @param string $name file name |
||
4127 | * @return string|false |
||
4128 | * @author Dmitry (dio) Levashov |
||
4129 | **/ |
||
4130 | protected function copyFrom($volume, $src, $destination, $name) { |
||
4131 | |||
4132 | elFinder::extendTimeLimit(); |
||
4133 | |||
4134 | if (($source = $volume->file($src)) == false) { |
||
4135 | return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error()); |
||
4136 | } |
||
4137 | |||
4138 | $errpath = $volume->path($source['hash']); |
||
4139 | |||
4140 | if (!$this->nameAccepted($source['name'])) { |
||
4141 | return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME); |
||
4142 | } |
||
4143 | |||
4144 | if (!$source['read']) { |
||
4145 | return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED); |
||
4146 | } |
||
4147 | |||
4148 | if ($source['mime'] == 'directory') { |
||
4149 | $test = $this->stat($this->joinPathCE($destination, $name)); |
||
4150 | $this->clearcache(); |
||
4151 | |||
4152 | if (($test && $test['mime'] != 'directory') || (! $test && ! $test = $this->mkdir($this->encode($destination), $name))) { |
||
4153 | return $this->setError(elFinder::ERROR_COPY, $errpath); |
||
4154 | } |
||
4155 | |||
4156 | $path = $this->joinPathCE($destination, $name); |
||
4157 | $path = $this->decode($test['hash']); |
||
4158 | |||
4159 | foreach ($volume->scandir($src) as $entr) { |
||
4160 | if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) { |
||
4161 | $this->remove($path, true); // fall back |
||
4162 | return $this->setError($this->error, elFinder::ERROR_COPY, $errpath); |
||
4163 | } |
||
4164 | } |
||
4165 | |||
4166 | $this->added[] = $test; |
||
4167 | } else { |
||
4168 | // $mime = $source['mime']; |
||
4169 | // $w = $h = 0; |
||
4170 | if (($dim = $volume->dimensions($src))) { |
||
4171 | $s = explode('x', $dim); |
||
4172 | $source['width'] = $s[0]; |
||
4173 | $source['height'] = $s[1]; |
||
4174 | } |
||
4175 | |||
4176 | if (($fp = $volume->open($src)) == false |
||
4177 | || ($path = $this->saveCE($fp, $destination, $name, $source)) == false) { |
||
4178 | $fp && $volume->close($fp, $src); |
||
4179 | return $this->setError(elFinder::ERROR_COPY, $errpath); |
||
4180 | } |
||
4181 | $volume->close($fp, $src); |
||
4182 | |||
4183 | // MIME check |
||
4184 | $stat = $this->stat($path); |
||
4185 | $mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($stat['name']); |
||
4186 | if ($stat['mime'] === $mimeByName) { |
||
4187 | $mimeByName = ''; |
||
4188 | } |
||
4189 | if (!$this->allowPutMime($stat['mime']) || ($mimeByName && $mimeByName !== 'unknown' && !$this->allowPutMime($mimeByName))) { |
||
4190 | $this->remove($path, true); |
||
4191 | return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath); |
||
4192 | } |
||
4193 | |||
4194 | $this->added[] = $stat; |
||
4195 | } |
||
4196 | |||
4197 | return $path; |
||
4198 | } |
||
4199 | |||
4200 | /** |
||
4201 | * Remove file/ recursive remove dir |
||
4202 | * |
||
4203 | * @param string $path file path |
||
4204 | * @param bool $force try to remove even if file locked |
||
4205 | * @return bool |
||
4206 | * @author Dmitry (dio) Levashov |
||
4207 | **/ |
||
4208 | protected function remove($path, $force = false) { |
||
4209 | $stat = $this->stat($path); |
||
4210 | |||
4211 | if (empty($stat)) { |
||
4212 | return $this->setError(elFinder::ERROR_RM, $path, elFinder::ERROR_FILE_NOT_FOUND); |
||
4213 | } |
||
4214 | |||
4215 | $stat['realpath'] = $path; |
||
4216 | $this->rmTmb($stat); |
||
4217 | $this->clearcache(); |
||
4218 | |||
4219 | if (!$force && !empty($stat['locked'])) { |
||
4220 | return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash'])); |
||
4221 | } |
||
4222 | |||
4223 | if ($stat['mime'] == 'directory' && empty($stat['thash'])) { |
||
4224 | $ret = $this->delTree($this->convEncIn($path)); |
||
4225 | $this->convEncOut(); |
||
4226 | if (!$ret) { |
||
4227 | return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash'])); |
||
4228 | } |
||
4229 | } else { |
||
4230 | if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) { |
||
4231 | return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash'])); |
||
4232 | } |
||
4233 | } |
||
4234 | |||
4235 | $this->removed[] = $stat; |
||
4236 | return true; |
||
4237 | } |
||
4238 | |||
4239 | |||
4240 | /************************* thumbnails **************************/ |
||
4241 | |||
4242 | /** |
||
4243 | * Return thumbnail file name for required file |
||
4244 | * |
||
4245 | * @param array $stat file stat |
||
4246 | * @return string |
||
4247 | * @author Dmitry (dio) Levashov |
||
4248 | **/ |
||
4249 | protected function tmbname($stat) { |
||
4250 | return $stat['hash'].$stat['ts'].'.png'; |
||
4251 | } |
||
4252 | |||
4253 | /** |
||
4254 | * Return thumnbnail name if exists |
||
4255 | * |
||
4256 | * @param string $path file path |
||
4257 | * @param array $stat file stat |
||
4258 | * @return string|false |
||
4259 | * @author Dmitry (dio) Levashov |
||
4260 | **/ |
||
4261 | protected function gettmb($path, $stat) { |
||
4262 | if ($this->tmbURL && $this->tmbPath) { |
||
4263 | // file itself thumnbnail |
||
4264 | if (strpos($path, $this->tmbPath) === 0) { |
||
4265 | return basename($path); |
||
4266 | } |
||
4267 | |||
4268 | $name = $this->tmbname($stat); |
||
4269 | if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) { |
||
4270 | return $name; |
||
4271 | } |
||
4272 | } |
||
4273 | return false; |
||
4274 | } |
||
4275 | |||
4276 | /** |
||
4277 | * Return true if thumnbnail for required file can be created |
||
4278 | * |
||
4279 | * @param string $path thumnbnail path |
||
4280 | * @param array $stat file stat |
||
4281 | * @param bool $checkTmbPath |
||
4282 | * @return string|bool |
||
4283 | * @author Dmitry (dio) Levashov |
||
4284 | **/ |
||
4285 | protected function canCreateTmb($path, $stat, $checkTmbPath = true) { |
||
4286 | if ((! $checkTmbPath || $this->tmbPathWritable) |
||
4287 | && (! $this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail |
||
4288 | ) { |
||
4289 | $mime = strtolower($stat['mime']); |
||
4290 | list($type) = explode('/', $mime); |
||
4291 | if (! empty($this->imgConverter)) { |
||
4292 | if (isset($this->imgConverter[$mime])) { |
||
4293 | return true; |
||
4294 | } |
||
4295 | if (isset($this->imgConverter[$type])) { |
||
4296 | return true; |
||
4297 | } |
||
4298 | } |
||
4299 | return $this->imgLib |
||
4300 | && ($type === 'image') |
||
4301 | && ($this->imgLib == 'gd' ? in_array($stat['mime'], array('image/jpeg', 'image/png', 'image/gif', 'image/x-ms-bmp')) : true); |
||
4302 | } |
||
4303 | return false; |
||
4304 | } |
||
4305 | |||
4306 | /** |
||
4307 | * Return true if required file can be resized. |
||
4308 | * By default - the same as canCreateTmb |
||
4309 | * |
||
4310 | * @param string $path thumnbnail path |
||
4311 | * @param array $stat file stat |
||
4312 | * @return string|bool |
||
4313 | * @author Dmitry (dio) Levashov |
||
4314 | **/ |
||
4315 | protected function canResize($path, $stat) { |
||
4316 | return $this->canCreateTmb($path, $stat, false); |
||
4317 | } |
||
4318 | |||
4319 | /** |
||
4320 | * Create thumnbnail and return it's URL on success |
||
4321 | * |
||
4322 | * @param string $path file path |
||
4323 | * @param $stat |
||
4324 | * @return false|string |
||
4325 | * @internal param string $mime file mime type |
||
4326 | * @author Dmitry (dio) Levashov |
||
4327 | */ |
||
4328 | protected function createTmb($path, $stat) { |
||
4329 | if (!$stat || !$this->canCreateTmb($path, $stat)) { |
||
4330 | return false; |
||
4331 | } |
||
4332 | |||
4333 | $name = $this->tmbname($stat); |
||
4334 | $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$name; |
||
4335 | |||
4336 | $maxlength = -1; |
||
4337 | $imgConverter = null; |
||
4338 | |||
4339 | // check imgConverter |
||
4340 | $mime = strtolower($stat['mime']); |
||
4341 | list($type) = explode('/', $mime); |
||
4342 | if (isset($this->imgConverter[$mime])) { |
||
4343 | $imgConverter = $this->imgConverter[$mime]['func']; |
||
4344 | if (! empty($this->imgConverter[$mime]['maxlen'])) { |
||
4345 | $maxlength = intval($this->imgConverter[$mime]['maxlen']); |
||
4346 | } |
||
4347 | } else if (isset($this->imgConverter[$type])) { |
||
4348 | $imgConverter = $this->imgConverter[$type]['func']; |
||
4349 | if (! empty($this->imgConverter[$type]['maxlen'])) { |
||
4350 | $maxlength = intval($this->imgConverter[$type]['maxlen']); |
||
4351 | } |
||
4352 | } |
||
4353 | if ($imgConverter && ! is_callable($imgConverter)) { |
||
4354 | return false; |
||
4355 | } |
||
4356 | |||
4357 | // copy image into tmbPath so some drivers does not store files on local fs |
||
4358 | if (($src = $this->fopenCE($path, 'rb')) == false) { |
||
4359 | return false; |
||
4360 | } |
||
4361 | |||
4362 | if (($trg = fopen($tmb, 'wb')) == false) { |
||
4363 | $this->fcloseCE($src, $path); |
||
4364 | return false; |
||
4365 | } |
||
4366 | |||
4367 | stream_copy_to_stream($src, $trg, $maxlength); |
||
4368 | |||
4369 | $this->fcloseCE($src, $path); |
||
4370 | fclose($trg); |
||
4371 | |||
4372 | // call imgConverter |
||
4373 | if ($imgConverter) { |
||
4374 | if (! call_user_func_array($imgConverter, array($tmb, $stat, $this))) { |
||
4375 | file_exists($tmb) && unlink($tmb); |
||
4376 | return false; |
||
4377 | } |
||
4378 | } |
||
4379 | |||
4380 | $result = false; |
||
4381 | |||
4382 | $tmbSize = $this->tmbSize; |
||
4383 | |||
4384 | if ($this->imgLib === 'imagick') { |
||
4385 | try { |
||
4386 | $imagickTest = new imagick($tmb); |
||
4387 | $imagickTest->clear(); |
||
4388 | $imagickTest = true; |
||
4389 | } catch (Exception $e) { |
||
4390 | $imagickTest = false; |
||
4391 | } |
||
4392 | } |
||
4393 | |||
4394 | if (($this->imgLib === 'imagick' && ! $imagickTest) || ($s = getimagesize($tmb)) === false) { |
||
4395 | if ($this->imgLib === 'imagick') { |
||
4396 | $bgcolor = $this->options['tmbBgColor']; |
||
4397 | if ($bgcolor === 'transparent') { |
||
4398 | $bgcolor = 'rgba(255, 255, 255, 0.0)'; |
||
4399 | } |
||
4400 | try { |
||
4401 | $imagick = new imagick(); |
||
4402 | $imagick->setBackgroundColor(new ImagickPixel($bgcolor)); |
||
4403 | $imagick->readImage($this->getExtentionByMime($stat['mime'], ':') . $tmb); |
||
4404 | $imagick->setImageFormat('png'); |
||
4405 | $imagick->writeImage($tmb); |
||
4406 | $imagick->clear(); |
||
4407 | if (($s = getimagesize($tmb)) !== false) { |
||
4408 | $result = true; |
||
4409 | } |
||
4410 | } catch (Exception $e) {} |
||
4411 | } |
||
4412 | if (! $result) { |
||
4413 | file_exists($tmb) && unlink($tmb); |
||
4414 | return false; |
||
4415 | } |
||
4416 | $result = false; |
||
4417 | } |
||
4418 | |||
4419 | /* If image smaller or equal thumbnail size - just fitting to thumbnail square */ |
||
4420 | if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) { |
||
4421 | $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' ); |
||
4422 | } else { |
||
4423 | |||
4424 | if ($this->options['tmbCrop']) { |
||
4425 | |||
4426 | $result = $tmb; |
||
4427 | /* Resize and crop if image bigger than thumbnail */ |
||
4428 | if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize) ) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) { |
||
4429 | $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png'); |
||
4430 | } |
||
4431 | |||
4432 | if ($result && ($s = getimagesize($tmb)) != false) { |
||
4433 | $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0; |
||
4434 | $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0; |
||
4435 | $result = $this->imgCrop($result, $tmbSize, $tmbSize, $x, $y, 'png'); |
||
4436 | } else { |
||
4437 | $result = false; |
||
4438 | } |
||
4439 | |||
4440 | } else { |
||
4441 | $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png'); |
||
4442 | } |
||
4443 | |||
4444 | if ($result) { |
||
4445 | if ($s = getimagesize($tmb)) { |
||
4446 | if ($s[0] !== $tmbSize || $s[1] !== $tmbSize) { |
||
4447 | $result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' ); |
||
4448 | } |
||
4449 | } |
||
4450 | } |
||
4451 | } |
||
4452 | |||
4453 | if (!$result) { |
||
4454 | unlink($tmb); |
||
4455 | return false; |
||
4456 | } |
||
4457 | |||
4458 | return $name; |
||
4459 | } |
||
4460 | |||
4461 | /** |
||
4462 | * Resize image |
||
4463 | * |
||
4464 | * @param string $path image file |
||
4465 | * @param int $width new width |
||
4466 | * @param int $height new height |
||
4467 | * @param bool $keepProportions crop image |
||
4468 | * @param bool $resizeByBiggerSide resize image based on bigger side if true |
||
4469 | * @param string $destformat image destination format |
||
4470 | * @param int $jpgQuality JEPG quality (1-100) |
||
4471 | * @param array $options Other extra options |
||
4472 | * @return string|false |
||
4473 | * @author Dmitry (dio) Levashov |
||
4474 | * @author Alexey Sukhotin |
||
4475 | **/ |
||
4476 | protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null, $jpgQuality = null, $options = array()) { |
||
4477 | if (($s = getimagesize($path)) == false) { |
||
4478 | return false; |
||
4479 | } |
||
4480 | |||
4481 | $result = false; |
||
4482 | |||
4483 | list($size_w, $size_h) = array($width, $height); |
||
4484 | |||
4485 | if (!$jpgQuality) { |
||
4486 | $jpgQuality = $this->options['jpgQuality']; |
||
4487 | } |
||
4488 | |||
4489 | if ($keepProportions == true) { |
||
4490 | |||
4491 | list($orig_w, $orig_h) = array($s[0], $s[1]); |
||
4492 | |||
4493 | /* Resizing by biggest side */ |
||
4494 | if ($resizeByBiggerSide) { |
||
4495 | if ($orig_w > $orig_h) { |
||
4496 | $size_h = round($orig_h * $width / $orig_w); |
||
4497 | $size_w = $width; |
||
4498 | } else { |
||
4499 | $size_w = round($orig_w * $height / $orig_h); |
||
4500 | $size_h = $height; |
||
4501 | } |
||
4502 | } else { |
||
4503 | if ($orig_w > $orig_h) { |
||
4504 | $size_w = round($orig_w * $height / $orig_h); |
||
4505 | $size_h = $height; |
||
4506 | } else { |
||
4507 | $size_h = round($orig_h * $width / $orig_w); |
||
4508 | $size_w = $width; |
||
4509 | } |
||
4510 | } |
||
4511 | } |
||
4512 | |||
4513 | elFinder::extendTimeLimit(300); |
||
4514 | switch ($this->imgLib) { |
||
4515 | case 'imagick': |
||
4516 | |||
4517 | try { |
||
4518 | $img = new imagick($path); |
||
4519 | } catch (Exception $e) { |
||
4520 | return false; |
||
4521 | } |
||
4522 | |||
4523 | // Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb |
||
4524 | // resize bench: http://app-mgng.rhcloud.com/9 |
||
4525 | // resize sample: http://www.dylanbeattie.net/magick/filters/result.html |
||
4526 | $filter = ($destformat === 'png' /* createTmb */)? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS; |
||
4527 | |||
4528 | $ani = ($img->getNumberImages() > 1); |
||
4529 | if ($ani && is_null($destformat)) { |
||
4530 | $img = $img->coalesceImages(); |
||
4531 | do { |
||
4532 | $img->resizeImage($size_w, $size_h, $filter, 1); |
||
4533 | } while ($img->nextImage()); |
||
4534 | $img = $img->optimizeImageLayers(); |
||
4535 | $result = $img->writeImages($path, true); |
||
4536 | } else { |
||
4537 | if ($ani) { |
||
4538 | $img->setFirstIterator(); |
||
4539 | } |
||
4540 | if (strtoupper($img->getImageFormat()) === 'JPEG') { |
||
4541 | $img->setImageCompression(imagick::COMPRESSION_JPEG); |
||
4542 | $img->setImageCompressionQuality($jpgQuality); |
||
4543 | if (isset($options['preserveExif']) && ! $options['preserveExif']) { |
||
4544 | try { |
||
4545 | $orientation = $img->getImageOrientation(); |
||
4546 | } catch (ImagickException $e) { |
||
4547 | $orientation = 0; |
||
4548 | } |
||
4549 | $img->stripImage(); |
||
4550 | if ($orientation) { |
||
4551 | $img->setImageOrientation($orientation); |
||
4552 | } |
||
4553 | } |
||
4554 | } |
||
4555 | $img->resizeImage($size_w, $size_h, $filter, true); |
||
4556 | if ($destformat) { |
||
4557 | $result = $this->imagickImage($img, $path, $destformat, $jpgQuality); |
||
4558 | } else { |
||
4559 | $result = $img->writeImage($path); |
||
4560 | } |
||
4561 | } |
||
4562 | |||
4563 | $img->clear(); |
||
4564 | |||
4565 | return $result ? $path : false; |
||
4566 | |||
4567 | break; |
||
4568 | |||
4569 | case 'convert': |
||
4570 | extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s)); |
||
4571 | $filter = ($destformat === 'png' /* createTmb */)? '-filter Box' : '-filter Lanczos'; |
||
4572 | $strip = (isset($options['preserveExif']) && ! $options['preserveExif'])? ' -strip' : ''; |
||
4573 | //$cmd = sprintf('convert%s%s +repage %s -resize %dx%d +repage%s %s %s', $coalesce, $jpgQuality, $filter, $size_w, $size_h, $deconstruct, $quotedPath, $quotedDstPath); |
||
4574 | $cmd = sprintf('convert %s%s%s %s -geometry %dx%d! %s %s', $quotedPath, $coalesce, $jpgQuality, $filter, $size_w, $size_h, $deconstruct, $quotedDstPath); |
||
4575 | |||
4576 | $result = false; |
||
4577 | if ($this->procExec($cmd) === 0) { |
||
4578 | $result = true; |
||
4579 | } |
||
4580 | return $result ? $path : false; |
||
4581 | |||
4582 | break; |
||
4583 | |||
4584 | case 'gd': |
||
4585 | $img = $this->gdImageCreate($path,$s['mime']); |
||
4586 | |||
4587 | if ($img && false != ($tmp = imagecreatetruecolor($size_w, $size_h))) { |
||
4588 | |||
4589 | $bgNum = false; |
||
4590 | if ($s[2] === IMAGETYPE_GIF && (! $destformat || $destformat === 'gif')) { |
||
4591 | $bgIdx = imagecolortransparent($img); |
||
4592 | if ($bgIdx !== -1) { |
||
4593 | $c = imagecolorsforindex($img, $bgIdx); |
||
4594 | $bgNum = imagecolorallocate($tmp, $c['red'], $c['green'], $c['blue']); |
||
4595 | imagefill($tmp, 0, 0, $bgNum); |
||
4596 | imagecolortransparent($tmp, $bgNum); |
||
4597 | } |
||
4598 | } |
||
4599 | if ($bgNum === false) { |
||
4600 | $this->gdImageBackground($tmp, 'transparent'); |
||
4601 | } |
||
4602 | |||
4603 | if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) { |
||
4604 | return false; |
||
4605 | } |
||
4606 | |||
4607 | $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality); |
||
4608 | |||
4609 | imagedestroy($img); |
||
4610 | imagedestroy($tmp); |
||
4611 | |||
4612 | return $result ? $path : false; |
||
4613 | |||
4614 | } |
||
4615 | break; |
||
4616 | } |
||
4617 | |||
4618 | return false; |
||
4619 | } |
||
4620 | |||
4621 | /** |
||
4622 | * Crop image |
||
4623 | * |
||
4624 | * @param string $path image file |
||
4625 | * @param int $width crop width |
||
4626 | * @param int $height crop height |
||
4627 | * @param bool $x crop left offset |
||
4628 | * @param bool $y crop top offset |
||
4629 | * @param string $destformat image destination format |
||
4630 | * @param int $jpgQuality JEPG quality (1-100) |
||
4631 | * @return string|false |
||
4632 | * @author Dmitry (dio) Levashov |
||
4633 | * @author Alexey Sukhotin |
||
4634 | **/ |
||
4635 | protected function imgCrop($path, $width, $height, $x, $y, $destformat = null, $jpgQuality = null) { |
||
4636 | if (($s = getimagesize($path)) == false) { |
||
4637 | return false; |
||
4638 | } |
||
4639 | |||
4640 | $result = false; |
||
4641 | |||
4642 | if (!$jpgQuality) { |
||
4643 | $jpgQuality = $this->options['jpgQuality']; |
||
4644 | } |
||
4645 | |||
4646 | elFinder::extendTimeLimit(300); |
||
4647 | switch ($this->imgLib) { |
||
4648 | case 'imagick': |
||
4649 | |||
4650 | try { |
||
4651 | $img = new imagick($path); |
||
4652 | } catch (Exception $e) { |
||
4653 | return false; |
||
4654 | } |
||
4655 | |||
4656 | $ani = ($img->getNumberImages() > 1); |
||
4657 | if ($ani && is_null($destformat)) { |
||
4658 | $img = $img->coalesceImages(); |
||
4659 | do { |
||
4660 | $img->setImagePage($s[0], $s[1], 0, 0); |
||
4661 | $img->cropImage($width, $height, $x, $y); |
||
4662 | $img->setImagePage($width, $height, 0, 0); |
||
4663 | } while ($img->nextImage()); |
||
4664 | $img = $img->optimizeImageLayers(); |
||
4665 | $result = $img->writeImages($path, true); |
||
4666 | } else { |
||
4667 | if ($ani) { |
||
4668 | $img->setFirstIterator(); |
||
4669 | } |
||
4670 | $img->setImagePage($s[0], $s[1], 0, 0); |
||
4671 | $img->cropImage($width, $height, $x, $y); |
||
4672 | $img->setImagePage($width, $height, 0, 0); |
||
4673 | $result = $this->imagickImage($img, $path, $destformat, $jpgQuality); |
||
4674 | } |
||
4675 | |||
4676 | $img->clear(); |
||
4677 | |||
4678 | return $result ? $path : false; |
||
4679 | |||
4680 | break; |
||
4681 | |||
4682 | case 'convert': |
||
4683 | extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s)); |
||
4684 | $cmd = sprintf('convert %s%s%s -crop %dx%d+%d+%d%s %s', $quotedPath, $coalesce, $jpgQuality, $width, $height, $x, $y, $deconstruct, $quotedDstPath); |
||
4685 | |||
4686 | $result = false; |
||
4687 | if ($this->procExec($cmd) === 0) { |
||
4688 | $result = true; |
||
4689 | } |
||
4690 | return $result ? $path : false; |
||
4691 | |||
4692 | break; |
||
4693 | |||
4694 | case 'gd': |
||
4695 | $img = $this->gdImageCreate($path,$s['mime']); |
||
4696 | |||
4697 | if ($img && false != ($tmp = imagecreatetruecolor($width, $height))) { |
||
4698 | |||
4699 | $bgNum = false; |
||
4700 | if ($s[2] === IMAGETYPE_GIF && (! $destformat || $destformat === 'gif')) { |
||
4701 | $bgIdx = imagecolortransparent($img); |
||
4702 | if ($bgIdx !== -1) { |
||
4703 | $c = imagecolorsforindex($img, $bgIdx); |
||
4704 | $bgNum = imagecolorallocate($tmp, $c['red'], $c['green'], $c['blue']); |
||
4705 | imagefill($tmp, 0, 0, $bgNum); |
||
4706 | imagecolortransparent($tmp, $bgNum); |
||
4707 | } |
||
4708 | } |
||
4709 | if ($bgNum === false) { |
||
4710 | $this->gdImageBackground($tmp, 'transparent'); |
||
4711 | } |
||
4712 | |||
4713 | $size_w = $width; |
||
4714 | $size_h = $height; |
||
4715 | |||
4716 | if ($s[0] < $width || $s[1] < $height) { |
||
4717 | $size_w = $s[0]; |
||
4718 | $size_h = $s[1]; |
||
4719 | } |
||
4720 | |||
4721 | if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) { |
||
4722 | return false; |
||
4723 | } |
||
4724 | |||
4725 | $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality); |
||
4726 | |||
4727 | imagedestroy($img); |
||
4728 | imagedestroy($tmp); |
||
4729 | |||
4730 | return $result ? $path : false; |
||
4731 | |||
4732 | } |
||
4733 | break; |
||
4734 | } |
||
4735 | |||
4736 | return false; |
||
4737 | } |
||
4738 | |||
4739 | /** |
||
4740 | * Put image to square |
||
4741 | * |
||
4742 | * @param string $path image file |
||
4743 | * @param int $width square width |
||
4744 | * @param int $height square height |
||
4745 | * @param int|string $align reserved |
||
4746 | * @param int|string $valign reserved |
||
4747 | * @param string $bgcolor square background color in #rrggbb format |
||
4748 | * @param string $destformat image destination format |
||
4749 | * @param int $jpgQuality JEPG quality (1-100) |
||
4750 | * @return false|string |
||
4751 | * @author Dmitry (dio) Levashov |
||
4752 | * @author Alexey Sukhotin |
||
4753 | */ |
||
4754 | protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null, $jpgQuality = null) { |
||
4755 | if (($s = getimagesize($path)) == false) { |
||
4756 | return false; |
||
4757 | } |
||
4758 | |||
4759 | $result = false; |
||
4760 | |||
4761 | /* Coordinates for image over square aligning */ |
||
4762 | $y = ceil(abs($height - $s[1]) / 2); |
||
4763 | $x = ceil(abs($width - $s[0]) / 2); |
||
4764 | |||
4765 | if (!$jpgQuality) { |
||
4766 | $jpgQuality = $this->options['jpgQuality']; |
||
4767 | } |
||
4768 | |||
4769 | elFinder::extendTimeLimit(300); |
||
4770 | switch ($this->imgLib) { |
||
4771 | case 'imagick': |
||
4772 | try { |
||
4773 | $img = new imagick($path); |
||
4774 | } catch (Exception $e) { |
||
4775 | return false; |
||
4776 | } |
||
4777 | |||
4778 | if ($bgcolor === 'transparent') { |
||
4779 | $bgcolor = 'rgba(255, 255, 255, 0.0)'; |
||
4780 | } |
||
4781 | $ani = ($img->getNumberImages() > 1); |
||
4782 | if ($ani && is_null($destformat)) { |
||
4783 | $img1 = new Imagick(); |
||
4784 | $img1->setFormat('gif'); |
||
4785 | $img = $img->coalesceImages(); |
||
4786 | do { |
||
4787 | $gif = new Imagick(); |
||
4788 | $gif->newImage($width, $height, new ImagickPixel($bgcolor)); |
||
4789 | $gif->setImageColorspace($img->getImageColorspace()); |
||
4790 | $gif->setImageFormat('gif'); |
||
4791 | $gif->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y ); |
||
4792 | $gif->setImageDelay($img->getImageDelay()); |
||
4793 | $gif->setImageIterations($img->getImageIterations()); |
||
4794 | $img1->addImage($gif); |
||
4795 | $gif->clear(); |
||
4796 | } while ($img->nextImage()); |
||
4797 | $img1 = $img1->optimizeImageLayers(); |
||
4798 | $result = $img1->writeImages($path, true); |
||
4799 | } else { |
||
4800 | if ($ani) { |
||
4801 | $img->setFirstIterator(); |
||
4802 | } |
||
4803 | $img1 = new Imagick(); |
||
4804 | $img1->newImage($width, $height, new ImagickPixel($bgcolor)); |
||
4805 | $img1->setImageColorspace($img->getImageColorspace()); |
||
4806 | $img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y ); |
||
4807 | $result = $this->imagickImage($img1, $path, $destformat, $jpgQuality); |
||
4808 | } |
||
4809 | |||
4810 | $img1->clear(); |
||
4811 | $img->clear(); |
||
4812 | return $result ? $path : false; |
||
4813 | |||
4814 | break; |
||
4815 | |||
4816 | case 'convert': |
||
4817 | extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s)); |
||
4818 | if ($bgcolor === 'transparent') { |
||
4819 | $bgcolor = 'rgba(255, 255, 255, 0.0)'; |
||
4820 | } |
||
4821 | $cmd = sprintf('convert -size %dx%d "xc:%s" png:- | convert%s%s png:- %s -geometry +%d+%d -compose over -composite%s %s', $width, $height, $bgcolor, $coalesce, $jpgQuality, $quotedPath, $x, $y, $deconstruct, $quotedDstPath); |
||
4822 | |||
4823 | $result = false; |
||
4824 | if ($this->procExec($cmd) === 0) { |
||
4825 | $result = true; |
||
4826 | } |
||
4827 | return $result ? $path : false; |
||
4828 | |||
4829 | break; |
||
4830 | |||
4831 | case 'gd': |
||
4832 | $img = $this->gdImageCreate($path,$s['mime']); |
||
4833 | |||
4834 | if ($img && false != ($tmp = imagecreatetruecolor($width, $height))) { |
||
4835 | |||
4836 | $this->gdImageBackground($tmp, $bgcolor); |
||
4837 | if ($bgcolor === 'transparent' && ($destformat === 'png' || $s[2] === IMAGETYPE_PNG)) { |
||
4838 | $bgNum = imagecolorallocatealpha($tmp, 255, 255, 255, 127); |
||
4839 | imagefill($tmp, 0, 0, $bgNum); |
||
4840 | } |
||
4841 | |||
4842 | if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) { |
||
4843 | return false; |
||
4844 | } |
||
4845 | |||
4846 | $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality); |
||
4847 | |||
4848 | imagedestroy($img); |
||
4849 | imagedestroy($tmp); |
||
4850 | |||
4851 | return $result ? $path : false; |
||
4852 | } |
||
4853 | break; |
||
4854 | } |
||
4855 | |||
4856 | return false; |
||
4857 | } |
||
4858 | |||
4859 | /** |
||
4860 | * Rotate image |
||
4861 | * |
||
4862 | * @param string $path image file |
||
4863 | * @param int $degree rotete degrees |
||
4864 | * @param string $bgcolor square background color in #rrggbb format |
||
4865 | * @param string $destformat image destination format |
||
4866 | * @param int $jpgQuality JEPG quality (1-100) |
||
4867 | * @return string|false |
||
4868 | * @author nao-pon |
||
4869 | * @author Troex Nevelin |
||
4870 | **/ |
||
4871 | protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null, $jpgQuality = null) { |
||
4872 | if (($s = getimagesize($path)) == false || $degree % 360 === 0) { |
||
4873 | return false; |
||
4874 | } |
||
4875 | |||
4876 | $result = false; |
||
4877 | |||
4878 | // try lossless rotate |
||
4879 | if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) { |
||
4880 | $count = ($degree / 90) % 4; |
||
4881 | $exiftran = array( |
||
4882 | 1 => '-9', |
||
4883 | 2 => '-1', |
||
4884 | 3 => '-2' |
||
4885 | ); |
||
4886 | $jpegtran = array( |
||
4887 | 1 => '90', |
||
4888 | 2 => '180', |
||
4889 | 3 => '270' |
||
4890 | ); |
||
4891 | $quotedPath = escapeshellarg($path); |
||
4892 | $cmds = array( |
||
4893 | 'exiftran -i '.$exiftran[$count].' '.$path, |
||
4894 | 'jpegtran -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath |
||
4895 | ); |
||
4896 | foreach($cmds as $cmd) { |
||
4897 | if ($this->procExec($cmd) === 0) { |
||
4898 | $result = true; |
||
4899 | break; |
||
4900 | } |
||
4901 | } |
||
4902 | if ($result) { |
||
4903 | return $path; |
||
4904 | } |
||
4905 | } |
||
4906 | |||
4907 | if (!$jpgQuality) { |
||
4908 | $jpgQuality = $this->options['jpgQuality']; |
||
4909 | } |
||
4910 | |||
4911 | elFinder::extendTimeLimit(300); |
||
4912 | switch ($this->imgLib) { |
||
4913 | case 'imagick': |
||
4914 | try { |
||
4915 | $img = new imagick($path); |
||
4916 | } catch (Exception $e) { |
||
4917 | return false; |
||
4918 | } |
||
4919 | |||
4920 | if ($s[2] === IMAGETYPE_GIF || $s[2] === IMAGETYPE_PNG) { |
||
4921 | $bgcolor = 'rgba(255, 255, 255, 0.0)'; |
||
4922 | } |
||
4923 | if ($img->getNumberImages() > 1) { |
||
4924 | $img = $img->coalesceImages(); |
||
4925 | do { |
||
4926 | $img->rotateImage(new ImagickPixel($bgcolor), $degree); |
||
4927 | } while ($img->nextImage()); |
||
4928 | $img = $img->optimizeImageLayers(); |
||
4929 | $result = $img->writeImages($path, true); |
||
4930 | } else { |
||
4931 | $img->rotateImage(new ImagickPixel($bgcolor), $degree); |
||
4932 | $result = $this->imagickImage($img, $path, $destformat, $jpgQuality); |
||
4933 | } |
||
4934 | $img->clear(); |
||
4935 | return $result ? $path : false; |
||
4936 | |||
4937 | break; |
||
4938 | |||
4939 | case 'convert': |
||
4940 | extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s)); |
||
4941 | if ($s[2] === IMAGETYPE_GIF || $s[2] === IMAGETYPE_PNG) { |
||
4942 | $bgcolor = 'rgba(255, 255, 255, 0.0)'; |
||
4943 | } |
||
4944 | $cmd = sprintf('convert %s%s%s -background "%s" -rotate %d%s %s', $quotedPath, $coalesce, $jpgQuality, $bgcolor, $degree, $deconstruct, $quotedDstPath); |
||
4945 | |||
4946 | $result = false; |
||
4947 | if ($this->procExec($cmd) === 0) { |
||
4948 | $result = true; |
||
4949 | } |
||
4950 | return $result ? $path : false; |
||
4951 | |||
4952 | break; |
||
4953 | |||
4954 | case 'gd': |
||
4955 | $img = $this->gdImageCreate($path,$s['mime']); |
||
4956 | |||
4957 | $degree = 360 - $degree; |
||
4958 | |||
4959 | $bgNum = -1; |
||
4960 | $bgIdx = false; |
||
4961 | if ($s[2] === IMAGETYPE_GIF) { |
||
4962 | $bgIdx = imagecolortransparent($img); |
||
4963 | if ($bgIdx !== -1) { |
||
4964 | $c = imagecolorsforindex($img, $bgIdx); |
||
4965 | $w = imagesx($img); |
||
4966 | $h = imagesy($img); |
||
4967 | $newImg = imagecreatetruecolor($w, $h); |
||
4968 | imagepalettecopy($newImg, $img); |
||
4969 | $bgNum = imagecolorallocate($newImg, $c['red'], $c['green'], $c['blue']); |
||
4970 | imagefill($newImg, 0, 0, $bgNum); |
||
4971 | imagecolortransparent($newImg, $bgNum); |
||
4972 | imagecopy($newImg, $img, 0, 0, 0, 0, $w, $h); |
||
4973 | imagedestroy($img); |
||
4974 | $img = $newImg; |
||
4975 | $newImg = null; |
||
4976 | } |
||
4977 | } else if ($s[2] === IMAGETYPE_PNG) { |
||
4978 | $bgNum = imagecolorallocatealpha($img, 255, 255, 255, 127); |
||
4979 | } |
||
4980 | if ($bgNum === -1) { |
||
4981 | list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x"); |
||
4982 | $bgNum = imagecolorallocate($img, $r, $g, $b); |
||
4983 | } |
||
4984 | |||
4985 | $tmp = imageRotate($img, $degree, $bgNum); |
||
4986 | if ($bgIdx !== -1) { |
||
4987 | imagecolortransparent($tmp, $bgNum); |
||
4988 | } |
||
4989 | |||
4990 | $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality); |
||
4991 | |||
4992 | imageDestroy($img); |
||
4993 | imageDestroy($tmp); |
||
4994 | |||
4995 | return $result ? $path : false; |
||
4996 | |||
4997 | break; |
||
4998 | } |
||
4999 | |||
5000 | return false; |
||
5001 | } |
||
5002 | |||
5003 | /** |
||
5004 | * Execute shell command |
||
5005 | * |
||
5006 | * @param string $command command line |
||
5007 | * @param array $output stdout strings |
||
5008 | * @param array|int $return_var process exit code |
||
5009 | * @param array $error_output stderr strings |
||
5010 | * @return int exit code |
||
5011 | * @author Alexey Sukhotin |
||
5012 | */ |
||
5013 | protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) { |
||
5014 | |||
5015 | static $allowed = null; |
||
5016 | |||
5017 | if ($allowed === null) { |
||
5018 | if ($allowed = function_exists('proc_open')) { |
||
5019 | if ($disabled = ini_get('disable_functions')) { |
||
5020 | $funcs = array_map('trim', explode(',', $disabled)); |
||
5021 | $allowed = ! in_array('proc_open', $funcs); |
||
5022 | } |
||
5023 | } |
||
5024 | } |
||
5025 | |||
5026 | if (! $allowed) { |
||
5027 | $return_var = -1; |
||
5028 | return $return_var; |
||
5029 | } |
||
5030 | |||
5031 | if (! $command) { |
||
5032 | $return_var = 0; |
||
5033 | return $return_var; |
||
5034 | } |
||
5035 | |||
5036 | $descriptorspec = array( |
||
5037 | 0 => array("pipe", "r"), // stdin |
||
5038 | 1 => array("pipe", "w"), // stdout |
||
5039 | 2 => array("pipe", "w") // stderr |
||
5040 | ); |
||
5041 | |||
5042 | $process = proc_open($command, $descriptorspec, $pipes, null, null); |
||
5043 | |||
5044 | if (is_resource($process)) { |
||
5045 | |||
5046 | fclose($pipes[0]); |
||
5047 | |||
5048 | $tmpout = ''; |
||
5049 | $tmperr = ''; |
||
5050 | |||
5051 | $output = stream_get_contents($pipes[1]); |
||
5052 | $error_output = stream_get_contents($pipes[2]); |
||
5053 | |||
5054 | fclose($pipes[1]); |
||
5055 | fclose($pipes[2]); |
||
5056 | $return_var = proc_close($process); |
||
5057 | |||
5058 | } else { |
||
5059 | $return_var = -1; |
||
5060 | } |
||
5061 | |||
5062 | return $return_var; |
||
5063 | |||
5064 | } |
||
5065 | |||
5066 | /** |
||
5067 | * Remove thumbnail, also remove recursively if stat is directory |
||
5068 | * |
||
5069 | * @param string $stat file stat |
||
5070 | * @return void |
||
5071 | * @author Dmitry (dio) Levashov |
||
5072 | * @author Naoki Sawada |
||
5073 | * @author Troex Nevelin |
||
5074 | **/ |
||
5075 | protected function rmTmb($stat) { |
||
5076 | if ($stat['mime'] === 'directory') { |
||
5077 | foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) { |
||
5078 | elFinder::extendTimeLimit(30); |
||
5079 | $name = $this->basenameCE($p); |
||
5080 | $name != '.' && $name != '..' && $this->rmTmb($this->stat($p)); |
||
5081 | } |
||
5082 | } else if (!empty($stat['tmb']) && $stat['tmb'] != "1") { |
||
5083 | $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb']; |
||
5084 | file_exists($tmb) && unlink($tmb); |
||
5085 | clearstatcache(); |
||
5086 | } |
||
5087 | } |
||
5088 | |||
5089 | /** |
||
5090 | * Create an gd image according to the specified mime type |
||
5091 | * |
||
5092 | * @param string $path image file |
||
5093 | * @param string $mime |
||
5094 | * @return gd image resource identifier |
||
5095 | */ |
||
5096 | protected function gdImageCreate($path,$mime){ |
||
5097 | switch($mime){ |
||
5098 | case 'image/jpeg': |
||
5099 | return imagecreatefromjpeg($path); |
||
5100 | |||
5101 | case 'image/png': |
||
5102 | return imagecreatefrompng($path); |
||
5103 | |||
5104 | case 'image/gif': |
||
5105 | return imagecreatefromgif($path); |
||
5106 | |||
5107 | case 'image/x-ms-bmp': |
||
5108 | if (!function_exists('imagecreatefrombmp')) { |
||
5109 | include_once dirname(__FILE__).'/libs/GdBmp.php'; |
||
5110 | } |
||
5111 | return imagecreatefrombmp($path); |
||
5112 | |||
5113 | case 'image/xbm': |
||
5114 | return imagecreatefromxbm($path); |
||
5115 | |||
5116 | case 'image/xpm': |
||
5117 | return imagecreatefromxpm($path); |
||
5118 | } |
||
5119 | return false; |
||
5120 | } |
||
5121 | |||
5122 | /** |
||
5123 | * Output gd image to file |
||
5124 | * |
||
5125 | * @param resource $image gd image resource |
||
5126 | * @param string $filename The path to save the file to. |
||
5127 | * @param string $destformat The Image type to use for $filename |
||
5128 | * @param string $mime The original image mime type |
||
5129 | * @param int $jpgQuality JEPG quality (1-100) |
||
5130 | * @return bool |
||
5131 | */ |
||
5132 | protected function gdImage($image, $filename, $destformat, $mime, $jpgQuality = null ){ |
||
5133 | |||
5134 | if (! $jpgQuality) { |
||
5135 | $jpgQuality = $this->options['jpgQuality']; |
||
5136 | } |
||
5137 | if ($destformat) { |
||
5138 | switch ($destformat) { |
||
5139 | case 'jpg': |
||
5140 | $mime = 'image/jpeg'; |
||
5141 | break; |
||
5142 | case 'gif': |
||
5143 | $mime = 'image/gif'; |
||
5144 | break; |
||
5145 | case 'png': |
||
5146 | default: |
||
5147 | $mime = 'image/png'; |
||
5148 | break; |
||
5149 | } |
||
5150 | } |
||
5151 | switch ($mime) { |
||
5152 | case 'image/gif': |
||
5153 | return imagegif($image, $filename); |
||
5154 | case 'image/jpeg': |
||
5155 | return imagejpeg($image, $filename, $jpgQuality); |
||
5156 | case 'image/wbmp': |
||
5157 | return imagewbmp($image, $filename); |
||
5158 | case 'image/png': |
||
5159 | default: |
||
5160 | return imagepng($image, $filename); |
||
5161 | } |
||
5162 | } |
||
5163 | |||
5164 | /** |
||
5165 | * Output imagick image to file |
||
5166 | * |
||
5167 | * @param resource $img imagick image resource |
||
5168 | * @param string $filename The path to save the file to. |
||
5169 | * @param string $destformat The Image type to use for $filename |
||
5170 | * @param int $jpgQuality JEPG quality (1-100) |
||
5171 | * @return bool |
||
5172 | */ |
||
5173 | protected function imagickImage($img, $filename, $destformat, $jpgQuality = null ){ |
||
5174 | |||
5175 | if (!$jpgQuality) { |
||
5176 | $jpgQuality = $this->options['jpgQuality']; |
||
5177 | } |
||
5178 | |||
5179 | try { |
||
5180 | if ($destformat) { |
||
5181 | if ($destformat === 'gif') { |
||
5182 | $img->setImageFormat('gif'); |
||
5183 | } else if ($destformat === 'png') { |
||
5184 | $img->setImageFormat('png'); |
||
5185 | } else if ($destformat === 'jpg') { |
||
5186 | $img->setImageFormat('jpeg'); |
||
5187 | } |
||
5188 | } |
||
5189 | if (strtoupper($img->getImageFormat()) === 'JPEG') { |
||
5190 | $img->setImageCompression(imagick::COMPRESSION_JPEG); |
||
5191 | $img->setImageCompressionQuality($jpgQuality); |
||
5192 | try { |
||
5193 | $orientation = $img->getImageOrientation(); |
||
5194 | } catch (ImagickException $e) { |
||
5195 | $orientation = 0; |
||
5196 | } |
||
5197 | $img->stripImage(); |
||
5198 | if ($orientation) { |
||
5199 | $img->setImageOrientation($orientation); |
||
5200 | } |
||
5201 | } |
||
5202 | $result = $img->writeImage($filename); |
||
5203 | } catch (Exception $e) { |
||
5204 | $result = false; |
||
5205 | } |
||
5206 | |||
5207 | return $result; |
||
5208 | } |
||
5209 | |||
5210 | /** |
||
5211 | * Assign the proper background to a gd image |
||
5212 | * |
||
5213 | * @param resource $image gd image resource |
||
5214 | * @param string $bgcolor background color in #rrggbb format |
||
5215 | */ |
||
5216 | protected function gdImageBackground($image, $bgcolor){ |
||
5217 | |||
5218 | if ($bgcolor === 'transparent'){ |
||
5219 | imagealphablending($image, false); |
||
5220 | imagesavealpha($image, true); |
||
5221 | } else { |
||
5222 | list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x"); |
||
5223 | $bgcolor1 = imagecolorallocate($image, $r, $g, $b); |
||
5224 | imagefill($image, 0, 0, $bgcolor1); |
||
5225 | } |
||
5226 | } |
||
5227 | |||
5228 | /** |
||
5229 | * Prepare variables for exec convert of ImageMagick |
||
5230 | * |
||
5231 | * @param string $path |
||
5232 | * @param string $destformat |
||
5233 | * @param int $jpgQuality |
||
5234 | * @param array $imageSize |
||
5235 | * @return array |
||
5236 | */ |
||
5237 | protected function imageMagickConvertPrepare($path, $destformat, $jpgQuality, $imageSize = null) { |
||
5238 | if (is_null($imageSize)) { |
||
5239 | $imageSize = getimagesize($path); |
||
5240 | } |
||
5241 | if (!$imageSize) { |
||
5242 | return array(); |
||
5243 | } |
||
5244 | $srcType = $this->getExtentionByMime($imageSize['mime'], ':'); |
||
5245 | $ani = false; |
||
5246 | $cmd = 'identify ' . escapeshellarg($srcType . $path); |
||
5247 | if ($this->procExec($cmd, $o) === 0) { |
||
5248 | $ani = preg_split('/(?:\r\n|\n|\r)/', trim($o)); |
||
5249 | if (count($ani) < 2) { |
||
5250 | $ani = false; |
||
5251 | } |
||
5252 | } |
||
5253 | $coalesce = $index = ''; |
||
5254 | $deconstruct = ' +repage'; |
||
5255 | if ($ani) { |
||
5256 | if (is_null($destformat)) { |
||
5257 | $coalesce = ' -coalesce -repage 0x0'; |
||
5258 | $deconstruct = ' +repage -deconstruct -layers optimize'; |
||
5259 | } else { |
||
5260 | $index = '[0]'; |
||
5261 | if ($srcType === 'ico:') { |
||
5262 | foreach($ani as $_i => $_info) { |
||
5263 | if (preg_match('/ (\d+)x(\d+) /', $_info, $m)) { |
||
5264 | if ($m[1] == $imageSize[0] && $m[2] == $imageSize[1]) { |
||
5265 | $index = '[' . $_i . ']'; |
||
5266 | break; |
||
5267 | } |
||
5268 | } |
||
5269 | } |
||
5270 | } |
||
5271 | } |
||
5272 | } |
||
5273 | if ($imageSize[2] === IMAGETYPE_JPEG || $imageSize[2] === IMAGETYPE_JPEG2000) { |
||
5274 | $jpgQuality = ' -quality ' . $jpgQuality; |
||
5275 | } else { |
||
5276 | $jpgQuality = ''; |
||
5277 | } |
||
5278 | $quotedPath = escapeshellarg($srcType . $path . $index); |
||
5279 | $quotedDstPath = escapeshellarg(($destformat? ($destformat . ':') : $srcType) . $path); |
||
5280 | return compact('ani', 'index', 'coalesce', 'deconstruct', 'jpgQuality', 'quotedPath', 'quotedDstPath'); |
||
5281 | } |
||
5282 | |||
5283 | /*********************** misc *************************/ |
||
5284 | |||
5285 | /** |
||
5286 | * Return smart formatted date |
||
5287 | * |
||
5288 | * @param int $ts file timestamp |
||
5289 | * @return string |
||
5290 | * @author Dmitry (dio) Levashov |
||
5291 | **/ |
||
5292 | // protected function formatDate($ts) { |
||
5293 | // if ($ts > $this->today) { |
||
5294 | // return 'Today '.date($this->options['timeFormat'], $ts); |
||
5295 | // } |
||
5296 | // |
||
5297 | // if ($ts > $this->yesterday) { |
||
5298 | // return 'Yesterday '.date($this->options['timeFormat'], $ts); |
||
5299 | // } |
||
5300 | // |
||
5301 | // return date($this->options['dateFormat'], $ts); |
||
5302 | // } |
||
5303 | |||
5304 | /** |
||
5305 | * Find position of first occurrence of string in a string with multibyte support |
||
5306 | * |
||
5307 | * @param string $haystack The string being checked. |
||
5308 | * @param string $needle The string to find in haystack. |
||
5309 | * @param int $offset The search offset. If it is not specified, 0 is used. |
||
5310 | * @return int|bool |
||
5311 | * @author Alexey Sukhotin |
||
5312 | **/ |
||
5313 | protected function stripos($haystack , $needle , $offset = 0) { |
||
5314 | if (function_exists('mb_stripos')) { |
||
5315 | return mb_stripos($haystack , $needle , $offset, 'UTF-8'); |
||
5316 | } else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) { |
||
5317 | return mb_strpos(mb_strtolower($haystack, 'UTF-8'), mb_strtolower($needle, 'UTF-8'), $offset); |
||
5318 | } |
||
5319 | return stripos($haystack , $needle , $offset); |
||
5320 | } |
||
5321 | |||
5322 | /** |
||
5323 | * Get server side available archivers |
||
5324 | * |
||
5325 | * @param bool $use_cache |
||
5326 | * @return array |
||
5327 | */ |
||
5328 | protected function getArchivers($use_cache = true) { |
||
5329 | |||
5330 | $sessionKey = 'ARCHIVERS_CACHE'; |
||
5331 | if ($use_cache && isset($this->sessionCache[$sessionKey]) && is_array($this->sessionCache[$sessionKey])) { |
||
5332 | return $this->sessionCache[$sessionKey]; |
||
5333 | } |
||
5334 | |||
5335 | $arcs = array( |
||
5336 | 'create' => array(), |
||
5337 | 'extract' => array() |
||
5338 | ); |
||
5339 | |||
5340 | if ($this->procExec('') === 0) { |
||
5341 | |||
5342 | $this->procExec('tar --version', $o, $ctar); |
||
5343 | |||
5344 | if ($ctar == 0) { |
||
5345 | $arcs['create']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar'); |
||
5346 | $arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar'); |
||
5347 | unset($o); |
||
5348 | $test = $this->procExec('gzip --version', $o, $c); |
||
5349 | if ($c == 0) { |
||
5350 | $arcs['create']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz'); |
||
5351 | $arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz'); |
||
5352 | } |
||
5353 | unset($o); |
||
5354 | $test = $this->procExec('bzip2 --version', $o, $c); |
||
5355 | if ($c == 0) { |
||
5356 | $arcs['create']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz'); |
||
5357 | $arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz'); |
||
5358 | } |
||
5359 | unset($o); |
||
5360 | $test = $this->procExec('xz --version', $o, $c); |
||
5361 | if ($c == 0) { |
||
5362 | $arcs['create']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz'); |
||
5363 | $arcs['extract']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz'); |
||
5364 | } |
||
5365 | } |
||
5366 | unset($o); |
||
5367 | $this->procExec('zip -v', $o, $c); |
||
5368 | if ($c == 0) { |
||
5369 | $arcs['create']['application/zip'] = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip'); |
||
5370 | } |
||
5371 | unset($o); |
||
5372 | $this->procExec('unzip --help', $o, $c); |
||
5373 | if ($c == 0) { |
||
5374 | $arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '', 'ext' => 'zip'); |
||
5375 | } |
||
5376 | unset($o); |
||
5377 | $this->procExec('rar --version', $o, $c); |
||
5378 | if ($c == 0 || $c == 7) { |
||
5379 | $arcs['create']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar'); |
||
5380 | $arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y', 'ext' => 'rar'); |
||
5381 | } else { |
||
5382 | unset($o); |
||
5383 | $test = $this->procExec('unrar', $o, $c); |
||
5384 | if ($c==0 || $c == 7) { |
||
5385 | $arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar'); |
||
5386 | } |
||
5387 | } |
||
5388 | unset($o); |
||
5389 | $this->procExec('7za --help', $o, $c); |
||
5390 | if ($c == 0) { |
||
5391 | $arcs['create']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z'); |
||
5392 | $arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z'); |
||
5393 | |||
5394 | if (empty($arcs['create']['application/zip'])) { |
||
5395 | $arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip'); |
||
5396 | } |
||
5397 | if (empty($arcs['extract']['application/zip'])) { |
||
5398 | $arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip'); |
||
5399 | } |
||
5400 | if (empty($arcs['create']['application/x-tar'])) { |
||
5401 | $arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar'); |
||
5402 | } |
||
5403 | if (empty($arcs['extract']['application/x-tar'])) { |
||
5404 | $arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar'); |
||
5405 | } |
||
5406 | } else if (substr(PHP_OS,0,3) === 'WIN') { |
||
5407 | // check `7z` for Windows server. |
||
5408 | unset($o); |
||
5409 | $this->procExec('7z', $o, $c); |
||
5410 | if ($c == 0) { |
||
5411 | $arcs['create']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'a', 'ext' => '7z'); |
||
5412 | $arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z'); |
||
5413 | |||
5414 | if (empty($arcs['create']['application/zip'])) { |
||
5415 | $arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip'); |
||
5416 | } |
||
5417 | if (empty($arcs['extract']['application/zip'])) { |
||
5418 | $arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip'); |
||
5419 | } |
||
5420 | if (empty($arcs['create']['application/x-tar'])) { |
||
5421 | $arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar'); |
||
5422 | } |
||
5423 | if (empty($arcs['extract']['application/x-tar'])) { |
||
5424 | $arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar'); |
||
5425 | } |
||
5426 | } |
||
5427 | } |
||
5428 | |||
5429 | } |
||
5430 | |||
5431 | // Use PHP ZipArchive Class |
||
5432 | if (class_exists('ZipArchive', false)) { |
||
5433 | if (empty($arcs['create']['application/zip'])) { |
||
5434 | $arcs['create']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => array('self', 'zipArchiveZip'), 'ext' => 'zip'); |
||
5435 | } |
||
5436 | if (empty($arcs['extract']['application/zip'])) { |
||
5437 | $arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => array('self', 'zipArchiveUnzip'), 'ext' => 'zip'); |
||
5438 | } |
||
5439 | } |
||
5440 | |||
5441 | $this->sessionCache[$sessionKey] = $arcs; |
||
5442 | $this->session->set($this->id, $this->sessionCache); |
||
5443 | return $arcs; |
||
5444 | } |
||
5445 | |||
5446 | /** |
||
5447 | * Resolve relative / (Unix-like)absolute path |
||
5448 | * |
||
5449 | * @param string $path target path |
||
5450 | * @param string $base base path |
||
5451 | * @return string |
||
5452 | */ |
||
5453 | protected function getFullPath($path, $base) { |
||
5454 | $separator = $this->separator; |
||
5455 | $systemroot = $this->systemRoot; |
||
5456 | |||
5457 | if ($base[0] === $separator && strpos($base, 0, strlen($systemroot)) !== $systemroot) { |
||
5458 | $base = $systemroot . substr($base, 1); |
||
5459 | } |
||
5460 | |||
5461 | // 'Here' |
||
5462 | if ($path === '' || $path === '.' . $separator) return $base; |
||
5463 | |||
5464 | $sepquoted = preg_quote($separator, '#'); |
||
5465 | |||
5466 | if (substr($path, 0, 3) === '..' . $separator) { |
||
5467 | $path = $base . $separator . $path; |
||
5468 | } |
||
5469 | // normalize `/../` |
||
5470 | $normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#'; // '#(/)[^\/]+/\.\./#' |
||
5471 | while(preg_match($normreg, $path)) { |
||
5472 | $path = preg_replace($normreg, '$1', $path, 1); |
||
5473 | } |
||
5474 | |||
5475 | // Absolute path |
||
5476 | if ($path[0] === $separator || strpos($path, $systemroot) === 0) { |
||
5477 | return $path; |
||
5478 | } |
||
5479 | |||
5480 | $preg_separator = '#' . $sepquoted . '#'; |
||
5481 | |||
5482 | // Relative path from 'Here' |
||
5483 | if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.') { |
||
5484 | $arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY); |
||
5485 | if ($arrn[0] !== '.') { |
||
5486 | array_unshift($arrn, '.'); |
||
5487 | } |
||
5488 | $arrn[0] = $base; |
||
5489 | return join($separator, $arrn); |
||
5490 | } |
||
5491 | |||
5492 | return $path; |
||
5493 | } |
||
5494 | |||
5495 | /** |
||
5496 | * Remove directory recursive on local file system |
||
5497 | * |
||
5498 | * @param string $dir Target dirctory path |
||
5499 | * @return boolean |
||
5500 | * @author Naoki Sawada |
||
5501 | */ |
||
5502 | public function rmdirRecursive($dir) { |
||
5503 | if (!is_link($dir) && is_dir($dir)) { |
||
5504 | chmod($dir, 0777); |
||
5505 | if ($handle = opendir($dir)) { |
||
5506 | while (false !== ($file = readdir($handle))) { |
||
5507 | if ($file === '.' || $file === '..') { |
||
5508 | continue; |
||
5509 | } |
||
5510 | elFinder::extendTimeLimit(30); |
||
5511 | $path = $dir . DIRECTORY_SEPARATOR . $file; |
||
5512 | if (!is_link($dir) && is_dir($path)) { |
||
5513 | $this->rmdirRecursive($path); |
||
5514 | } else { |
||
5515 | chmod($path, 0666); |
||
5516 | unlink($path); |
||
5517 | } |
||
5518 | } |
||
5519 | closedir($handle); |
||
5520 | } |
||
5521 | return rmdir($dir); |
||
5522 | } elseif (is_file($dir) || is_link($dir)) { |
||
5523 | chmod($dir, 0666); |
||
5524 | return unlink($dir); |
||
5525 | } |
||
5526 | return false; |
||
5527 | } |
||
5528 | |||
5529 | /** |
||
5530 | * Create archive and return its path |
||
5531 | * |
||
5532 | * @param string $dir target dir |
||
5533 | * @param array $files files names list |
||
5534 | * @param string $name archive name |
||
5535 | * @param array $arc archiver options |
||
5536 | * @return string|bool |
||
5537 | * @author Dmitry (dio) Levashov, |
||
5538 | * @author Alexey Sukhotin |
||
5539 | * @author Naoki Sawada |
||
5540 | **/ |
||
5541 | protected function makeArchive($dir, $files, $name, $arc) { |
||
5542 | if ($arc['cmd'] === 'phpfunction') { |
||
5543 | if (is_callable($arc['argc'])) { |
||
5544 | call_user_func_array($arc['argc'], array($dir, $files, $name)); |
||
5545 | } |
||
5546 | } else { |
||
5547 | $cwd = getcwd(); |
||
5548 | chdir($dir); |
||
5549 | |||
5550 | foreach($files as $i => $file) { |
||
5551 | $files[$i] = '.'.DIRECTORY_SEPARATOR.$file; |
||
5552 | } |
||
5553 | $files = array_map('escapeshellarg', $files); |
||
5554 | |||
5555 | $cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files); |
||
5556 | $this->procExec($cmd, $o, $c); |
||
5557 | chdir($cwd); |
||
5558 | } |
||
5559 | $path = $dir.DIRECTORY_SEPARATOR.$name; |
||
5560 | return file_exists($path) ? $path : false; |
||
5561 | } |
||
5562 | |||
5563 | /** |
||
5564 | * Unpack archive |
||
5565 | * |
||
5566 | * @param string $path archive path |
||
5567 | * @param array $arc archiver command and arguments (same as in $this->archivers) |
||
5568 | * @param bool $remove remove archive ( unlink($path) ) |
||
5569 | * @return void |
||
5570 | * @author Dmitry (dio) Levashov |
||
5571 | * @author Alexey Sukhotin |
||
5572 | * @author Naoki Sawada |
||
5573 | **/ |
||
5574 | protected function unpackArchive($path, $arc, $remove = true) { |
||
5575 | $dir = dirname($path); |
||
5576 | if ($arc['cmd'] === 'phpfunction') { |
||
5577 | if (is_callable($arc['argc'])) { |
||
5578 | call_user_func_array($arc['argc'], array($path, $dir)); |
||
5579 | } |
||
5580 | } else { |
||
5581 | $cwd = getcwd(); |
||
5582 | chdir($dir); |
||
5583 | $cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path)); |
||
5584 | $this->procExec($cmd, $o, $c); |
||
5585 | chdir($cwd); |
||
5586 | } |
||
5587 | $remove && unlink($path); |
||
5588 | } |
||
5589 | |||
5590 | /** |
||
5591 | * Return files of target directory that is dotfiles excludes. |
||
5592 | * |
||
5593 | * @param string $dir target directory path |
||
5594 | * @return array |
||
5595 | * @throws Exception |
||
5596 | * @author Naoki Sawada |
||
5597 | */ |
||
5598 | protected static function localScandir($dir) { |
||
5599 | // PHP function scandir() is not work well in specific environment. I dont know why. |
||
5600 | // ref. https://github.com/Studio-42/elFinder/issues/1248 |
||
5601 | $files = array(); |
||
5602 | if ($dh = opendir($dir)) { |
||
5603 | while (false !== ($file = readdir($dh))) { |
||
5604 | if ($file !== '.' && $file !== '..') { |
||
5605 | $files[] = $file; |
||
5606 | } |
||
5607 | } |
||
5608 | closedir($dh); |
||
5609 | } else { |
||
5610 | throw new Exception('Can not open local directory.'); |
||
5611 | } |
||
5612 | return $files; |
||
5613 | } |
||
5614 | |||
5615 | /** |
||
5616 | * Create Zip archive using PHP class ZipArchive |
||
5617 | * |
||
5618 | * @param string $dir target dir |
||
5619 | * @param array $files files names list |
||
5620 | * @param string|object $zipPath Zip archive name |
||
5621 | * @return bool |
||
5622 | * @author Naoki Sawada |
||
5623 | */ |
||
5624 | protected static function zipArchiveZip($dir, $files, $zipPath) { |
||
5625 | try { |
||
5626 | if ($start = is_string($zipPath)) { |
||
5627 | $zip = new ZipArchive(); |
||
5628 | if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) { |
||
5629 | $zip = false; |
||
5630 | } |
||
5631 | } else { |
||
5632 | $zip = $zipPath; |
||
5633 | } |
||
5634 | if ($zip) { |
||
5635 | foreach($files as $file) { |
||
5636 | $path = $dir . DIRECTORY_SEPARATOR . $file; |
||
5637 | if (is_dir($path)) { |
||
5638 | $zip->addEmptyDir($file); |
||
5639 | $_files = array(); |
||
5640 | if ($handle = opendir($path)) { |
||
5641 | while (false !== ($entry = readdir($handle))) { |
||
5642 | if ($entry !== "." && $entry !== "..") { |
||
5643 | $_files[] = $file . DIRECTORY_SEPARATOR . $entry; |
||
5644 | } |
||
5645 | } |
||
5646 | closedir($handle); |
||
5647 | } |
||
5648 | if ($_files) { |
||
5649 | self::zipArchiveZip($dir, $_files, $zip); |
||
5650 | } |
||
5651 | } else { |
||
5652 | $zip->addFile($path, $file); |
||
5653 | } |
||
5654 | } |
||
5655 | $start && $zip->close(); |
||
5656 | } |
||
5657 | } catch (Exception $e) { |
||
5658 | return false; |
||
5659 | } |
||
5660 | return true; |
||
5661 | } |
||
5662 | |||
5663 | /** |
||
5664 | * Unpack Zip archive using PHP class ZipArchive |
||
5665 | * |
||
5666 | * @param string $zipPath Zip archive name |
||
5667 | * @param string $toDir Extract to path |
||
5668 | * @return bool |
||
5669 | * @author Naoki Sawada |
||
5670 | */ |
||
5671 | protected static function zipArchiveUnzip($zipPath, $toDir) { |
||
5672 | try { |
||
5673 | $zip = new ZipArchive(); |
||
5674 | if ($zip->open($zipPath) === true) { |
||
5675 | $zip->extractTo($toDir); |
||
5676 | $zip->close(); |
||
5677 | } |
||
5678 | } catch (Exception $e) { |
||
5679 | return false; |
||
5680 | } |
||
5681 | return true; |
||
5682 | } |
||
5683 | |||
5684 | /**==================================* abstract methods *====================================**/ |
||
5685 | |||
5686 | /*********************** paths/urls *************************/ |
||
5687 | |||
5688 | /** |
||
5689 | * Return parent directory path |
||
5690 | * |
||
5691 | * @param string $path file path |
||
5692 | * @return string |
||
5693 | * @author Dmitry (dio) Levashov |
||
5694 | **/ |
||
5695 | abstract protected function _dirname($path); |
||
5696 | |||
5697 | /** |
||
5698 | * Return file name |
||
5699 | * |
||
5700 | * @param string $path file path |
||
5701 | * @return string |
||
5702 | * @author Dmitry (dio) Levashov |
||
5703 | **/ |
||
5704 | abstract protected function _basename($path); |
||
5705 | |||
5706 | /** |
||
5707 | * Join dir name and file name and return full path. |
||
5708 | * Some drivers (db) use int as path - so we give to concat path to driver itself |
||
5709 | * |
||
5710 | * @param string $dir dir path |
||
5711 | * @param string $name file name |
||
5712 | * @return string |
||
5713 | * @author Dmitry (dio) Levashov |
||
5714 | **/ |
||
5715 | abstract protected function _joinPath($dir, $name); |
||
5716 | |||
5717 | /** |
||
5718 | * Return normalized path |
||
5719 | * |
||
5720 | * @param string $path file path |
||
5721 | * @return string |
||
5722 | * @author Dmitry (dio) Levashov |
||
5723 | **/ |
||
5724 | abstract protected function _normpath($path); |
||
5725 | |||
5726 | /** |
||
5727 | * Return file path related to root dir |
||
5728 | * |
||
5729 | * @param string $path file path |
||
5730 | * @return string |
||
5731 | * @author Dmitry (dio) Levashov |
||
5732 | **/ |
||
5733 | abstract protected function _relpath($path); |
||
5734 | |||
5735 | /** |
||
5736 | * Convert path related to root dir into real path |
||
5737 | * |
||
5738 | * @param string $path rel file path |
||
5739 | * @return string |
||
5740 | * @author Dmitry (dio) Levashov |
||
5741 | **/ |
||
5742 | abstract protected function _abspath($path); |
||
5743 | |||
5744 | /** |
||
5745 | * Return fake path started from root dir. |
||
5746 | * Required to show path on client side. |
||
5747 | * |
||
5748 | * @param string $path file path |
||
5749 | * @return string |
||
5750 | * @author Dmitry (dio) Levashov |
||
5751 | **/ |
||
5752 | abstract protected function _path($path); |
||
5753 | |||
5754 | /** |
||
5755 | * Return true if $path is children of $parent |
||
5756 | * |
||
5757 | * @param string $path path to check |
||
5758 | * @param string $parent parent path |
||
5759 | * @return bool |
||
5760 | * @author Dmitry (dio) Levashov |
||
5761 | **/ |
||
5762 | abstract protected function _inpath($path, $parent); |
||
5763 | |||
5764 | /** |
||
5765 | * Return stat for given path. |
||
5766 | * Stat contains following fields: |
||
5767 | * - (int) size file size in b. required |
||
5768 | * - (int) ts file modification time in unix time. required |
||
5769 | * - (string) mime mimetype. required for folders, others - optionally |
||
5770 | * - (bool) read read permissions. required |
||
5771 | * - (bool) write write permissions. required |
||
5772 | * - (bool) locked is object locked. optionally |
||
5773 | * - (bool) hidden is object hidden. optionally |
||
5774 | * - (string) alias for symlinks - link target path relative to root path. optionally |
||
5775 | * - (string) target for symlinks - link target path. optionally |
||
5776 | * |
||
5777 | * If file does not exists - returns empty array or false. |
||
5778 | * |
||
5779 | * @param string $path file path |
||
5780 | * @return array|false |
||
5781 | * @author Dmitry (dio) Levashov |
||
5782 | **/ |
||
5783 | abstract protected function _stat($path); |
||
5784 | |||
5785 | |||
5786 | /***************** file stat ********************/ |
||
5787 | |||
5788 | |||
5789 | /** |
||
5790 | * Return true if path is dir and has at least one childs directory |
||
5791 | * |
||
5792 | * @param string $path dir path |
||
5793 | * @return bool |
||
5794 | * @author Dmitry (dio) Levashov |
||
5795 | **/ |
||
5796 | abstract protected function _subdirs($path); |
||
5797 | |||
5798 | /** |
||
5799 | * Return object width and height |
||
5800 | * Ususaly used for images, but can be realize for video etc... |
||
5801 | * |
||
5802 | * @param string $path file path |
||
5803 | * @param string $mime file mime type |
||
5804 | * @return string |
||
5805 | * @author Dmitry (dio) Levashov |
||
5806 | **/ |
||
5807 | abstract protected function _dimensions($path, $mime); |
||
5808 | |||
5809 | /******************** file/dir content *********************/ |
||
5810 | |||
5811 | /** |
||
5812 | * Return files list in directory |
||
5813 | * |
||
5814 | * @param string $path dir path |
||
5815 | * @return array |
||
5816 | * @author Dmitry (dio) Levashov |
||
5817 | **/ |
||
5818 | abstract protected function _scandir($path); |
||
5819 | |||
5820 | /** |
||
5821 | * Open file and return file pointer |
||
5822 | * |
||
5823 | * @param string $path file path |
||
5824 | * @param string $mode open mode |
||
5825 | * @return resource|false |
||
5826 | * @author Dmitry (dio) Levashov |
||
5827 | **/ |
||
5828 | abstract protected function _fopen($path, $mode="rb"); |
||
5829 | |||
5830 | /** |
||
5831 | * Close opened file |
||
5832 | * |
||
5833 | * @param resource $fp file pointer |
||
5834 | * @param string $path file path |
||
5835 | * @return bool |
||
5836 | * @author Dmitry (dio) Levashov |
||
5837 | **/ |
||
5838 | abstract protected function _fclose($fp, $path=''); |
||
5839 | |||
5840 | /******************** file/dir manipulations *************************/ |
||
5841 | |||
5842 | /** |
||
5843 | * Create dir and return created dir path or false on failed |
||
5844 | * |
||
5845 | * @param string $path parent dir path |
||
5846 | * @param string $name new directory name |
||
5847 | * @return string|bool |
||
5848 | * @author Dmitry (dio) Levashov |
||
5849 | **/ |
||
5850 | abstract protected function _mkdir($path, $name); |
||
5851 | |||
5852 | /** |
||
5853 | * Create file and return it's path or false on failed |
||
5854 | * |
||
5855 | * @param string $path parent dir path |
||
5856 | * @param string $name new file name |
||
5857 | * @return string|bool |
||
5858 | * @author Dmitry (dio) Levashov |
||
5859 | **/ |
||
5860 | abstract protected function _mkfile($path, $name); |
||
5861 | |||
5862 | /** |
||
5863 | * Create symlink |
||
5864 | * |
||
5865 | * @param string $source file to link to |
||
5866 | * @param string $targetDir folder to create link in |
||
5867 | * @param string $name symlink name |
||
5868 | * @return bool |
||
5869 | * @author Dmitry (dio) Levashov |
||
5870 | **/ |
||
5871 | abstract protected function _symlink($source, $targetDir, $name); |
||
5872 | |||
5873 | /** |
||
5874 | * Copy file into another file (only inside one volume) |
||
5875 | * |
||
5876 | * @param string $source source file path |
||
5877 | * @param $targetDir |
||
5878 | * @param string $name file name |
||
5879 | * @return bool|string |
||
5880 | * @internal param string $target target dir path |
||
5881 | * @author Dmitry (dio) Levashov |
||
5882 | */ |
||
5883 | abstract protected function _copy($source, $targetDir, $name); |
||
5884 | |||
5885 | /** |
||
5886 | * Move file into another parent dir. |
||
5887 | * Return new file path or false. |
||
5888 | * |
||
5889 | * @param string $source source file path |
||
5890 | * @param $targetDir |
||
5891 | * @param string $name file name |
||
5892 | * @return bool|string |
||
5893 | * @internal param string $target target dir path |
||
5894 | * @author Dmitry (dio) Levashov |
||
5895 | */ |
||
5896 | abstract protected function _move($source, $targetDir, $name); |
||
5897 | |||
5898 | /** |
||
5899 | * Remove file |
||
5900 | * |
||
5901 | * @param string $path file path |
||
5902 | * @return bool |
||
5903 | * @author Dmitry (dio) Levashov |
||
5904 | **/ |
||
5905 | abstract protected function _unlink($path); |
||
5906 | |||
5907 | /** |
||
5908 | * Remove dir |
||
5909 | * |
||
5910 | * @param string $path dir path |
||
5911 | * @return bool |
||
5912 | * @author Dmitry (dio) Levashov |
||
5913 | **/ |
||
5914 | abstract protected function _rmdir($path); |
||
5915 | |||
5916 | /** |
||
5917 | * Create new file and write into it from file pointer. |
||
5918 | * Return new file path or false on error. |
||
5919 | * |
||
5920 | * @param resource $fp file pointer |
||
5921 | * @param string $dir target dir path |
||
5922 | * @param string $name file name |
||
5923 | * @param array $stat file stat (required by some virtual fs) |
||
5924 | * @return bool|string |
||
5925 | * @author Dmitry (dio) Levashov |
||
5926 | **/ |
||
5927 | abstract protected function _save($fp, $dir, $name, $stat); |
||
5928 | |||
5929 | /** |
||
5930 | * Get file contents |
||
5931 | * |
||
5932 | * @param string $path file path |
||
5933 | * @return string|false |
||
5934 | * @author Dmitry (dio) Levashov |
||
5935 | **/ |
||
5936 | abstract protected function _getContents($path); |
||
5937 | |||
5938 | /** |
||
5939 | * Write a string to a file |
||
5940 | * |
||
5941 | * @param string $path file path |
||
5942 | * @param string $content new file content |
||
5943 | * @return bool |
||
5944 | * @author Dmitry (dio) Levashov |
||
5945 | **/ |
||
5946 | abstract protected function _filePutContents($path, $content); |
||
5947 | |||
5948 | /** |
||
5949 | * Extract files from archive |
||
5950 | * |
||
5951 | * @param string $path file path |
||
5952 | * @param array $arc archiver options |
||
5953 | * @return bool |
||
5954 | * @author Dmitry (dio) Levashov, |
||
5955 | * @author Alexey Sukhotin |
||
5956 | **/ |
||
5957 | abstract protected function _extract($path, $arc); |
||
5958 | |||
5959 | /** |
||
5960 | * Create archive and return its path |
||
5961 | * |
||
5962 | * @param string $dir target dir |
||
5963 | * @param array $files files names list |
||
5964 | * @param string $name archive name |
||
5965 | * @param array $arc archiver options |
||
5966 | * @return string|bool |
||
5967 | * @author Dmitry (dio) Levashov, |
||
5968 | * @author Alexey Sukhotin |
||
5969 | **/ |
||
5970 | abstract protected function _archive($dir, $files, $name, $arc); |
||
5971 | |||
5972 | /** |
||
5973 | * Detect available archivers |
||
5974 | * |
||
5975 | * @return void |
||
5976 | * @author Dmitry (dio) Levashov, |
||
5977 | * @author Alexey Sukhotin |
||
5978 | **/ |
||
5979 | abstract protected function _checkArchivers(); |
||
5980 | |||
5981 | /** |
||
5982 | * Change file mode (chmod) |
||
5983 | * |
||
5984 | * @param string $path file path |
||
5985 | * @param string $mode octal string such as '0755' |
||
5986 | * @return bool |
||
5987 | * @author David Bartle, |
||
5988 | **/ |
||
5989 | abstract protected function _chmod($path, $mode); |
||
5990 | |||
5991 | |||
5992 | } // END class |
||
5993 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.