Completed
Push — master ( ac5f5b...19a64a )
by
unknown
27:06 queued 12:25
created

system/elfinder/elFinderVolumeDriver.class.php (1 issue)

Upgrade to new PHP Analysis Engine

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
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 24 and the first side effect is on line 10.

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.

Loading history...
2
/**
3
 * @package     Arastta eCommerce
4
 * @copyright   2015-2017 Arastta Association. All rights reserved.
5
 * @copyright   See CREDITS.txt for credits and other copyright notices.
6
 * @license     GNU GPL version 3; see LICENSE.txt
7
 * @link        https://arastta.org
8
 */
9
10
defined('AREXE') or die;
11
 
12
/**
13
 * Base class for elFinder volume.
14
 * Provide 2 layers:
15
 *  1. Public API (commands)
16
 *  2. abstract fs API
17
 *
18
 * All abstract methods begin with "_"
19
 *
20
 * @author Dmitry (dio) Levashov
21
 * @author Troex Nevelin
22
 * @author Alexey Sukhotin
23
 **/
24
abstract class elFinderVolumeDriver {
25
	
26
	/**
27
	 * Driver id
28
	 * Must be started from letter and contains [a-z0-9]
29
	 * Used as part of volume id
30
	 *
31
	 * @var string
32
	 **/
33
	protected $driverId = 'a';
34
	
35
	/**
36
	 * Volume id - used as prefix for files hashes
37
	 *
38
	 * @var string
39
	 **/
40
	protected $id = '';
41
	
42
	/**
43
	 * Flag - volume "mounted" and available
44
	 *
45
	 * @var bool
46
	 **/
47
	protected $mounted = false;
48
	
49
	/**
50
	 * Root directory path
51
	 *
52
	 * @var string
53
	 **/
54
	protected $root = '';
55
	
56
	/**
57
	 * Root basename | alias
58
	 *
59
	 * @var string
60
	 **/
61
	protected $rootName = '';
62
	
63
	/**
64
	 * Default directory to open
65
	 *
66
	 * @var string
67
	 **/
68
	protected $startPath = '';
69
	
70
	/**
71
	 * Base URL
72
	 *
73
	 * @var string
74
	 **/
75
	protected $URL = '';
76
	
77
	/**
78
	 * Thumbnails dir path
79
	 *
80
	 * @var string
81
	 **/
82
	protected $tmbPath = '';
83
	
84
	/**
85
	 * Is thumbnails dir writable
86
	 *
87
	 * @var bool
88
	 **/
89
	protected $tmbPathWritable = false;
90
	
91
	/**
92
	 * Thumbnails base URL
93
	 *
94
	 * @var string
95
	 **/
96
	protected $tmbURL = '';
97
	
98
	/**
99
	 * Thumbnails size in px
100
	 *
101
	 * @var int
102
	 **/
103
	protected $tmbSize = 48;
104
	
105
	/**
106
	 * Image manipulation lib name
107
	 * auto|imagick|mogtify|gd
108
	 *
109
	 * @var string
110
	 **/
111
	protected $imgLib = 'auto';
112
	
113
	/**
114
	 * Library to crypt files name
115
	 *
116
	 * @var string
117
	 **/
118
	protected $cryptLib = '';
119
	
120
	/**
121
	 * Archivers config
122
	 *
123
	 * @var array
124
	 **/
125
	protected $archivers = array(
126
		'create'  => array(),
127
		'extract' => array()
128
	);
129
	
130
	/**
131
	 * How many subdirs levels return for tree
132
	 *
133
	 * @var int
134
	 **/
135
	protected $treeDeep = 1;
136
	
137
	/**
138
	 * Errors from last failed action
139
	 *
140
	 * @var array
141
	 **/
142
	protected $error = array();
143
	
144
	/**
145
	 * Today 24:00 timestamp
146
	 *
147
	 * @var int
148
	 **/
149
	protected $today = 0;
150
	
151
	/**
152
	 * Yesterday 24:00 timestamp
153
	 *
154
	 * @var int
155
	 **/
156
	protected $yesterday = 0;
157
	
158
	/**
159
	 * Object configuration
160
	 *
161
	 * @var array
162
	 **/
163
	protected $options = array(
164
		'id'              => '',
165
		// root directory path
166
		'path'            => '',
167
		// open this path on initial request instead of root path
168
		'startPath'       => '',
169
		// how many subdirs levels return per request
170
		'treeDeep'        => 1,
171
		// root url, not set to disable sending URL to client (replacement for old "fileURL" option)
172
		'URL'             => '',
173
		// directory separator. required by client to show paths correctly
174
		'separator'       => DIRECTORY_SEPARATOR,
175
		// library to crypt/uncrypt files names (not implemented)
176
		'cryptLib'        => '',
177
		// how to detect files mimetypes. (auto/internal/finfo/mime_content_type)
178
		'mimeDetect'      => 'auto',
179
		// mime.types file path (for mimeDetect==internal)
180
		'mimefile'        => '',
181
		// directory for thumbnails
182
		'tmbPath'         => '.tmb',
183
		// mode to create thumbnails dir
184
		'tmbPathMode'     => 0755,
185
		// thumbnails dir URL. Set it if store thumbnails outside root directory
186
		'tmbURL'          => '',
187
		// thumbnails size (px)
188
		'tmbSize'         => 48,
189
		// thumbnails crop (true - crop, false - scale image to fit thumbnail size)
190
		'tmbCrop'         => true,
191
		// thumbnails background color (hex #rrggbb or 'transparent')
192
		'tmbBgColor'      => '#ffffff',
193
		// image manipulations library
194
		'imgLib'          => 'auto',
195
		// on paste file -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
196
		'copyOverwrite'   => true,
197
		// if true - join new and old directories content on paste
198
		'copyJoin'        => true,
199
		// on upload -  if true - old file will be replaced with new one, if false new file get name - original_name-number.ext
200
		'uploadOverwrite' => true,
201
		// mimetypes allowed to upload
202
		'uploadAllow'     => array(),
203
		// mimetypes not allowed to upload
204
		'uploadDeny'      => array(),
205
		// order to proccess uploadAllow and uploadDeny options
206
		'uploadOrder'     => array('deny', 'allow'),
207
		// maximum upload file size. NOTE - this is size for every uploaded files
208
		'uploadMaxSize'   => 0,
209
		// files dates format
210
		'dateFormat'      => 'j M Y H:i',
211
		// files time format
212
		'timeFormat'      => 'H:i',
213
		// if true - every folder will be check for children folders, otherwise all folders will be marked as having subfolders
214
		'checkSubfolders' => true,
215
		// allow to copy from this volume to other ones?
216
		'copyFrom'        => true,
217
		// allow to copy from other volumes to this one?
218
		'copyTo'          => true,
219
		// list of commands disabled on this root
220
		'disabled'        => array(),
221
		// regexp or function name to validate new file name
222
		'acceptedName'    => '/^[^\.].*/', //<-- DONT touch this! Use constructor options to overwrite it!
223
		// function/class method to control files permissions
224
		'accessControl'   => null,
225
		// some data required by access control
226
		'accessControlData' => null,
227
		// default permissions.
228
		'defaults'     => array(
229
			'read'   => true,
230
			'write'  => true,
231
			'locked' => false,
232
			'hidden' => false
233
		),
234
		// files attributes
235
		'attributes'   => array(),
236
		// Allowed archive's mimetypes to create. Leave empty for all available types.
237
		'archiveMimes' => array(),
238
		// Manual config for archivers. See example below. Leave empty for auto detect
239
		'archivers'    => array(),
240
		// required to fix bug on macos
241
		'utf8fix'      => false,
242
		 //                           й                 ё              Й               Ё              Ø         Å
243
		'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"),
244
		'utf8replace'  => array("\u0439",        "\u0451",       "\u0419",       "\u0401",       "\u00d8", "\u00c5")
245
	);
246
247
	/**
248
	 * Defaults permissions
249
	 *
250
	 * @var array
251
	 **/
252
	protected $defaults = array(
253
		'read'   => true,
254
		'write'  => true,
255
		'locked' => false,
256
		'hidden' => false
257
	);
258
	
259
	/**
260
	 * Access control function/class
261
	 *
262
	 * @var mixed
263
	 **/
264
	protected $attributes = array();
265
	
266
	/**
267
	 * Access control function/class
268
	 *
269
	 * @var mixed
270
	 **/
271
	protected $access = null;
272
	
273
	/**
274
	 * Mime types allowed to upload
275
	 *
276
	 * @var array
277
	 **/
278
	protected $uploadAllow = array();
279
	
280
	/**
281
	 * Mime types denied to upload
282
	 *
283
	 * @var array
284
	 **/
285
	protected $uploadDeny = array();
286
	
287
	/**
288
	 * Order to validate uploadAllow and uploadDeny
289
	 *
290
	 * @var array
291
	 **/
292
	protected $uploadOrder = array();
293
	
294
	/**
295
	 * Maximum allowed upload file size.
296
	 * Set as number or string with unit - "10M", "500K", "1G"
297
	 *
298
	 * @var int|string
299
	 **/
300
	protected $uploadMaxSize = 0;
301
	
302
	/**
303
	 * Mimetype detect method
304
	 *
305
	 * @var string
306
	 **/
307
	protected $mimeDetect = 'auto';
308
	
309
	/**
310
	 * Flag - mimetypes from externail file was loaded
311
	 *
312
	 * @var bool
313
	 **/
314
	private static $mimetypesLoaded = false;
315
	
316
	/**
317
	 * Finfo object for mimeDetect == 'finfo'
318
	 *
319
	 * @var object
320
	 **/
321
	protected $finfo = null;
322
	
323
	/**
324
	 * List of disabled client's commands
325
	 *
326
	 * @var array
327
	 **/
328
	protected $disabled = array();
329
	
330
	/**
331
	 * default extensions/mimetypes for mimeDetect == 'internal' 
332
	 *
333
	 * @var array
334
	 **/
335
	protected static $mimetypes = array(
336
		// applications
337
		'ai'    => 'application/postscript',
338
		'eps'   => 'application/postscript',
339
		'exe'   => 'application/x-executable',
340
		'doc'   => 'application/vnd.ms-word',
341
		'xls'   => 'application/vnd.ms-excel',
342
		'ppt'   => 'application/vnd.ms-powerpoint',
343
		'pps'   => 'application/vnd.ms-powerpoint',
344
		'pdf'   => 'application/pdf',
345
		'xml'   => 'application/xml',
346
		'swf'   => 'application/x-shockwave-flash',
347
		'torrent' => 'application/x-bittorrent',
348
		'jar'   => 'application/x-jar',
349
		// open office (finfo detect as application/zip)
350
		'odt'   => 'application/vnd.oasis.opendocument.text',
351
		'ott'   => 'application/vnd.oasis.opendocument.text-template',
352
		'oth'   => 'application/vnd.oasis.opendocument.text-web',
353
		'odm'   => 'application/vnd.oasis.opendocument.text-master',
354
		'odg'   => 'application/vnd.oasis.opendocument.graphics',
355
		'otg'   => 'application/vnd.oasis.opendocument.graphics-template',
356
		'odp'   => 'application/vnd.oasis.opendocument.presentation',
357
		'otp'   => 'application/vnd.oasis.opendocument.presentation-template',
358
		'ods'   => 'application/vnd.oasis.opendocument.spreadsheet',
359
		'ots'   => 'application/vnd.oasis.opendocument.spreadsheet-template',
360
		'odc'   => 'application/vnd.oasis.opendocument.chart',
361
		'odf'   => 'application/vnd.oasis.opendocument.formula',
362
		'odb'   => 'application/vnd.oasis.opendocument.database',
363
		'odi'   => 'application/vnd.oasis.opendocument.image',
364
		'oxt'   => 'application/vnd.openofficeorg.extension',
365
		// MS office 2007 (finfo detect as application/zip)
366
		'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
367
		'docm'  => 'application/vnd.ms-word.document.macroEnabled.12',
368
		'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
369
		'dotm'  => 'application/vnd.ms-word.template.macroEnabled.12',
370
		'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
371
		'xlsm'  => 'application/vnd.ms-excel.sheet.macroEnabled.12',
372
		'xltx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
373
		'xltm'  => 'application/vnd.ms-excel.template.macroEnabled.12',
374
		'xlsb'  => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
375
		'xlam'  => 'application/vnd.ms-excel.addin.macroEnabled.12',
376
		'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
377
		'pptm'  => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
378
		'ppsx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
379
		'ppsm'  => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
380
		'potx'  => 'application/vnd.openxmlformats-officedocument.presentationml.template',
381
		'potm'  => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
382
		'ppam'  => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
383
		'sldx'  => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
384
		'sldm'  => 'application/vnd.ms-powerpoint.slide.macroEnabled.12',
385
		// archives
386
		'gz'    => 'application/x-gzip',
387
		'tgz'   => 'application/x-gzip',
388
		'bz'    => 'application/x-bzip2',
389
		'bz2'   => 'application/x-bzip2',
390
		'tbz'   => 'application/x-bzip2',
391
		'xz'    => 'application/x-xz',
392
		'zip'   => 'application/zip',
393
		'rar'   => 'application/x-rar',
394
		'tar'   => 'application/x-tar',
395
		'7z'    => 'application/x-7z-compressed',
396
		// texts
397
		'txt'   => 'text/plain',
398
		'php'   => 'text/x-php',
399
		'html'  => 'text/html',
400
		'htm'   => 'text/html',
401
		'js'    => 'text/javascript',
402
		'css'   => 'text/css',
403
		'rtf'   => 'text/rtf',
404
		'rtfd'  => 'text/rtfd',
405
		'py'    => 'text/x-python',
406
		'java'  => 'text/x-java-source',
407
		'rb'    => 'text/x-ruby',
408
		'sh'    => 'text/x-shellscript',
409
		'pl'    => 'text/x-perl',
410
		'xml'   => 'text/xml',
411
		'sql'   => 'text/x-sql',
412
		'c'     => 'text/x-csrc',
413
		'h'     => 'text/x-chdr',
414
		'cpp'   => 'text/x-c++src',
415
		'hh'    => 'text/x-c++hdr',
416
		'log'   => 'text/plain',
417
		'csv'   => 'text/x-comma-separated-values',
418
		// images
419
		'bmp'   => 'image/x-ms-bmp',
420
		'jpg'   => 'image/jpeg',
421
		'jpeg'  => 'image/jpeg',
422
		'gif'   => 'image/gif',
423
		'png'   => 'image/png',
424
		'tif'   => 'image/tiff',
425
		'tiff'  => 'image/tiff',
426
		'tga'   => 'image/x-targa',
427
		'psd'   => 'image/vnd.adobe.photoshop',
428
		'ai'    => 'image/vnd.adobe.photoshop',
429
		'xbm'   => 'image/xbm',
430
		'pxm'   => 'image/pxm',
431
		//audio
432
		'mp3'   => 'audio/mpeg',
433
		'mid'   => 'audio/midi',
434
		'ogg'   => 'audio/ogg',
435
		'oga'   => 'audio/ogg',
436
		'm4a'   => 'audio/x-m4a',
437
		'wav'   => 'audio/wav',
438
		'wma'   => 'audio/x-ms-wma',
439
		// video
440
		'avi'   => 'video/x-msvideo',
441
		'dv'    => 'video/x-dv',
442
		'mp4'   => 'video/mp4',
443
		'mpeg'  => 'video/mpeg',
444
		'mpg'   => 'video/mpeg',
445
		'mov'   => 'video/quicktime',
446
		'wm'    => 'video/x-ms-wmv',
447
		'flv'   => 'video/x-flv',
448
		'mkv'   => 'video/x-matroska',
449
		'webm'  => 'video/webm',
450
		'ogv'   => 'video/ogg',
451
		'ogm'   => 'video/ogg'
452
		);
453
	
454
	/**
455
	 * Directory separator - required by client
456
	 *
457
	 * @var string
458
	 **/
459
	protected $separator = DIRECTORY_SEPARATOR;
460
	
461
	/**
462
	 * Mimetypes allowed to display
463
	 *
464
	 * @var array
465
	 **/
466
	protected $onlyMimes = array();
467
	
468
	/**
469
	 * Store files moved or overwrited files info
470
	 *
471
	 * @var array
472
	 **/
473
	protected $removed = array();
474
	
475
	/**
476
	 * Cache storage
477
	 *
478
	 * @var array
479
	 **/
480
	protected $cache = array();
481
	
482
	/**
483
	 * Cache by folders
484
	 *
485
	 * @var array
486
	 **/
487
	protected $dirsCache = array();
488
	
489
	/*********************************************************************/
490
	/*                            INITIALIZATION                         */
491
	/*********************************************************************/
492
	
493
	/**
494
	 * Prepare driver before mount volume.
495
	 * Return true if volume is ready.
496
	 *
497
	 * @return bool
498
	 * @author Dmitry (dio) Levashov
499
	 **/
500
	protected function init() {
501
		return true;
502
	}	
503
		
504
	/**
505
	 * Configure after successfull mount.
506
	 * By default set thumbnails path and image manipulation library.
507
	 *
508
	 * @return void
509
	 * @author Dmitry (dio) Levashov
510
	 **/
511
	protected function configure() {
512
		// set thumbnails path
513
		$path = $this->options['tmbPath'];
514
		if ($path) {
515
			if (!file_exists($path)) {
516
				if (@mkdir($path)) {
517
					chmod($path, $this->options['tmbPathMode']);
518
				} else {
519
					$path = '';
520
				}
521
			} 
522
			
523
			if (is_dir($path) && is_readable($path)) {
524
				$this->tmbPath = $path;
525
				$this->tmbPathWritable = is_writable($path);
526
			}
527
		}
528
529
		// set image manipulation library
530
		$type = preg_match('/^(imagick|gd|auto)$/i', $this->options['imgLib'])
531
			? strtolower($this->options['imgLib'])
532
			: 'auto';
533
534
		if (($type == 'imagick' || $type == 'auto') && extension_loaded('imagick')) {
535
			$this->imgLib = 'imagick';
536
		} else {
537
			$this->imgLib = function_exists('gd_info') ? 'gd' : '';
538
		}
539
		
540
	}
541
	
542
	
543
	/*********************************************************************/
544
	/*                              PUBLIC API                           */
545
	/*********************************************************************/
546
	
547
	/**
548
	 * Return driver id. Used as a part of volume id.
549
	 *
550
	 * @return string
551
	 * @author Dmitry (dio) Levashov
552
	 **/
553
	public function driverId() {
554
		return $this->driverId;
555
	}
556
	
557
	/**
558
	 * Return volume id
559
	 *
560
	 * @return string
561
	 * @author Dmitry (dio) Levashov
562
	 **/
563
	public function id() {
564
		return $this->id;
565
	}
566
		
567
	/**
568
	 * Return debug info for client
569
	 *
570
	 * @return array
571
	 * @author Dmitry (dio) Levashov
572
	 **/
573
	public function debug() {
574
		return array(
575
			'id'         => $this->id(),
576
			'name'       => strtolower(substr(get_class($this), strlen('elfinderdriver'))),
577
			'mimeDetect' => $this->mimeDetect,
578
			'imgLib'     => $this->imgLib
579
		);
580
	}
581
	
582
	/**
583
	 * "Mount" volume.
584
	 * Return true if volume available for read or write, 
585
	 * false - otherwise
586
	 *
587
	 * @return bool
588
	 * @author Dmitry (dio) Levashov
589
	 * @author Alexey Sukhotin
590
	 **/
591
	public function mount(array $opts) {
592
		if (!isset($opts['path']) || $opts['path'] === '') {
593
			return $this->setError('Path undefined.');;
594
		}
595
		
596
		$this->options = array_merge($this->options, $opts);
597
		$this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_';
598
		$this->root = $this->_normpath($this->options['path']);
599
		$this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR;
600
		
601
		// default file attribute
602
		$this->defaults = array(
603
			'read'    => isset($this->options['defaults']['read'])  ? !!$this->options['defaults']['read']  : true,
604
			'write'   => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true,
605
			'locked'  => isset($this->options['defaults']['locked']) ? !!$this->options['defaults']['locked'] : false,
606
			'hidden'  => isset($this->options['defaults']['hidden']) ? !!$this->options['defaults']['hidden'] : false
607
		);
608
609
		// root attributes
610
		$this->attributes[] = array(
611
			'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR).'$~',
612
			'locked'  => true,
613
			'hidden'  => false
614
		);
615
		// set files attributes
616 View Code Duplication
		if (!empty($this->options['attributes']) && is_array($this->options['attributes'])) {
617
			
618
			foreach ($this->options['attributes'] as $a) {
619
				// attributes must contain pattern and at least one rule
620
				if (!empty($a['pattern']) || count($a) > 1) {
621
					$this->attributes[] = $a;
622
				}
623
			}
624
		}
625
626
		if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) {
627
			$this->access = $this->options['accessControl'];
628
		}
629
		
630
		$this->today     = mktime(0,0,0, date('m'), date('d'), date('Y'));
631
		$this->yesterday = $this->today-86400;
632
		
633
		// debug($this->attributes);
634
		if (!$this->init()) {
635
			return false;
636
		}
637
		
638
		// check some options is arrays
639
		$this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow'])
640
			? $this->options['uploadAllow']
641
			: array();
642
			
643
		$this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny'])
644
			? $this->options['uploadDeny']
645
			: array();
646
647
		if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x
648
			$parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow');
649
			$this->uploadOrder = array(trim($parts[0]), trim($parts[1]));
650
		} else { // telephat_mode off
651
			$this->uploadOrder = $this->options['uploadOrder'];
652
		}
653
			
654
		if (!empty($this->options['uploadMaxSize'])) {
655
			$size = ''.$this->options['uploadMaxSize'];
656
			$unit = strtolower(substr($size, strlen($size) - 1));
657
			$n = 1;
658
			switch ($unit) {
659
				case 'k':
660
					$n = 1024;
661
					break;
662
				case 'm':
663
					$n = 1048576;
664
					break;
665
				case 'g':
666
					$n = 1073741824;
667
			}
668
			$this->uploadMaxSize = intval($size)*$n;
669
		}
670
			
671
		$this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled'])
672
			? $this->options['disabled']
673
			: array();
674
		
675
		$this->cryptLib   = $this->options['cryptLib'];
676
		$this->mimeDetect = $this->options['mimeDetect'];
677
678
		// find available mimetype detect method
679
		$type = strtolower($this->options['mimeDetect']);
680
		$type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto';
681
		$regexp = '/text\/x\-(php|c\+\+)/';
682
	
683
		if (($type == 'finfo' || $type == 'auto') 
684
		&& class_exists('finfo')) {
685
			$tmpFileInfo = @explode(';', @finfo_file(finfo_open(FILEINFO_MIME), __FILE__));
686
		} else {
687
			$tmpFileInfo = false;
688
		}
689
	
690
		if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) {
691
			$type = 'finfo';
692
			$this->finfo = finfo_open(FILEINFO_MIME);
693
		} elseif (($type == 'mime_content_type' || $type == 'auto') 
694
		&& function_exists('mime_content_type')
695
		&& preg_match($regexp, array_shift(explode(';', mime_content_type(__FILE__))))) {
696
			$type = 'mime_content_type';
697
		} else {
698
			$type = 'internal';
699
		}
700
		$this->mimeDetect = $type;
701
702
		// load mimes from external file for mimeDetect == 'internal'
703
		// based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163
704
		// file must be in file directory or in parent one 
705
		if ($this->mimeDetect == 'internal' && !self::$mimetypesLoaded) {
706
			self::$mimetypesLoaded = true;
707
			$this->mimeDetect = 'internal';
708
			$file = false;
709
			if (!empty($this->options['mimefile']) && file_exists($this->options['mimefile'])) {
710
				$file = $this->options['mimefile'];
711
			} elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) {
712
				$file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types';
713
			} elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) {
714
				$file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types';
715
			}
716
717
			if ($file && file_exists($file)) {
718
				$mimecf = file($file);
719
720
				foreach ($mimecf as $line_num => $line) {
721
					if (!preg_match('/^\s*#/', $line)) {
722
						$mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
723
						for ($i = 1, $size = count($mime); $i < $size ; $i++) {
724
							if (!isset(self::$mimetypes[$mime[$i]])) {
725
								self::$mimetypes[$mime[$i]] = $mime[0];
726
							}
727
						}
728
					}
729
				}
730
			}
731
		}
732
733
		$this->rootName = empty($this->options['alias']) ? $this->_basename($this->root) : $this->options['alias'];
734
		$root = $this->stat($this->root);
735
		
736
		if (!$root) {
737
			return $this->setError('Root folder does not exists.');
738
		}
739
		if (!$root['read'] && !$root['write']) {
740
			return $this->setError('Root folder has not read and write permissions.');
741
		}
742
		
743
		// debug($root);
744
		
745
		if ($root['read']) {
746
			// check startPath - path to open by default instead of root
747
			if ($this->options['startPath']) {
748
				$start = $this->stat($this->options['startPath']);
749
				if (!empty($start)
750
				&& $start['mime'] == 'directory'
751
				&& $start['read']
752
				&& empty($start['hidden'])
753
				&& $this->_inpath($this->options['startPath'], $this->root)) {
754
					$this->startPath = $this->options['startPath'];
755
					if (substr($this->startPath, -1, 1) == $this->options['separator']) {
756
						$this->startPath = substr($this->startPath, 0, -1);
757
					}
758
				}
759
			}
760
		} else {
761
			$this->options['URL']     = '';
762
			$this->options['tmbURL']  = '';
763
			$this->options['tmbPath'] = '';
764
			// read only volume
765
			array_unshift($this->attributes, array(
766
				'pattern' => '/.*/',
767
				'read'    => false
768
			));
769
		}
770
		$this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1;
771
		$this->tmbSize  = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48;
772
		$this->URL      = $this->options['URL'];
773
		if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) {
774
			$this->URL .= '/';
775
		}
776
777
		$this->tmbURL   = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : '';
778
		if ($this->tmbURL && preg_match("|[^/?&=]$|", $this->tmbURL)) {
779
			$this->tmbURL .= '/';
780
		}
781
		
782
		$this->nameValidator = !empty($this->options['acceptedName']) && (is_string($this->options['acceptedName']) || is_callable($this->options['acceptedName']))
783
			? $this->options['acceptedName']
784
			: '';
785
786
		$this->_checkArchivers();
787
		// manual control archive types to create
788 View Code Duplication
		if (!empty($this->options['archiveMimes']) && is_array($this->options['archiveMimes'])) {
789
			foreach ($this->archivers['create'] as $mime => $v) {
790
				if (!in_array($mime, $this->options['archiveMimes'])) {
791
					unset($this->archivers['create'][$mime]);
792
				}
793
			}
794
		}
795
		
796
		// manualy add archivers
797 View Code Duplication
		if (!empty($this->options['archivers']['create']) && is_array($this->options['archivers']['create'])) {
798
			foreach ($this->options['archivers']['create'] as $mime => $conf) {
799
				if (strpos($mime, 'application/') === 0 
800
				&& !empty($conf['cmd']) 
801
				&& isset($conf['argc']) 
802
				&& !empty($conf['ext'])
803
				&& !isset($this->archivers['create'][$mime])) {
804
					$this->archivers['create'][$mime] = $conf;
805
				}
806
			}
807
		}
808
		
809 View Code Duplication
		if (!empty($this->options['archivers']['extract']) && is_array($this->options['archivers']['extract'])) {
810
			foreach ($this->options['archivers']['extract'] as $mime => $conf) {
811
				if (strpos($mime, 'application/') === 0
812
				&& !empty($conf['cmd']) 
813
				&& isset($conf['argc']) 
814
				&& !empty($conf['ext'])
815
				&& !isset($this->archivers['extract'][$mime])) {
816
					$this->archivers['extract'][$mime] = $conf;
817
				}
818
			}
819
		}
820
821
		$this->configure();
822
		// echo $this->uploadMaxSize;
823
		// echo $this->options['uploadMaxSize'];
824
		return $this->mounted = true;
825
	}
826
	
827
	/**
828
	 * Some "unmount" stuffs - may be required by virtual fs
829
	 *
830
	 * @return void
831
	 * @author Dmitry (dio) Levashov
832
	 **/
833
	public function umount() {
834
	}
835
	
836
	/**
837
	 * Return error message from last failed action
838
	 *
839
	 * @return array
840
	 * @author Dmitry (dio) Levashov
841
	 **/
842
	public function error() {
843
		return $this->error;
844
	}
845
	
846
	/**
847
	 * Set mimetypes allowed to display to client
848
	 *
849
	 * @param  array  $mimes
850
	 * @return void
851
	 * @author Dmitry (dio) Levashov
852
	 **/
853
	public function setMimesFilter($mimes) {
854
		if (is_array($mimes)) {
855
			$this->onlyMimes = $mimes;
856
		}
857
	}
858
	
859
	/**
860
	 * Return root folder hash
861
	 *
862
	 * @return string
863
	 * @author Dmitry (dio) Levashov
864
	 **/
865
	public function root() {
866
		return $this->encode($this->root);
867
	}
868
	
869
	/**
870
	 * Return root or startPath hash
871
	 *
872
	 * @return string
873
	 * @author Dmitry (dio) Levashov
874
	 **/
875
	public function defaultPath() {
876
		return $this->encode($this->startPath ? $this->startPath : $this->root);
877
	}
878
		
879
	/**
880
	 * Return volume options required by client:
881
	 *
882
	 * @return array
883
	 * @author Dmitry (dio) Levashov
884
	 **/
885
	public function options($hash) {
886
		return array(
887
			'path'          => $this->_path($this->decode($hash)),
888
			'url'           => $this->URL,
889
			'tmbUrl'        => $this->tmbURL,
890
			'disabled'      => $this->disabled,
891
			'separator'     => $this->separator,
892
			'copyOverwrite' => intval($this->options['copyOverwrite']),
893
			'archivers'     => array(
894
				// 'create'  => array_keys($this->archivers['create']),
895
				// 'extract' => array_keys($this->archivers['extract']),
896
				'create'  => isset($this->archivers['create']) && is_array($this->archivers['create'])  ? array_keys($this->archivers['create'])  : array(),
897
				'extract' => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array()
898
			)
899
		);
900
	}
901
	
902
	/**
903
	 * Return true if command disabled in options
904
	 *
905
	 * @param  string  $cmd  command name
906
	 * @return bool
907
	 * @author Dmitry (dio) Levashov
908
	 **/
909
	public function commandDisabled($cmd) {
910
		return in_array($cmd, $this->disabled);
911
	}
912
	
913
	/**
914
	 * Return true if mime is required mimes list
915
	 *
916
	 * @param  string     $mime   mime type to check
917
	 * @param  array      $mimes  allowed mime types list or not set to use client mimes list
918
	 * @param  bool|null  $empty  what to return on empty list
919
	 * @return bool|null
920
	 * @author Dmitry (dio) Levashov
921
	 * @author Troex Nevelin
922
	 **/
923
	public function mimeAccepted($mime, $mimes = array(), $empty = true) {
924
		$mimes = !empty($mimes) ? $mimes : $this->onlyMimes;
925
		if (empty($mimes)) {
926
			return $empty;
927
		}
928
		return $mime == 'directory'
929
			|| in_array('all', $mimes)
930
			|| in_array('All', $mimes)
931
			|| in_array($mime, $mimes)
932
			|| in_array(substr($mime, 0, strpos($mime, '/')), $mimes);
933
	}
934
	
935
	/**
936
	 * Return true if voume is readable.
937
	 *
938
	 * @return bool
939
	 * @author Dmitry (dio) Levashov
940
	 **/
941
	public function isReadable() {
942
		$stat = $this->stat($this->root);
943
		return $stat['read'];
944
	}
945
	
946
	/**
947
	 * Return true if copy from this volume allowed
948
	 *
949
	 * @return bool
950
	 * @author Dmitry (dio) Levashov
951
	 **/
952
	public function copyFromAllowed() {
953
		return !!$this->options['copyFrom'];
954
	}
955
	
956
	/**
957
	 * Return file path related to root
958
	 *
959
	 * @param  string   $hash  file hash
960
	 * @return string
961
	 * @author Dmitry (dio) Levashov
962
	 **/
963
	public function path($hash) {
964
		return $this->_path($this->decode($hash));
965
	}
966
	
967
	/**
968
	 * Return file real path if file exists
969
	 *
970
	 * @param  string  $hash  file hash
971
	 * @return string
972
	 * @author Dmitry (dio) Levashov
973
	 **/
974
	public function realpath($hash) {
975
		$path = $this->decode($hash);
976
		return $this->stat($path) ? $path : false;
977
	}
978
	
979
	/**
980
	 * Return list of moved/overwrited files
981
	 *
982
	 * @return array
983
	 * @author Dmitry (dio) Levashov
984
	 **/
985
	public function removed() {
986
		return $this->removed;
987
	}
988
	
989
	/**
990
	 * Clean removed files list
991
	 *
992
	 * @return void
993
	 * @author Dmitry (dio) Levashov
994
	 **/
995
	public function resetRemoved() {
996
		$this->removed = array();
997
	}
998
	
999
	/**
1000
	 * Return file/dir hash or first founded child hash with required attr == $val
1001
	 *
1002
	 * @param  string   $hash  file hash
1003
	 * @param  string   $attr  attribute name
1004
	 * @param  bool     $val   attribute value
1005
	 * @return string|false
1006
	 * @author Dmitry (dio) Levashov
1007
	 **/
1008
	public function closest($hash, $attr, $val) {
1009
		return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false;
1010
	}
1011
	
1012
	/**
1013
	 * Return file info or false on error
1014
	 *
1015
	 * @param  string   $hash      file hash
1016
	 * @param  bool     $realpath  add realpath field to file info
1017
	 * @return array|false
1018
	 * @author Dmitry (dio) Levashov
1019
	 **/
1020
	public function file($hash) {
1021
		$path = $this->decode($hash);
1022
		
1023
		return ($file = $this->stat($path)) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1024
	}
1025
	
1026
	/**
1027
	 * Return folder info
1028
	 *
1029
	 * @param  string   $hash  folder hash
1030
	 * @param  bool     $hidden  return hidden file info
1031
	 * @return array|false
1032
	 * @author Dmitry (dio) Levashov
1033
	 **/
1034
	public function dir($hash, $resolveLink=false) {
1035
		if (($dir = $this->file($hash)) == false) {
1036
			return $this->setError(elFinder::ERROR_DIR_NOT_FOUND);
1037
		}
1038
1039
		if ($resolveLink && !empty($dir['thash'])) {
1040
			$dir = $this->file($dir['thash']);
1041
		}
1042
		
1043
		return $dir && $dir['mime'] == 'directory' && empty($dir['hidden']) 
1044
			? $dir 
1045
			: $this->setError(elFinder::ERROR_NOT_DIR);
1046
	}
1047
	
1048
	/**
1049
	 * Return directory content or false on error
1050
	 *
1051
	 * @param  string   $hash   file hash
1052
	 * @return array|false
1053
	 * @author Dmitry (dio) Levashov
1054
	 **/
1055
	public function scandir($hash) {
1056
		if (($dir = $this->dir($hash)) == false) {
1057
			return false;
1058
		}
1059
		
1060
		return $dir['read']
1061
			? $this->getScandir($this->decode($hash))
1062
			: $this->setError(elFinder::ERROR_PERM_DENIED);
1063
	}
1064
1065
	/**
1066
	 * Return dir files names list
1067
	 * 
1068
	 * @param  string  $hash   file hash
1069
	 * @return array
1070
	 * @author Dmitry (dio) Levashov
1071
	 **/
1072
	public function ls($hash) {
1073
		if (($dir = $this->dir($hash)) == false || !$dir['read']) {
1074
			return false;
1075
		}
1076
		
1077
		$list = array();
1078
		$path = $this->decode($hash);
1079
		
1080
		foreach ($this->getScandir($path) as $stat) {
1081
			if (empty($stat['hidden']) && $this->mimeAccepted($stat['mime'])) {
1082
				$list[] = $stat['name'];
1083
			}
1084
		}
1085
1086
		return $list;
1087
	}
1088
1089
	/**
1090
	 * Return subfolders for required folder or false on error
1091
	 *
1092
	 * @param  string   $hash  folder hash or empty string to get tree from root folder
1093
	 * @param  int      $deep  subdir deep
1094
	 * @param  string   $exclude  dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders
1095
	 * @return array|false
1096
	 * @author Dmitry (dio) Levashov
1097
	 **/
1098
	public function tree($hash='', $deep=0, $exclude='') {
1099
		$path = $hash ? $this->decode($hash) : $this->root;
1100
		
1101 View Code Duplication
		if (($dir = $this->stat($path)) == false || $dir['mime'] != 'directory') {
1102
			return false;
1103
		}
1104
		
1105
		$dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $exclude ? $this->decode($exclude) : null);
1106
		array_unshift($dirs, $dir);
1107
		return $dirs;
1108
	}
1109
	
1110
	/**
1111
	 * Return part of dirs tree from required dir up to root dir
1112
	 *
1113
	 * @param  string    $hash   directory hash
1114
	 * @param  bool|null $lineal only lineal parents
1115
	 * @return array
1116
	 * @author Dmitry (dio) Levashov
1117
	 **/
1118
	public function parents($hash, $lineal = false) {
1119
		if (($current = $this->dir($hash)) == false) {
1120
			return false;
1121
		}
1122
1123
		$path = $this->decode($hash);
1124
		$tree = array();
1125
		
1126
		while ($path && $path != $this->root) {
1127
			$path = $this->_dirname($path);
1128
			$stat = $this->stat($path);
1129
			if (!empty($stat['hidden']) || !$stat['read']) {
1130
				return false;
1131
			}
1132
			
1133
			array_unshift($tree, $stat);
1134
			if (!$lineal && ($path != $this->root)) {
1135
				foreach ($this->gettree($path, 0) as $dir) {
1136
					if (!in_array($dir, $tree)) {
1137
						$tree[] = $dir;
1138
					}
1139
				}
1140
			}
1141
		}
1142
1143
		return $tree ? $tree : array($current);
1144
	}
1145
	
1146
	/**
1147
	 * Create thumbnail for required file and return its name of false on failed
1148
	 *
1149
	 * @return string|false
1150
	 * @author Dmitry (dio) Levashov
1151
	 **/
1152
	public function tmb($hash) {
1153
		$path = $this->decode($hash);
1154
		$stat = $this->stat($path);
1155
		
1156
		if (isset($stat['tmb'])) {
1157
			return $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb'];
1158
		}
1159
		return false;
1160
	}
1161
	
1162
	/**
1163
	 * Return file size / total directory size
1164
	 *
1165
	 * @param  string   file hash
1166
	 * @return int
1167
	 * @author Dmitry (dio) Levashov
1168
	 **/
1169
	public function size($hash) {
1170
		return $this->countSize($this->decode($hash));
1171
	}
1172
	
1173
	/**
1174
	 * Open file for reading and return file pointer
1175
	 *
1176
	 * @param  string   file hash
1177
	 * @return Resource
1178
	 * @author Dmitry (dio) Levashov
1179
	 **/
1180
	public function open($hash) {
1181 View Code Duplication
		if (($file = $this->file($hash)) == false
1182
		|| $file['mime'] == 'directory') {
1183
			return false;
1184
		}
1185
		
1186
		return $this->_fopen($this->decode($hash), 'rb');
1187
	}
1188
	
1189
	/**
1190
	 * Close file pointer
1191
	 *
1192
	 * @param  Resource  $fp   file pointer
1193
	 * @param  string    $hash file hash
1194
	 * @return void
1195
	 * @author Dmitry (dio) Levashov
1196
	 **/
1197
	public function close($fp, $hash) {
1198
		$this->_fclose($fp, $this->decode($hash));
1199
	}
1200
	
1201
	/**
1202
	 * Create directory and return dir info
1203
	 *
1204
	 * @param  string   $dst  destination directory
1205
	 * @param  string   $name directory name
1206
	 * @return array|false
1207
	 * @author Dmitry (dio) Levashov
1208
	 **/
1209
	public function mkdir($dst, $name) {
1210
		if ($this->commandDisabled('mkdir')) {
1211
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1212
		}
1213
		
1214
		if (!$this->nameAccepted($name)) {
1215
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1216
		}
1217
		
1218 View Code Duplication
		if (($dir = $this->dir($dst)) == false) {
1219
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1220
		}
1221
		
1222
		if (!$dir['write']) {
1223
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1224
		}
1225
		
1226
		$path = $this->decode($dst);
1227
		$dst  = $this->_joinPath($path, $name);
1228
		$stat = $this->stat($dst); 
1229
		if (!empty($stat)) { 
1230
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1231
		}
1232
		$this->clearcache();
1233
		return ($path = $this->_mkdir($path, $name)) ? $this->stat($path) : false;
1234
	}
1235
	
1236
	/**
1237
	 * Create empty file and return its info
1238
	 *
1239
	 * @param  string   $dst  destination directory
1240
	 * @param  string   $name file name
1241
	 * @return array|false
1242
	 * @author Dmitry (dio) Levashov
1243
	 **/
1244
	public function mkfile($dst, $name) {
1245
		if ($this->commandDisabled('mkfile')) {
1246
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1247
		}
1248
		
1249
		if (!$this->nameAccepted($name)) {
1250
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1251
		}
1252
		
1253 View Code Duplication
		if (($dir = $this->dir($dst)) == false) {
1254
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1255
		}
1256
		
1257
		$path = $this->decode($dst);
1258
		
1259
		if (!$dir['write'] || !$this->allowCreate($path, $name)) {
1260
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1261
		}
1262
		
1263
		if ($this->stat($this->_joinPath($path, $name))) {
1264
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1265
		}
1266
		
1267
		$this->clearcache();
1268
		return ($path = $this->_mkfile($path, $name)) ? $this->stat($path) : false;
1269
	}
1270
	
1271
	/**
1272
	 * Rename file and return file info
1273
	 *
1274
	 * @param  string  $hash  file hash
1275
	 * @param  string  $name  new file name
1276
	 * @return array|false
1277
	 * @author Dmitry (dio) Levashov
1278
	 **/
1279
	public function rename($hash, $name) {
1280
		if ($this->commandDisabled('rename')) {
1281
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1282
		}
1283
		
1284
		if (!$this->nameAccepted($name)) {
1285
			return $this->setError(elFinder::ERROR_INVALID_NAME, $name);
1286
		}
1287
		
1288
		if (!($file = $this->file($hash))) {
1289
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1290
		}
1291
		
1292
		if ($name == $file['name']) {
1293
			return $file;
1294
		}
1295
		
1296
		if (!empty($file['locked'])) {
1297
			return $this->setError(elFinder::ERROR_LOCKED, $file['name']);
1298
		}
1299
		
1300
		$path = $this->decode($hash);
1301
		$dir  = $this->_dirname($path);
1302
		$stat = $this->stat($this->_joinPath($dir, $name));
1303
		if ($stat) {
1304
			return $this->setError(elFinder::ERROR_EXISTS, $name);
1305
		}
1306
		
1307
		if (!$this->allowCreate($dir, $name)) {
1308
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1309
		}
1310
1311
		$this->rmTmb($file); // remove old name tmbs, we cannot do this after dir move
1312
1313
1314
		if (($path = $this->_move($path, $dir, $name))) {
1315
			$this->clearcache();
1316
			return $this->stat($path);
1317
		}
1318
		return false;
1319
	}
1320
	
1321
	/**
1322
	 * Create file copy with suffix "copy number" and return its info
1323
	 *
1324
	 * @param  string   $hash    file hash
1325
	 * @param  string   $suffix  suffix to add to file name
1326
	 * @return array|false
1327
	 * @author Dmitry (dio) Levashov
1328
	 **/
1329
	public function duplicate($hash, $suffix='copy') {
1330
		if ($this->commandDisabled('duplicate')) {
1331
			return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED);
1332
		}
1333
		
1334 View Code Duplication
		if (($file = $this->file($hash)) == false) {
1335
			return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND);
1336
		}
1337
1338
		$path = $this->decode($hash);
1339
		$dir  = $this->_dirname($path);
1340
		$name = $this->uniqueName($dir, $this->_basename($path), ' '.$suffix.' ');
1341
1342
		if (!$this->allowCreate($dir, $name)) {
1343
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1344
		}
1345
1346
		return ($path = $this->copy($path, $dir, $name)) == false
1347
			? false
1348
			: $this->stat($path);
1349
	}
1350
	
1351
	/**
1352
	 * Save uploaded file. 
1353
	 * On success return array with new file stat and with removed file hash (if existed file was replaced)
1354
	 *
1355
	 * @param  Resource $fp      file pointer
1356
	 * @param  string   $dst     destination folder hash
1357
	 * @param  string   $src     file name
1358
	 * @param  string   $tmpname file tmp name - required to detect mime type
1359
	 * @return array|false
1360
	 * @author Dmitry (dio) Levashov
1361
	 **/
1362
	public function upload($fp, $dst, $name, $tmpname) {
1363
		if ($this->commandDisabled('upload')) {
1364
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1365
		}
1366
		
1367 View Code Duplication
		if (($dir = $this->dir($dst)) == false) {
1368
			return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1369
		}
1370
1371
		if (!$dir['write']) {
1372
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1373
		}
1374
		
1375
		if (!$this->nameAccepted($name)) {
1376
			return $this->setError(elFinder::ERROR_INVALID_NAME);
1377
		}
1378
		
1379
		$mime = $this->mimetype($this->mimeDetect == 'internal' ? $name : $tmpname, $name); 
1380
		if ($mime == 'unknown' && $this->mimeDetect == 'internal') {
1381
			$mime = elFinderVolumeDriver::mimetypeInternalDetect($name);
1382
		}
1383
1384
		// logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order
1385
		$allow  = $this->mimeAccepted($mime, $this->uploadAllow, null);
1386
		$deny   = $this->mimeAccepted($mime, $this->uploadDeny,  null);
1387
		$upload = true; // default to allow
1388
		if (strtolower($this->uploadOrder[0]) == 'allow') { // array('allow', 'deny'), default is to 'deny'
1389
			$upload = false; // default is deny
1390
			if (!$deny && ($allow === true)) { // match only allow
1391
				$upload = true;
1392
			}// else (both match | no match | match only deny) { deny }
1393
		} else { // array('deny', 'allow'), default is to 'allow' - this is the default rule
1394
			$upload = true; // default is allow
1395
			if (($deny === true) && !$allow) { // match only deny
1396
				$upload = false;
1397
			} // else (both match | no match | match only allow) { allow }
1398
		}
1399
		if (!$upload) {
1400
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME);
1401
		}
1402
1403
		if ($this->uploadMaxSize > 0 && filesize($tmpname) > $this->uploadMaxSize) {
1404
			return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE);
1405
		}
1406
1407
		$dstpath = $this->decode($dst);
1408
		$test    = $this->_joinPath($dstpath, $name);
1409
		
1410
		$file = $this->stat($test);
1411
		$this->clearcache();
1412
		
1413
		if ($file) { // file exists
1414
			if ($this->options['uploadOverwrite']) {
1415
				if (!$file['write']) {
1416
					return $this->setError(elFinder::ERROR_PERM_DENIED);
1417
				} elseif ($file['mime'] == 'directory') {
1418
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $name);
1419
				} 
1420
				$this->remove($test);
1421
			} else {
1422
				$name = $this->uniqueName($dstpath, $name, '-', false);
1423
			}
1424
		}
1425
		
1426
		$stat = array(
1427
			'mime'   => $mime, 
1428
			'width'  => 0, 
1429
			'height' => 0, 
1430
			'size'   => filesize($tmpname));
1431
		
1432
		// $w = $h = 0;
1433
		if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) {
1434
			$stat['width'] = $s[0];
1435
			$stat['height'] = $s[1];
1436
		}
1437
		// $this->clearcache();
1438
		if (($path = $this->_save($fp, $dstpath, $name, $stat)) == false) {
1439
			return false;
1440
		}
1441
		
1442
		
1443
1444
		return $this->stat($path);
1445
	}
1446
	
1447
	/**
1448
	 * Paste files
1449
	 *
1450
	 * @param  Object  $volume  source volume
1451
	 * @param  string  $source  file hash
1452
	 * @param  string  $dst     destination dir hash
1453
	 * @param  bool    $rmSrc   remove source after copy?
1454
	 * @return array|false
1455
	 * @author Dmitry (dio) Levashov
1456
	 **/
1457
	public function paste($volume, $src, $dst, $rmSrc = false) {
1458
		$err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY;
1459
		
1460
		if ($this->commandDisabled('paste')) {
1461
			return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED);
1462
		}
1463
1464
		if (($file = $volume->file($src, $rmSrc)) == false) {
1465
			return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND);
1466
		}
1467
1468
		$name = $file['name'];
1469
		$errpath = $volume->path($src);
1470
		
1471 View Code Duplication
		if (($dir = $this->dir($dst)) == false) {
1472
			return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst);
1473
		}
1474
		
1475
		if (!$dir['write'] || !$file['read']) {
1476
			return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1477
		}
1478
1479
		$destination = $this->decode($dst);
1480
1481
		if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) {
1482
			return $rmSrc
1483
				? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test))
1484
				: $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1485
		}
1486
1487
		$test = $this->_joinPath($destination, $name);
1488
		$stat = $this->stat($test);
1489
		$this->clearcache();
1490
		if ($stat) {
1491
			if ($this->options['copyOverwrite']) {
1492
				// do not replace file with dir or dir with file
1493
				if (!$this->isSameType($file['mime'], $stat['mime'])) {
1494
					return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->_path($test));
1495
				}
1496
				// existed file is not writable
1497
				if (!$stat['write']) {
1498
					return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED);
1499
				}
1500
				// existed file locked or has locked child
1501
				if (($locked = $this->closestByAttr($test, 'locked', true))) {
1502
					return $this->setError(elFinder::ERROR_LOCKED, $this->_path($locked));
1503
				}
1504
				// target is entity file of alias
1505
				if ($volume == $this && ($test == @$file['target'] || $test == $this->decode($src))) {
1506
					return $this->setError(elFinder::ERROR_REPLACE, $errpath);
1507
				}
1508
				// remove existed file
1509
				if (!$this->remove($test)) {
1510
					return $this->setError(elFinder::ERROR_REPLACE, $this->_path($test));
1511
				}
1512
			} else {
1513
				$name = $this->uniqueName($destination, $name, ' ', false);
1514
			}
1515
		}
1516
		
1517
		// copy/move inside current volume
1518
		if ($volume == $this) {
1519
			$source = $this->decode($src);
1520
			// do not copy into itself
1521
			if ($this->_inpath($destination, $source)) {
1522
				return $this->setError(elFinder::ERROR_COPY_INTO_ITSELF, $errpath);
1523
			}
1524
			$method = $rmSrc ? 'move' : 'copy';
1525
			return ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false;
1526
		}
1527
		
1528
		// copy/move from another volume
1529
		if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) {
1530
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
1531
		}
1532
		
1533
		if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) {
1534
			return false;
1535
		}
1536
		
1537
		if ($rmSrc) {
1538
			if ($volume->rm($src)) {
1539
				$this->removed[] = $file;
1540
			} else {
1541
				return $this->setError(elFinder::ERROR_MOVE, $errpath, elFinder::ERROR_RM_SRC);
1542
			}
1543
		}
1544
		return $this->stat($path);
1545
	}
1546
	
1547
	/**
1548
	 * Return file contents
1549
	 *
1550
	 * @param  string  $hash  file hash
1551
	 * @return string|false
1552
	 * @author Dmitry (dio) Levashov
1553
	 **/
1554
	public function getContents($hash) {
1555
		$file = $this->file($hash);
1556
		
1557
		if (!$file) {
1558
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1559
		}
1560
		
1561
		if ($file['mime'] == 'directory') {
1562
			return $this->setError(elFinder::ERROR_NOT_FILE);
1563
		}
1564
		
1565
		if (!$file['read']) {
1566
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1567
		}
1568
		
1569
		return $this->_getContents($this->decode($hash));
1570
	}
1571
	
1572
	/**
1573
	 * Put content in text file and return file info.
1574
	 *
1575
	 * @param  string  $hash     file hash
1576
	 * @param  string  $content  new file content
1577
	 * @return array
1578
	 * @author Dmitry (dio) Levashov
1579
	 **/
1580
	public function putContents($hash, $content) {
1581
		if ($this->commandDisabled('edit')) {
1582
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1583
		}
1584
		
1585
		$path = $this->decode($hash);
1586
		
1587
		if (!($file = $this->file($hash))) {
1588
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1589
		}
1590
		
1591
		if (!$file['write']) {
1592
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1593
		}
1594
		$this->clearcache();
1595
		return $this->_filePutContents($path, $content) ? $this->stat($path) : false;
1596
	}
1597
	
1598
	/**
1599
	 * Extract files from archive
1600
	 *
1601
	 * @param  string  $hash  archive hash
1602
	 * @return array|bool
1603
	 * @author Dmitry (dio) Levashov, 
1604
	 * @author Alexey Sukhotin
1605
	 **/
1606
	public function extract($hash) {
1607
		if ($this->commandDisabled('extract')) {
1608
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1609
		}
1610
		
1611
		if (($file = $this->file($hash)) == false) {
1612
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1613
		}
1614
		
1615
		$archiver = isset($this->archivers['extract'][$file['mime']])
1616
			? $this->archivers['extract'][$file['mime']]
1617
			: false;
1618
			
1619
		if (!$archiver) {
1620
			return $this->setError(elFinder::ERROR_NOT_ARCHIVE);
1621
		}
1622
		
1623
		$path   = $this->decode($hash);
1624
		$parent = $this->stat($this->_dirname($path));
1625
1626
		if (!$file['read'] || !$parent['write']) {
1627
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1628
		}
1629
		$this->clearcache();
1630
		return ($path = $this->_extract($path, $archiver)) ? $this->stat($path) : false;
1631
	}
1632
1633
	/**
1634
	 * Add files to archive
1635
	 *
1636
	 * @return void
1637
	 **/
1638
	public function archive($hashes, $mime) {
1639
		if ($this->commandDisabled('archive')) {
1640
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1641
		}
1642
1643
		$archiver = isset($this->archivers['create'][$mime])
1644
			? $this->archivers['create'][$mime]
1645
			: false;
1646
			
1647
		if (!$archiver) {
1648
			return $this->setError(elFinder::ERROR_ARCHIVE_TYPE);
1649
		}
1650
		
1651
		$files = array();
1652
		
1653
		foreach ($hashes as $hash) {
1654
			if (($file = $this->file($hash)) == false) {
1655
				return $this->error(elFinder::ERROR_FILE_NOT_FOUND, '#'+$hash);
1656
			}
1657
			if (!$file['read']) {
1658
				return $this->error(elFinder::ERROR_PERM_DENIED);
1659
			}
1660
			$path = $this->decode($hash);
1661
			if (!isset($dir)) {
1662
				$dir = $this->_dirname($path);
1663
				$stat = $this->stat($dir);
1664
				if (!$stat['write']) {
1665
					return $this->error(elFinder::ERROR_PERM_DENIED);
1666
				}
1667
			}
1668
			
1669
			$files[] = $this->_basename($path);
1670
		}
1671
		
1672
		$name = (count($files) == 1 ? $files[0] : 'Archive').'.'.$archiver['ext'];
1673
		$name = $this->uniqueName($dir, $name, '');
1674
		$this->clearcache();
1675
		return ($path = $this->_archive($dir, $files, $name, $archiver)) ? $this->stat($path) : false;
1676
	}
1677
	
1678
	/**
1679
	 * Resize image
1680
	 *
1681
	 * @param  string   $hash    image file
1682
	 * @param  int      $width   new width
1683
	 * @param  int      $height  new height
1684
	 * @param  int      $x       X start poistion for crop
1685
	 * @param  int      $y       Y start poistion for crop
1686
	 * @param  string   $mode    action how to mainpulate image
1687
	 * @return array|false
1688
	 * @author Dmitry (dio) Levashov
1689
	 * @author Alexey Sukhotin
1690
	 * @author nao-pon
1691
	 * @author Troex Nevelin
1692
	 **/
1693
	public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) {
1694
		if ($this->commandDisabled('resize')) {
1695
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1696
		}
1697
		
1698
		if (($file = $this->file($hash)) == false) {
1699
			return $this->setError(elFinder::ERROR_FILE_NOT_FOUND);
1700
		}
1701
		
1702
		if (!$file['write'] || !$file['read']) {
1703
			return $this->setError(elFinder::ERROR_PERM_DENIED);
1704
		}
1705
		
1706
		$path = $this->decode($hash);
1707
		
1708
		if (!$this->canResize($path, $file)) {
1709
			return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE);
1710
		}
1711
1712 View Code Duplication
		switch($mode) {
1713
			
1714
			case 'propresize':
1715
				$result = $this->imgResize($path, $width, $height, true, true);
1716
				break;
1717
1718
			case 'crop':
1719
				$result = $this->imgCrop($path, $width, $height, $x, $y);
1720
				break;
1721
1722
			case 'fitsquare':
1723
				$result = $this->imgSquareFit($path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']));
1724
				break;
1725
1726
			case 'rotate':
1727
				$result = $this->imgRotate($path, $degree, ($bg ? $bg : $this->options['tmbBgColor']));
1728
				break;
1729
1730
			default:
1731
				$result = $this->imgResize($path, $width, $height, false, true);
1732
				break;
1733
		}
1734
1735
		if ($result) {
1736
			$this->rmTmb($file);
1737
			$this->clearcache();
1738
			return $this->stat($path);
1739
		}
1740
		
1741
		return false;
1742
	}
1743
	
1744
	/**
1745
	 * Remove file/dir
1746
	 *
1747
	 * @param  string  $hash  file hash
1748
	 * @return bool
1749
	 * @author Dmitry (dio) Levashov
1750
	 **/
1751
	public function rm($hash) {
1752
		return $this->commandDisabled('rm')
1753
			? $this->setError(elFinder::ERROR_PERM_DENIED)
1754
			: $this->remove($this->decode($hash));
1755
	}
1756
	
1757
	/**
1758
	 * Search files
1759
	 *
1760
	 * @param  string  $q  search string
1761
	 * @param  array   $mimes
1762
	 * @return array
1763
	 * @author Dmitry (dio) Levashov
1764
	 **/
1765
	public function search($q, $mimes) {
1766
		return $this->commandDisabled('search')
1767
			? array()
1768
			: $this->doSearch($this->root, $q, $mimes);
1769
	}
1770
	
1771
	/**
1772
	 * Return image dimensions
1773
	 *
1774
	 * @param  string  $hash  file hash
1775
	 * @return array
1776
	 * @author Dmitry (dio) Levashov
1777
	 **/
1778
	public function dimensions($hash) {
1779
		if (($file = $this->file($hash)) == false) {
1780
			return false;
1781
		}
1782
		
1783
		return $this->_dimensions($this->decode($hash), $file['mime']);
1784
	}
1785
	
1786
	/**
1787
	 * Save error message
1788
	 *
1789
	 * @param  array  error 
1790
	 * @return false
1791
	 * @author Dmitry(dio) Levashov
1792
	 **/
1793
	protected function setError($error) {
1794
		
1795
		$this->error = array();
1796
		
1797
		foreach (func_get_args() as $err) {
1798
			if (is_array($err)) {
1799
				$this->error = array_merge($this->error, $err);
1800
			} else {
1801
				$this->error[] = $err;
1802
			}
1803
		}
1804
		
1805
		// $this->error = is_array($error) ? $error : func_get_args();
1806
		return false;
1807
	}
1808
	
1809
	/*********************************************************************/
1810
	/*                               FS API                              */
1811
	/*********************************************************************/
1812
	
1813
	/***************** paths *******************/
1814
	
1815
	/**
1816
	 * Encode path into hash
1817
	 *
1818
	 * @param  string  file path
1819
	 * @return string
1820
	 * @author Dmitry (dio) Levashov
1821
	 * @author Troex Nevelin
1822
	 **/
1823
	protected function encode($path) {
1824
		if ($path !== '') {
1825
1826
			// cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root
1827
			$p = $this->_relpath($path);
1828
			// if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt
1829
			if ($p === '')	{
1830
				$p = DIRECTORY_SEPARATOR;
1831
			}
1832
1833
			// TODO crypt path and return hash
1834
			$hash = $this->crypt($p);
1835
			// hash is used as id in HTML that means it must contain vaild chars
1836
			// make base64 html safe and append prefix in begining
1837
			$hash = strtr(base64_encode($hash), '+/=', '-_.');
1838
			// remove dots '.' at the end, before it was '=' in base64
1839
			$hash = rtrim($hash, '.'); 
1840
			// append volume id to make hash unique
1841
			return $this->id.$hash;
1842
		}
1843
	}
1844
	
1845
	/**
1846
	 * Decode path from hash
1847
	 *
1848
	 * @param  string  file hash
1849
	 * @return string
1850
	 * @author Dmitry (dio) Levashov
1851
	 * @author Troex Nevelin
1852
	 **/
1853
	protected function decode($hash) {
1854
		if (strpos($hash, $this->id) === 0) {
1855
			// cut volume id after it was prepended in encode
1856
			$h = substr($hash, strlen($this->id));
1857
			// replace HTML safe base64 to normal
1858
			$h = base64_decode(strtr($h, '-_.', '+/='));
1859
			// TODO uncrypt hash and return path
1860
			$path = $this->uncrypt($h); 
1861
			// append ROOT to path after it was cut in encode
1862
			return $this->_abspath($path);//$this->root.($path == DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR.$path); 
1863
		}
1864
	}
1865
	
1866
	/**
1867
	 * Return crypted path 
1868
	 * Not implemented
1869
	 *
1870
	 * @param  string  path
1871
	 * @return mixed
1872
	 * @author Dmitry (dio) Levashov
1873
	 **/
1874
	protected function crypt($path) {
1875
		return $path;
1876
	}
1877
	
1878
	/**
1879
	 * Return uncrypted path 
1880
	 * Not implemented
1881
	 *
1882
	 * @param  mixed  hash
1883
	 * @return mixed
1884
	 * @author Dmitry (dio) Levashov
1885
	 **/
1886
	protected function uncrypt($hash) {
1887
		return $hash;
1888
	}
1889
	
1890
	/**
1891
	 * Validate file name based on $this->options['acceptedName'] regexp or function
1892
	 *
1893
	 * @param  string  $name  file name
1894
	 * @return bool
1895
	 * @author Dmitry (dio) Levashov
1896
	 **/
1897
	protected function nameAccepted($name) {
1898
		if ($this->nameValidator) {
1899
			if (is_callable($this->nameValidator)) {
1900
				$res = call_user_func($this->nameValidator, $name);
1901
				return $res;
1902
			}
1903
			if (preg_match($this->nameValidator, '') !== false) {
1904
				return preg_match($this->nameValidator, $name);
1905
			}
1906
		}
1907
		return true;
1908
	}
1909
	
1910
	/**
1911
	 * Return new unique name based on file name and suffix
1912
	 *
1913
	 * @param  string  $path    file path
1914
	 * @param  string  $suffix  suffix append to name
1915
	 * @return string
1916
	 * @author Dmitry (dio) Levashov
1917
	 **/
1918
	public function uniqueName($dir, $name, $suffix = ' copy', $checkNum=true) {
1919
		$ext  = '';
1920
1921
		if (preg_match('/\.((tar\.(gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(gz|bz2)|[a-z0-9]{1,4})$/i', $name, $m)) {
1922
			$ext  = '.'.$m[1];
1923
			$name = substr($name, 0,  strlen($name)-strlen($m[0]));
1924
		} 
1925
		
1926
		if ($checkNum && preg_match('/('.$suffix.')(\d*)$/i', $name, $m)) {
1927
			$i    = (int)$m[2];
1928
			$name = substr($name, 0, strlen($name)-strlen($m[2]));
1929
		} else {
1930
			$i     = 1;
1931
			$name .= $suffix;
1932
		}
1933
		$max = $i+100000;
1934
1935
		while ($i <= $max) {
1936
			$n = $name.($i > 0 ? $i : '').$ext;
1937
1938
			if (!$this->stat($this->_joinPath($dir, $n))) {
1939
				$this->clearcache();
1940
				return $n;
1941
			}
1942
			$i++;
1943
		}
1944
		return $name.md5($dir).$ext;
1945
	}
1946
	
1947
	/*********************** file stat *********************/
1948
	
1949
	/**
1950
	 * Check file attribute
1951
	 *
1952
	 * @param  string  $path  file path
1953
	 * @param  string  $name  attribute name (read|write|locked|hidden)
1954
	 * @param  bool    $val   attribute value returned by file system
1955
	 * @return bool
1956
	 * @author Dmitry (dio) Levashov
1957
	 **/
1958
	protected function attr($path, $name, $val=null) {
1959
		if (!isset($this->defaults[$name])) {
1960
			return false;
1961
		}
1962
		
1963
		
1964
		$perm = null;
1965
		
1966 View Code Duplication
		if ($this->access) {
1967
			$perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this);
1968
1969
			if ($perm !== null) {
1970
				return !!$perm;
1971
			}
1972
		}
1973
		
1974
		if ($this->separator != '/') {
1975
			$path = str_replace($this->separator, '/', $this->_relpath($path));
1976
		} else {
1977
			$path = $this->_relpath($path);
1978
		}
1979
1980
		$path = '/'.$path;
1981
1982 View Code Duplication
		for ($i = 0, $c = count($this->attributes); $i < $c; $i++) {
1983
			$attrs = $this->attributes[$i];
1984
			
1985
			if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $path)) {
1986
				$perm = $attrs[$name];
1987
			} 
1988
		}
1989
		
1990
		return $perm === null ? (is_null($val)? $this->defaults[$name] : $val) : !!$perm;
1991
	}
1992
	
1993
	/**
1994
	 * Return true if file with given name can be created in given folder.
1995
	 *
1996
	 * @param string $dir  parent dir path
1997
	 * @param string $name new file name
1998
	 * @return bool
1999
	 * @author Dmitry (dio) Levashov
2000
	 **/
2001
	protected function allowCreate($dir, $name) {
2002
		$path = $this->_joinPath($dir, $name);
2003
		$perm = null;
2004
		
2005 View Code Duplication
		if ($this->access) {
2006
			$perm = call_user_func($this->access, 'write', $path, $this->options['accessControlData'], $this);			
2007
			if ($perm !== null) {
2008
				return !!$perm;
2009
			}
2010
		}
2011
		
2012
		$testPath = $this->separator.$this->_relpath($path);
2013
		
2014 View Code Duplication
		for ($i = 0, $c = count($this->attributes); $i < $c; $i++) {
2015
			$attrs = $this->attributes[$i];
2016
			
2017
			if (isset($attrs['write']) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $testPath)) {
2018
				$perm = $attrs['write'];
2019
			} 
2020
		}
2021
		
2022
		return $perm === null ? true : $perm;
2023
	}
2024
	
2025
	/**
2026
	 * Return fileinfo 
2027
	 *
2028
	 * @param  string  $path  file cache
2029
	 * @return array
2030
	 * @author Dmitry (dio) Levashov
2031
	 **/
2032
	protected function stat($path) {
2033
		if ($path === false) {
2034
			return false;
2035
		}
2036
		return isset($this->cache[$path])
2037
			? $this->cache[$path]
2038
			: $this->updateCache($path, $this->_stat($path));
2039
	}
2040
	
2041
	/**
2042
	 * Put file stat in cache and return it
2043
	 *
2044
	 * @param  string  $path   file path
2045
	 * @param  array   $stat   file stat
2046
	 * @return array
2047
	 * @author Dmitry (dio) Levashov
2048
	 **/
2049
	protected function updateCache($path, $stat) {
2050
		if (empty($stat) || !is_array($stat)) {
2051
			return $this->cache[$path] = array();
2052
		}
2053
2054
		$stat['hash'] = $this->encode($path);
2055
2056
		$root = $path == $this->root;
2057
		
2058
		if ($root) {
2059
			$stat['volumeid'] = $this->id;
2060
			if ($this->rootName) {
2061
				$stat['name'] = $this->rootName;
2062
			}
2063
		} else {
2064
			if (!isset($stat['name']) || !strlen($stat['name'])) {
2065
				$stat['name'] = $this->_basename($path);
2066
			}
2067
			if (empty($stat['phash'])) {
2068
				$stat['phash'] = $this->encode($this->_dirname($path));
2069
			}
2070
		}
2071
		
2072
		// fix name if required
2073
		if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) {
2074
			$stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], json_encode($stat['name'])));
2075
		}
2076
		
2077
		
2078
		if (empty($stat['mime'])) {
2079
			$stat['mime'] = $this->mimetype($stat['name']);
2080
		}
2081
		
2082
		// @todo move dateformat to client
2083
		// $stat['date'] = isset($stat['ts'])
2084
		// 	? $this->formatDate($stat['ts'])
2085
		// 	: 'unknown';
2086
			
2087
		if (!isset($stat['size'])) {
2088
			$stat['size'] = 'unknown';
2089
		}	
2090
2091
		$stat['read']  = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null));
2092
		$stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null));
2093 View Code Duplication
		if ($root) {
2094
			$stat['locked'] = 1;
2095
		} elseif ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null)) {
2096
			$stat['locked'] = 1;
2097
		} else {
2098
			unset($stat['locked']);
2099
		}
2100
2101 View Code Duplication
		if ($root) {
2102
			unset($stat['hidden']);
2103
		} elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null) 
2104
		|| !$this->mimeAccepted($stat['mime'])) {
2105
			$stat['hidden'] = 1;
2106
		} else {
2107
			unset($stat['hidden']);
2108
		}
2109
		
2110
		if ($stat['read'] && empty($stat['hidden'])) {
2111
			
2112
			if ($stat['mime'] == 'directory') {
2113
				// for dir - check for subdirs
2114
2115
				if ($this->options['checkSubfolders']) {
2116
					if (isset($stat['dirs'])) {
2117
						if ($stat['dirs']) {
2118
							$stat['dirs'] = 1;
2119
						} else {
2120
							unset($stat['dirs']);
2121
						}
2122
					} elseif (!empty($stat['alias']) && !empty($stat['target'])) {
2123
						$stat['dirs'] = isset($this->cache[$stat['target']])
2124
							? intval(isset($this->cache[$stat['target']]['dirs']))
2125
							: $this->_subdirs($stat['target']);
2126
						
2127
					} elseif ($this->_subdirs($path)) {
2128
						$stat['dirs'] = 1;
2129
					}
2130
				} else {
2131
					$stat['dirs'] = 1;
2132
				}
2133
			} else {
2134
				// for files - check for thumbnails
2135
				$p = isset($stat['target']) ? $stat['target'] : $path;
2136
				if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) {
2137
					$tmb = $this->gettmb($p, $stat);
2138
					$stat['tmb'] = $tmb ? $tmb : 1;
2139
				}
2140
				
2141
			}
2142
		}
2143
		
2144
		if (!empty($stat['alias']) && !empty($stat['target'])) {
2145
			$stat['thash'] = $this->encode($stat['target']);
2146
			unset($stat['target']);
2147
		}
2148
2149
		return $this->cache[$path] = $stat;
2150
	}
2151
	
2152
	/**
2153
	 * Get stat for folder content and put in cache
2154
	 *
2155
	 * @param  string  $path
2156
	 * @return void
2157
	 * @author Dmitry (dio) Levashov
2158
	 **/
2159
	protected function cacheDir($path) {
2160
		$this->dirsCache[$path] = array();
2161
2162 View Code Duplication
		foreach ($this->_scandir($path) as $p) {
2163
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
2164
				$this->dirsCache[$path][] = $p;
2165
			}
2166
		}	
2167
	}
2168
	
2169
	/**
2170
	 * Clean cache
2171
	 *
2172
	 * @return void
2173
	 * @author Dmitry (dio) Levashov
2174
	 **/
2175
	protected function clearcache() {
2176
		$this->cache = $this->dirsCache = array();
2177
	}
2178
	
2179
	/**
2180
	 * Return file mimetype
2181
	 *
2182
	 * @param  string  $path  file path
2183
	 * @return string
2184
	 * @author Dmitry (dio) Levashov
2185
	 **/
2186
	protected function mimetype($path, $name = '') {
2187
		$type = '';
2188
		
2189
		if ($this->mimeDetect == 'finfo') {
2190
			if ($type = @finfo_file($this->finfo, $path)) {
2191
				if ($name === '') {
2192
					$name = $path;
2193
				}
2194
				$ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1);
2195
				if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip)~', $type)) {
2196
					if (isset(elFinderVolumeDriver::$mimetypes[$ext])) $type = elFinderVolumeDriver::$mimetypes[$ext];
2197
				}
2198
			}
2199
		} elseif ($type == 'mime_content_type') {
2200
			$type = mime_content_type($path);
2201
		} else {
2202
			$type = elFinderVolumeDriver::mimetypeInternalDetect($path);
2203
		}
2204
		
2205
		$type = explode(';', $type);
2206
		$type = trim($type[0]);
2207
2208
		if (in_array($type, array('application/x-empty', 'inode/x-empty'))) {
2209
			// finfo return this mime for empty files
2210
			$type = 'text/plain';
2211
		} elseif ($type == 'application/x-zip') {
2212
			// http://elrte.org/redmine/issues/163
2213
			$type = 'application/zip';
2214
		}
2215
		
2216
		return $type == 'unknown' && $this->mimeDetect != 'internal'
2217
			? elFinderVolumeDriver::mimetypeInternalDetect($path)
2218
			: $type;
2219
		
2220
	}
2221
	
2222
	/**
2223
	 * Detect file mimetype using "internal" method
2224
	 *
2225
	 * @param  string  $path  file path
2226
	 * @return string
2227
	 * @author Dmitry (dio) Levashov
2228
	 **/
2229
	static protected function mimetypeInternalDetect($path) {
2230
		$pinfo = pathinfo($path); 
2231
		$ext   = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
2232
		return isset(elFinderVolumeDriver::$mimetypes[$ext]) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown';
2233
		
2234
	}
2235
	
2236
	/**
2237
	 * Return file/total directory size
2238
	 *
2239
	 * @param  string  $path  file path
2240
	 * @return int
2241
	 * @author Dmitry (dio) Levashov
2242
	 **/
2243
	protected function countSize($path) {
2244
		$stat = $this->stat($path);
2245
2246
		if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) {
2247
			return 'unknown';
2248
		}
2249
		
2250
		if ($stat['mime'] != 'directory') {
2251
			return $stat['size'];
2252
		}
2253
		
2254
		$subdirs = $this->options['checkSubfolders'];
2255
		$this->options['checkSubfolders'] = true;
2256
		$result = 0;
2257
		foreach ($this->getScandir($path) as $stat) {
2258
			$size = $stat['mime'] == 'directory' && $stat['read'] 
2259
				? $this->countSize($this->_joinPath($path, $stat['name'])) 
2260
				: (isset($stat['size']) ? intval($stat['size']) : 0);
2261
			if ($size > 0) {
2262
				$result += $size;
2263
			}
2264
		}
2265
		$this->options['checkSubfolders'] = $subdirs;
2266
		return $result;
2267
	}
2268
	
2269
	/**
2270
	 * Return true if all mimes is directory or files
2271
	 *
2272
	 * @param  string  $mime1  mimetype
2273
	 * @param  string  $mime2  mimetype
2274
	 * @return bool
2275
	 * @author Dmitry (dio) Levashov
2276
	 **/
2277
	protected function isSameType($mime1, $mime2) {
2278
		return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory');
2279
	}
2280
	
2281
	/**
2282
	 * If file has required attr == $val - return file path,
2283
	 * If dir has child with has required attr == $val - return child path
2284
	 *
2285
	 * @param  string   $path  file path
2286
	 * @param  string   $attr  attribute name
2287
	 * @param  bool     $val   attribute value
2288
	 * @return string|false
2289
	 * @author Dmitry (dio) Levashov
2290
	 **/
2291
	protected function closestByAttr($path, $attr, $val) {
2292
		$stat = $this->stat($path);
2293
		
2294
		if (empty($stat)) {
2295
			return false;
2296
		}
2297
		
2298
		$v = isset($stat[$attr]) ? $stat[$attr] : false;
2299
		
2300
		if ($v == $val) {
2301
			return $path;
2302
		}
2303
2304
		return $stat['mime'] == 'directory'
2305
			? $this->childsByAttr($path, $attr, $val) 
2306
			: false;
2307
	}
2308
	
2309
	/**
2310
	 * Return first found children with required attr == $val
2311
	 *
2312
	 * @param  string   $path  file path
2313
	 * @param  string   $attr  attribute name
2314
	 * @param  bool     $val   attribute value
2315
	 * @return string|false
2316
	 * @author Dmitry (dio) Levashov
2317
	 **/
2318
	protected function childsByAttr($path, $attr, $val) {
2319
		foreach ($this->_scandir($path) as $p) {
2320
			if (($_p = $this->closestByAttr($p, $attr, $val)) != false) {
2321
				return $_p;
2322
			}
2323
		}
2324
		return false;
2325
	}
2326
	
2327
	/*****************  get content *******************/
2328
	
2329
	/**
2330
	 * Return required dir's files info.
2331
	 * If onlyMimes is set - return only dirs and files of required mimes
2332
	 *
2333
	 * @param  string  $path  dir path
2334
	 * @return array
2335
	 * @author Dmitry (dio) Levashov
2336
	 **/
2337
	protected function getScandir($path) {
2338
		$files = array();
2339
		
2340
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
2341
2342 View Code Duplication
		foreach ($this->dirsCache[$path] as $p) {
2343
			if (($stat = $this->stat($p)) && empty($stat['hidden'])) {
2344
				$files[] = $stat;
2345
			}
2346
		}
2347
2348
		return $files;
2349
	}
2350
	
2351
	
2352
	/**
2353
	 * Return subdirs tree
2354
	 *
2355
	 * @param  string  $path  parent dir path
2356
	 * @param  int     $deep  tree deep
2357
	 * @return array
2358
	 * @author Dmitry (dio) Levashov
2359
	 **/
2360
	protected function gettree($path, $deep, $exclude='') {
2361
		$dirs = array();
2362
		
2363
		!isset($this->dirsCache[$path]) && $this->cacheDir($path);
2364
2365
		foreach ($this->dirsCache[$path] as $p) {
2366
			$stat = $this->stat($p);
2367
			
2368
			if ($stat && empty($stat['hidden']) && $p != $exclude && $stat['mime'] == 'directory') {
2369
				$dirs[] = $stat;
2370
				if ($deep > 0 && !empty($stat['dirs'])) {
2371
					$dirs = array_merge($dirs, $this->gettree($p, $deep-1));
2372
				}
2373
			}
2374
		}
2375
2376
		return $dirs;
2377
	}	
2378
		
2379
	/**
2380
	 * Recursive files search
2381
	 *
2382
	 * @param  string  $path   dir path
2383
	 * @param  string  $q      search string
2384
	 * @param  array   $mimes
2385
	 * @return array
2386
	 * @author Dmitry (dio) Levashov
2387
	 **/
2388
	protected function doSearch($path, $q, $mimes) {
2389
		$result = array();
2390
2391
		foreach($this->_scandir($path) as $p) {
2392
			$stat = $this->stat($p);
2393
2394
			if (!$stat) { // invalid links
2395
				continue;
2396
			}
2397
2398
			if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'])) {
2399
				continue;
2400
			}
2401
			
2402
			$name = $stat['name'];
2403
2404
			if ($this->stripos($name, $q) !== false) {
2405
				$stat['path'] = $this->_path($p);
2406
				if ($this->URL && !isset($stat['url'])) {
2407
					$stat['url'] = $this->URL . str_replace($this->separator, '/', substr($p, strlen($this->root) + 1));
2408
				}
2409
				
2410
				$result[] = $stat;
2411
			}
2412
			if ($stat['mime'] == 'directory' && $stat['read'] && !isset($stat['alias'])) {
2413
				$result = array_merge($result, $this->doSearch($p, $q, $mimes));
2414
			}
2415
		}
2416
		
2417
		return $result;
2418
	}
2419
		
2420
	/**********************  manuipulations  ******************/
2421
		
2422
	/**
2423
	 * Copy file/recursive copy dir only in current volume.
2424
	 * Return new file path or false.
2425
	 *
2426
	 * @param  string  $src   source path
2427
	 * @param  string  $dst   destination dir path
2428
	 * @param  string  $name  new file name (optionaly)
2429
	 * @return string|false
2430
	 * @author Dmitry (dio) Levashov
2431
	 **/
2432
	protected function copy($src, $dst, $name) {
2433
		$srcStat = $this->stat($src);
2434
		$this->clearcache();
2435
		
2436
		if (!empty($srcStat['thash'])) {
2437
			$target = $this->decode($srcStat['thash']);
2438
			$stat   = $this->stat($target);
2439
			$this->clearcache();
2440
			return $stat && $this->_symlink($target, $dst, $name)
2441
				? $this->_joinPath($dst, $name)
2442
				: $this->setError(elFinder::ERROR_COPY, $this->_path($src));
2443
		} 
2444
		
2445
		if ($srcStat['mime'] == 'directory') {
2446
			$test = $this->stat($this->_joinPath($dst, $name));
2447
			
2448 View Code Duplication
			if (($test && $test['mime'] != 'directory') || !$this->_mkdir($dst, $name)) {
2449
				return $this->setError(elFinder::ERROR_COPY, $this->_path($src));
2450
			}
2451
			
2452
			$dst = $this->_joinPath($dst, $name);
2453
			
2454
			foreach ($this->getScandir($src) as $stat) {
2455
				if (empty($stat['hidden'])) {
2456
					$name = $stat['name'];
2457
					if (!$this->copy($this->_joinPath($src, $name), $dst, $name)) {
2458
						$this->remove($dst, true); // fall back
2459
						return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src));
2460
					}
2461
				}
2462
			}
2463
			$this->clearcache();
2464
			return $dst;
2465
		} 
2466
2467
		return $this->_copy($src, $dst, $name) 
2468
			? $this->_joinPath($dst, $name) 
2469
			: $this->setError(elFinder::ERROR_COPY, $this->_path($src));
2470
	}
2471
2472
	/**
2473
	 * Move file
2474
	 * Return new file path or false.
2475
	 *
2476
	 * @param  string  $src   source path
2477
	 * @param  string  $dst   destination dir path
2478
	 * @param  string  $name  new file name 
2479
	 * @return string|false
2480
	 * @author Dmitry (dio) Levashov
2481
	 **/
2482
	protected function move($src, $dst, $name) {
2483
		$stat = $this->stat($src);
2484
		$stat['realpath'] = $src;
2485
		$this->rmTmb($stat); // can not do rmTmb() after _move()
2486
		$this->clearcache();
2487
		
2488
		if ($this->_move($src, $dst, $name)) {
2489
			$this->removed[] = $stat;
2490
2491
			return $this->_joinPath($dst, $name);
2492
		}
2493
2494
		return $this->setError(elFinder::ERROR_MOVE, $this->_path($src));
2495
	}
2496
2497
	/**
2498
	 * Copy file from another volume.
2499
	 * Return new file path or false.
2500
	 *
2501
	 * @param  Object  $volume       source volume
2502
	 * @param  string  $src          source file hash
2503
	 * @param  string  $destination  destination dir path
2504
	 * @param  string  $name         file name
2505
	 * @return string|false
2506
	 * @author Dmitry (dio) Levashov
2507
	 **/
2508
	protected function copyFrom($volume, $src, $destination, $name) {
2509
		
2510
		if (($source = $volume->file($src)) == false) {
2511
			return $this->setError(elFinder::ERROR_COPY, '#'.$src, $volume->error());
2512
		}
2513
		
2514
		$errpath = $volume->path($src);
2515
		
2516
		if (!$this->nameAccepted($source['name'])) {
2517
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_INVALID_NAME);
2518
		}
2519
				
2520
		if (!$source['read']) {
2521
			return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED);
2522
		}
2523
		
2524
		if ($source['mime'] == 'directory') {
2525
			$stat = $this->stat($this->_joinPath($destination, $name));
2526
			$this->clearcache();
2527 View Code Duplication
			if ((!$stat || $stat['mime'] != 'directory') && !$this->_mkdir($destination, $name)) {
2528
				return $this->setError(elFinder::ERROR_COPY, $errpath);
2529
			}
2530
			
2531
			$path = $this->_joinPath($destination, $name);
2532
			
2533
			foreach ($volume->scandir($src) as $entr) {
2534
				if (!$this->copyFrom($volume, $entr['hash'], $path, $entr['name'])) {
2535
					$this->remove($path, true); // fall back
2536
					return $this->setError($this->error, elFinder::ERROR_COPY, $errpath);
2537
				}
2538
			}
2539
			
2540
		} else {
2541
			// $mime = $source['mime'];
2542
			// $w = $h = 0;
2543
			if (($dim = $volume->dimensions($src))) {
2544
				$s = explode('x', $dim);
2545
				$source['width']  = $s[0];
2546
				$source['height'] = $s[1];
2547
			}
2548
			
2549
			if (($fp = $volume->open($src)) == false
2550
			|| ($path = $this->_save($fp, $destination, $name, $source)) == false) {
2551
				$fp && $volume->close($fp, $src);
2552
				return $this->setError(elFinder::ERROR_COPY, $errpath);
2553
			}
2554
			$volume->close($fp, $src);
2555
		}
2556
		
2557
		return $path;
2558
	}
2559
		
2560
	/**
2561
	 * Remove file/ recursive remove dir
2562
	 *
2563
	 * @param  string  $path   file path
2564
	 * @param  bool    $force  try to remove even if file locked
2565
	 * @return bool
2566
	 * @author Dmitry (dio) Levashov
2567
	 **/
2568
	protected function remove($path, $force = false) {
2569
		$stat = $this->stat($path);
2570
		
2571
		if (empty($stat)) {
2572
			return $this->setError(elFinder::ERROR_RM, $this->_path($path), elFinder::ERROR_FILE_NOT_FOUND);
2573
		}
2574
		
2575
		$stat['realpath'] = $path;
2576
		$this->rmTmb($stat);
2577
		$this->clearcache();
2578
		
2579
		if (!$force && !empty($stat['locked'])) {
2580
			return $this->setError(elFinder::ERROR_LOCKED, $this->_path($path));
2581
		}
2582
		
2583
		if ($stat['mime'] == 'directory') {
2584
			foreach ($this->_scandir($path) as $p) {
2585
				$name = $this->_basename($p);
2586
				if ($name != '.' && $name != '..' && !$this->remove($p)) {
2587
					return false;
2588
				}
2589
			}
2590
			if (!$this->_rmdir($path)) {
2591
				return $this->setError(elFinder::ERROR_RM, $this->_path($path));
2592
			}
2593
			
2594
		} else {
2595
			if (!$this->_unlink($path)) {
2596
				return $this->setError(elFinder::ERROR_RM, $this->_path($path));
2597
			}
2598
		}
2599
2600
		$this->removed[] = $stat;
2601
		return true;
2602
	}
2603
	
2604
2605
	/************************* thumbnails **************************/
2606
		
2607
	/**
2608
	 * Return thumbnail file name for required file
2609
	 *
2610
	 * @param  array  $stat  file stat
2611
	 * @return string
2612
	 * @author Dmitry (dio) Levashov
2613
	 **/
2614
	protected function tmbname($stat) {
2615
		return $stat['hash'].$stat['ts'].'.png';
2616
	}
2617
	
2618
	/**
2619
	 * Return thumnbnail name if exists
2620
	 *
2621
	 * @param  string  $path file path
2622
	 * @param  array   $stat file stat
2623
	 * @return string|false
2624
	 * @author Dmitry (dio) Levashov
2625
	 **/
2626
	protected function gettmb($path, $stat) {
2627
		if ($this->tmbURL && $this->tmbPath) {
2628
			// file itself thumnbnail
2629
			if (strpos($path, $this->tmbPath) === 0) {
2630
				return basename($path);
2631
			}
2632
2633
			$name = $this->tmbname($stat);
2634
			if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) {
2635
				return $name;
2636
			}
2637
		}
2638
		return false;
2639
	}
2640
	
2641
	/**
2642
	 * Return true if thumnbnail for required file can be created
2643
	 *
2644
	 * @param  string  $path  thumnbnail path 
2645
	 * @param  array   $stat  file stat
2646
	 * @return string|bool
2647
	 * @author Dmitry (dio) Levashov
2648
	 **/
2649
	protected function canCreateTmb($path, $stat) {
2650
		return $this->tmbPathWritable 
2651
			&& strpos($path, $this->tmbPath) === false // do not create thumnbnail for thumnbnail
2652
			&& $this->imgLib 
2653
			&& strpos($stat['mime'], 'image') === 0 
2654
			&& ($this->imgLib == 'gd' ? $stat['mime'] == 'image/jpeg' || $stat['mime'] == 'image/png' || $stat['mime'] == 'image/gif' : true);
2655
	}
2656
	
2657
	/**
2658
	 * Return true if required file can be resized.
2659
	 * By default - the same as canCreateTmb
2660
	 *
2661
	 * @param  string  $path  thumnbnail path 
2662
	 * @param  array   $stat  file stat
2663
	 * @return string|bool
2664
	 * @author Dmitry (dio) Levashov
2665
	 **/
2666
	protected function canResize($path, $stat) {
2667
		return $this->canCreateTmb($path, $stat);
2668
	}
2669
	
2670
	/**
2671
	 * Create thumnbnail and return it's URL on success
2672
	 *
2673
	 * @param  string  $path  file path
2674
	 * @param  string  $mime  file mime type
2675
	 * @return string|false
2676
	 * @author Dmitry (dio) Levashov
2677
	 **/
2678
	protected function createTmb($path, $stat) {
2679
		if (!$stat || !$this->canCreateTmb($path, $stat)) {
2680
			return false;
2681
		}
2682
2683
		$name = $this->tmbname($stat);
2684
		$tmb  = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
2685
2686
		// copy image into tmbPath so some drivers does not store files on local fs
2687
		if (($src = $this->_fopen($path, 'rb')) == false) {
2688
			return false;
2689
		}
2690
2691
		if (($trg = fopen($tmb, 'wb')) == false) {
2692
			$this->_fclose($src, $path);
2693
			return false;
2694
		}
2695
2696
		while (!feof($src)) {
2697
			fwrite($trg, fread($src, 8192));
2698
		}
2699
2700
		$this->_fclose($src, $path);
2701
		fclose($trg);
2702
2703
		$result = false;
2704
		
2705
		$tmbSize = $this->tmbSize;
2706
		
2707
  		if (($s = getimagesize($tmb)) == false) {
2708
			return false;
2709
		}
2710
    
2711
    		/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
2712
    		if ($s[0] <= $tmbSize && $s[1]  <= $tmbSize) {
2713
     	   		$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
2714
	    	} else {
2715
2716
	    		if ($this->options['tmbCrop']) {
2717
        
2718
        			/* Resize and crop if image bigger than thumbnail */
2719
	        		if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize) ) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
2720
    					$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
2721
	        		}
2722
2723
				if (($s = getimagesize($tmb)) != false) {
2724
					$x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0;
2725
					$y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0;
2726
					$result = $this->imgCrop($tmb, $tmbSize, $tmbSize, $x, $y, 'png');
2727
				}
2728
2729
    			} else {
2730
        			$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
2731
      			}
2732
2733
			$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
2734
		}
2735
2736
		if (!$result) {
2737
			unlink($tmb);
2738
			return false;
2739
		}
2740
2741
		return $name;
2742
	}
2743
2744
	/**
2745
	 * Resize image
2746
	 *
2747
	 * @param  string   $path               image file
2748
	 * @param  int      $width              new width
2749
	 * @param  int      $height             new height
2750
	 * @param  bool	    $keepProportions    crop image
2751
	 * @param  bool	    $resizeByBiggerSide resize image based on bigger side if true
2752
	 * @param  string   $destformat         image destination format
2753
	 * @return string|false
2754
	 * @author Dmitry (dio) Levashov
2755
	 * @author Alexey Sukhotin
2756
	 **/
2757
  	protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) {
2758
		if (($s = @getimagesize($path)) == false) {
2759
			return false;
2760
		}
2761
2762
    	$result = false;
2763
    	
2764
		list($size_w, $size_h) = array($width, $height);
2765
    
2766
    	if ($keepProportions == true) {
2767
           
2768
      		list($orig_w, $orig_h, $new_w, $new_h) = array($s[0], $s[1], $width, $height);
2769
        
2770
      		/* Calculating image scale width and height */
2771
      		$xscale = $orig_w / $new_w;
2772
      		$yscale = $orig_h / $new_h;
2773
2774
      		/* Resizing by biggest side */
2775
2776
			if ($resizeByBiggerSide) {
2777
2778 View Code Duplication
		        if ($orig_w > $orig_h) {
2779
					$size_h = $orig_h * $width / $orig_w;
2780
					$size_w = $width;
2781
        		} else {
2782
          			$size_w = $orig_w * $height / $orig_h;
2783
          			$size_h = $height;
2784
				}
2785
      
2786 View Code Duplication
			} else {
2787
        		if ($orig_w > $orig_h) {
2788
          			$size_w = $orig_w * $height / $orig_h;
2789
          			$size_h = $height;
2790
		        } else {
2791
					$size_h = $orig_h * $width / $orig_w;
2792
					$size_w = $width;
2793
				}
2794
			}
2795
    	}
2796
2797
		switch ($this->imgLib) {
2798
			case 'imagick':
2799
				
2800
				try {
2801
					$img = new imagick($path);
2802
				} catch (Exception $e) {
2803
2804
					return false;
2805
				}
2806
2807
				$img->resizeImage($size_w, $size_h, Imagick::FILTER_LANCZOS, true);
2808
					
2809
				$result = $img->writeImage($path);
2810
2811
				return $result ? $path : false;
2812
2813
				break;
2814
2815
			case 'gd':
2816
				$img = self::gdImageCreate($path,$s['mime']);
2817
2818
				if ($img &&  false != ($tmp = imagecreatetruecolor($size_w, $size_h))) {
2819
				
2820
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
2821
					
2822
					if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) {
2823
						return false;
2824
					}
2825
		
2826
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
2827
2828
					imagedestroy($img);
2829
					imagedestroy($tmp);
2830
2831
					return $result ? $path : false;
2832
2833
				}
2834
				break;
2835
		}
2836
		
2837
		return false;
2838
  	}
2839
  
2840
	/**
2841
	 * Crop image
2842
	 *
2843
	 * @param  string   $path               image file
2844
	 * @param  int      $width              crop width
2845
	 * @param  int      $height             crop height
2846
	 * @param  bool	    $x                  crop left offset
2847
	 * @param  bool	    $y                  crop top offset
2848
	 * @param  string   $destformat         image destination format
2849
	 * @return string|false
2850
	 * @author Dmitry (dio) Levashov
2851
	 * @author Alexey Sukhotin
2852
	 **/
2853
  	protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) {
2854
		if (($s = @getimagesize($path)) == false) {
2855
			return false;
2856
		}
2857
2858
		$result = false;
2859
		
2860
		switch ($this->imgLib) {
2861 View Code Duplication
			case 'imagick':
2862
				
2863
				try {
2864
					$img = new imagick($path);
2865
				} catch (Exception $e) {
2866
2867
					return false;
2868
				}
2869
2870
				$img->cropImage($width, $height, $x, $y);
2871
2872
				$result = $img->writeImage($path);
2873
2874
				return $result ? $path : false;
2875
2876
				break;
2877
2878
			case 'gd':
2879
				$img = self::gdImageCreate($path,$s['mime']);
2880
2881
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
2882
					
2883
					self::gdImageBackground($tmp,$this->options['tmbBgColor']);
2884
2885
					$size_w = $width;
2886
					$size_h = $height;
2887
2888
					if ($s[0] < $width || $s[1] < $height) {
2889
						$size_w = $s[0];
2890
						$size_h = $s[1];
2891
					}
2892
2893
					if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) {
2894
						return false;
2895
					}
2896
					
2897
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
2898
2899
					imagedestroy($img);
2900
					imagedestroy($tmp);
2901
2902
					return $result ? $path : false;
2903
2904
				}
2905
				break;
2906
		}
2907
2908
		return false;
2909
	}
2910
2911
	/**
2912
	 * Put image to square
2913
	 *
2914
	 * @param  string   $path               image file
2915
	 * @param  int      $width              square width
2916
	 * @param  int      $height             square height
2917
	 * @param  int	    $align              reserved
2918
	 * @param  int 	    $valign             reserved
2919
	 * @param  string   $bgcolor            square background color in #rrggbb format
2920
	 * @param  string   $destformat         image destination format
2921
	 * @return string|false
2922
	 * @author Dmitry (dio) Levashov
2923
	 * @author Alexey Sukhotin
2924
	 **/
2925
	protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null) {
2926
		if (($s = @getimagesize($path)) == false) {
2927
			return false;
2928
		}
2929
2930
		$result = false;
2931
2932
		/* Coordinates for image over square aligning */
2933
		$y = ceil(abs($height - $s[1]) / 2); 
2934
		$x = ceil(abs($width - $s[0]) / 2);
2935
    
2936
		switch ($this->imgLib) {
2937
			case 'imagick':
2938
				try {
2939
					$img = new imagick($path);
2940
				} catch (Exception $e) {
2941
					return false;
2942
				}
2943
2944
				$img1 = new Imagick();
2945
				$img1->newImage($width, $height, new ImagickPixel($bgcolor));
2946
				$img1->setImageColorspace($img->getImageColorspace());
2947
				$img1->setImageFormat($destformat != null ? $destformat : $img->getFormat());
2948
				$img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
2949
				$result = $img1->writeImage($path);
2950
				return $result ? $path : false;
2951
2952
				break;
2953
2954
			case 'gd':
2955
				$img = self::gdImageCreate($path,$s['mime']);
2956
2957
				if ($img &&  false != ($tmp = imagecreatetruecolor($width, $height))) {
2958
2959
					self::gdImageBackground($tmp,$bgcolor);
2960
2961
					if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) {
2962
						return false;
2963
					}
2964
2965
					$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
2966
2967
					imagedestroy($img);
2968
					imagedestroy($tmp);
2969
2970
					return $result ? $path : false;
2971
				}
2972
				break;
2973
		}
2974
2975
		return false;
2976
	}
2977
2978
	/**
2979
	 * Rotate image
2980
	 *
2981
	 * @param  string   $path               image file
2982
	 * @param  int      $degree             rotete degrees
2983
	 * @param  string   $bgcolor            square background color in #rrggbb format
2984
	 * @param  string   $destformat         image destination format
2985
	 * @return string|false
2986
	 * @author nao-pon
2987
	 * @author Troex Nevelin
2988
	 **/
2989
	protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) {
2990
		if (($s = @getimagesize($path)) == false) {
2991
			return false;
2992
		}
2993
2994
		$result = false;
2995
2996
		switch ($this->imgLib) {
2997 View Code Duplication
			case 'imagick':
2998
				try {
2999
					$img = new imagick($path);
3000
				} catch (Exception $e) {
3001
					return false;
3002
				}
3003
3004
				$img->rotateImage(new ImagickPixel($bgcolor), $degree);
3005
				$result = $img->writeImage($path);
3006
				return $result ? $path : false;
3007
3008
				break;
3009
3010
			case 'gd':
3011
				$img = self::gdImageCreate($path,$s['mime']);
3012
3013
				$degree = 360 - $degree;
3014
				list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
3015
				$bgcolor = imagecolorallocate($img, $r, $g, $b);
3016
				$tmp = imageRotate($img, $degree, (int)$bgcolor);
3017
3018
				$result = self::gdImage($tmp, $path, $destformat, $s['mime']);
3019
3020
				imageDestroy($img);
3021
				imageDestroy($tmp);
3022
3023
				return $result ? $path : false;
3024
3025
				break;
3026
		}
3027
3028
		return false;
3029
	}
3030
3031
	/**
3032
	 * Execute shell command
3033
	 *
3034
	 * @param  string  $command       command line
3035
	 * @param  array   $output        stdout strings
3036
	 * @param  array   $return_var    process exit code
3037
	 * @param  array   $error_output  stderr strings
3038
	 * @return int     exit code
3039
	 * @author Alexey Sukhotin
3040
	 **/
3041
	protected function procExec($command , array &$output = null, &$return_var = -1, array &$error_output = null) {
3042
3043
		$descriptorspec = array(
3044
			0 => array("pipe", "r"),  // stdin
3045
			1 => array("pipe", "w"),  // stdout
3046
			2 => array("pipe", "w")   // stderr
3047
		);
3048
3049
		$process = proc_open($command, $descriptorspec, $pipes, null, null);
3050
3051
		if (is_resource($process)) {
3052
3053
			fclose($pipes[0]);
3054
3055
			$tmpout = '';
3056
			$tmperr = '';
3057
3058
			$output = stream_get_contents($pipes[1]);
3059
			$error_output = stream_get_contents($pipes[2]);
3060
3061
			fclose($pipes[1]);
3062
			fclose($pipes[2]);
3063
			$return_var = proc_close($process);
3064
3065
3066
		}
3067
		
3068
		return $return_var;
3069
		
3070
	}
3071
3072
	/**
3073
	 * Remove thumbnail, also remove recursively if stat is directory
3074
	 *
3075
	 * @param  string  $stat  file stat
3076
	 * @return void
3077
	 * @author Dmitry (dio) Levashov
3078
	 * @author Naoki Sawada
3079
	 * @author Troex Nevelin
3080
	 **/
3081
	protected function rmTmb($stat) {
3082
		if ($stat['mime'] === 'directory') {
3083
			foreach ($this->_scandir($this->decode($stat['hash'])) as $p) {
3084
				$name = $this->_basename($p);
3085
				$name != '.' && $name != '..' && $this->rmTmb($this->stat($p));
3086
			}
3087
		} else if (!empty($stat['tmb']) && $stat['tmb'] != "1") {
3088
			$tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$stat['tmb'];
3089
			file_exists($tmb) && @unlink($tmb);
3090
			clearstatcache();
3091
		}
3092
	}
3093
	
3094
	/**
3095
	 * Create an gd image according to the specified mime type
3096
	 *
3097
	 * @param string $path image file
3098
	 * @param string $mime
3099
	 * @return gd image resource identifier
3100
	 */
3101
	protected function gdImageCreate($path,$mime){
3102
		switch($mime){
3103
			case 'image/jpeg':
3104
			return imagecreatefromjpeg($path);
3105
3106
			case 'image/png':
3107
			return imagecreatefrompng($path);
3108
3109
			case 'image/gif':
3110
			return imagecreatefromgif($path);
3111
3112
			case 'image/xbm':
3113
			return imagecreatefromxbm($path);
3114
		}
3115
		return false;
3116
	}
3117
3118
	/**
3119
	 * Output gd image to file
3120
	 *
3121
	 * @param resource $image gd image resource
3122
	 * @param string $filename The path to save the file to.
3123
	 * @param string $destformat The Image type to use for $filename
3124
	 * @param string $mime The original image mime type
3125
	 */
3126
	protected function gdImage($image, $filename, $destformat, $mime ){
3127
3128
		if ($destformat == 'jpg' || ($destformat == null && $mime == 'image/jpeg')) {
3129
			return imagejpeg($image, $filename, 100);
3130
		}
3131
3132
		if ($destformat == 'gif' || ($destformat == null && $mime == 'image/gif')) {
3133
			return imagegif($image, $filename, 7);
3134
		}
3135
3136
		return imagepng($image, $filename, 7);
3137
	}
3138
3139
	/**
3140
	 * Assign the proper background to a gd image
3141
	 *
3142
	 * @param resource $image gd image resource
3143
	 * @param string $bgcolor background color in #rrggbb format
3144
	 */
3145
	protected function gdImageBackground($image, $bgcolor){
3146
3147
		if( $bgcolor == 'transparent' ){
3148
			imagesavealpha($image,true);
3149
			$bgcolor1 = imagecolorallocatealpha($image, 255, 255, 255, 127);
3150
3151
		}else{
3152
			list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x");
3153
			$bgcolor1 = imagecolorallocate($image, $r, $g, $b);
3154
		}
3155
3156
		imagefill($image, 0, 0, $bgcolor1);
3157
	}
3158
3159
	/*********************** misc *************************/
3160
	
3161
	/**
3162
	 * Return smart formatted date
3163
	 *
3164
	 * @param  int     $ts  file timestamp
3165
	 * @return string
3166
	 * @author Dmitry (dio) Levashov
3167
	 **/
3168
	// protected function formatDate($ts) {
3169
	// 	if ($ts > $this->today) {
3170
	// 		return 'Today '.date($this->options['timeFormat'], $ts);
3171
	// 	}
3172
	// 	
3173
	// 	if ($ts > $this->yesterday) {
3174
	// 		return 'Yesterday '.date($this->options['timeFormat'], $ts);
3175
	// 	} 
3176
	// 	
3177
	// 	return date($this->options['dateFormat'], $ts);
3178
	// }
3179
3180
	/**
3181
	* Find position of first occurrence of string in a string with multibyte support
3182
	*
3183
	* @param  string  $haystack  The string being checked.
3184
	* @param  string  $needle    The string to find in haystack.
3185
	* @param  int     $offset    The search offset. If it is not specified, 0 is used.
3186
	* @return int|bool
3187
	* @author Alexey Sukhotin
3188
	**/
3189
	protected function stripos($haystack , $needle , $offset = 0) {
3190
		if (function_exists('mb_stripos')) {
3191
			return mb_stripos($haystack , $needle , $offset);
3192
		} else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) {
3193
			return mb_strpos(mb_strtolower($haystack), mb_strtolower($needle), $offset);
3194
		} 
3195
		return stripos($haystack , $needle , $offset);
3196
	}
3197
3198
	/**
3199
	 * Get server side available archivers
3200
	 * 
3201
	 * @param bool $use_cache
3202
	 * @return array
3203
	 */
3204
	protected function getArchivers($use_cache = true) {
3205
3206
		$arcs = array(
3207
				'create'  => array(),
3208
				'extract' => array()
3209
		);
3210
3211
		if (!function_exists('proc_open')) {
3212
			return $arcs;
3213
		}
3214
		
3215
		if ($use_cache && isset($_SESSION['ELFINDER_ARCHIVERS_CACHE']) && is_array($_SESSION['ELFINDER_ARCHIVERS_CACHE'])) {
3216
			return $_SESSION['ELFINDER_ARCHIVERS_CACHE'];
3217
		}
3218
		
3219
		$this->procExec('tar --version', $o, $ctar);
3220
		
3221
		if ($ctar == 0) {
3222
			$arcs['create']['application/x-tar']  = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
3223
			$arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
3224
			unset($o);
3225
			$test = $this->procExec('gzip --version', $o, $c);
3226 View Code Duplication
			if ($c == 0) {
3227
				$arcs['create']['application/x-gzip']  = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
3228
				$arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
3229
			}
3230
			unset($o);
3231
			$test = $this->procExec('bzip2 --version', $o, $c);
3232 View Code Duplication
			if ($c == 0) {
3233
				$arcs['create']['application/x-bzip2']  = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
3234
				$arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
3235
			}
3236
			unset($o);
3237
			$test = $this->procExec('xz --version', $o, $c);
3238 View Code Duplication
			if ($c == 0) {
3239
				$arcs['create']['application/x-xz']  = array('cmd' => 'tar', 'argc' => '-cJf', 'ext' => 'xz');
3240
				$arcs['extract']['application/x-xz'] = array('cmd' => 'tar', 'argc' => '-xJf', 'ext' => 'xz');
3241
			}
3242
		}
3243
		unset($o);
3244
		$this->procExec('zip -v', $o, $c);
3245
		if ($c == 0) {
3246
			$arcs['create']['application/zip']  = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
3247
		}
3248
		unset($o);
3249
		$this->procExec('unzip --help', $o, $c);
3250
		if ($c == 0) {
3251
			$arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '',  'ext' => 'zip');
3252
		}
3253
		unset($o);
3254
		$this->procExec('rar --version', $o, $c);
3255
		if ($c == 0 || $c == 7) {
3256
			$arcs['create']['application/x-rar']  = array('cmd' => 'rar', 'argc' => 'a -inul', 'ext' => 'rar');
3257
			$arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x -y',    'ext' => 'rar');
3258
		} else {
3259
			unset($o);
3260
			$test = $this->procExec('unrar', $o, $c);
3261
			if ($c==0 || $c == 7) {
3262
				$arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x -y', 'ext' => 'rar');
3263
			}
3264
		}
3265
		unset($o);
3266
		$this->procExec('7za --help', $o, $c);
3267
		if ($c == 0) {
3268
			$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
3269
			$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'x -y', 'ext' => '7z');
3270
			
3271
			if (empty($arcs['create']['application/zip'])) {
3272
				$arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip', 'ext' => 'zip');
3273
			}
3274 View Code Duplication
			if (empty($arcs['extract']['application/zip'])) {
3275
				$arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'x -tzip -y', 'ext' => 'zip');
3276
			}
3277
			if (empty($arcs['create']['application/x-tar'])) {
3278
				$arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar', 'ext' => 'tar');
3279
			}
3280 View Code Duplication
			if (empty($arcs['extract']['application/x-tar'])) {
3281
				$arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'x -ttar -y', 'ext' => 'tar');
3282
			}
3283
		} else if (substr(PHP_OS,0,3) === 'WIN') {
3284
			// check `7z` for Windows server.
3285
			unset($o);
3286
			$this->procExec('7z', $o, $c);
3287
			if ($c == 0) {
3288
				$arcs['create']['application/x-7z-compressed']  = array('cmd' => '7z', 'argc' => 'a', 'ext' => '7z');
3289
				$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7z', 'argc' => 'x -y', 'ext' => '7z');
3290
				
3291 View Code Duplication
				if (empty($arcs['create']['application/zip'])) {
3292
					$arcs['create']['application/zip'] = array('cmd' => '7z', 'argc' => 'a -tzip', 'ext' => 'zip');
3293
				}
3294 View Code Duplication
				if (empty($arcs['extract']['application/zip'])) {
3295
					$arcs['extract']['application/zip'] = array('cmd' => '7z', 'argc' => 'x -tzip -y', 'ext' => 'zip');
3296
				}
3297 View Code Duplication
				if (empty($arcs['create']['application/x-tar'])) {
3298
					$arcs['create']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'a -ttar', 'ext' => 'tar');
3299
				}
3300 View Code Duplication
				if (empty($arcs['extract']['application/x-tar'])) {
3301
					$arcs['extract']['application/x-tar'] = array('cmd' => '7z', 'argc' => 'x -ttar -y', 'ext' => 'tar');
3302
				}
3303
			}
3304
		}
3305
		
3306
		$_SESSION['ELFINDER_ARCHIVERS_CACHE'] = $arcs;
3307
		return $arcs;
3308
	}
3309
3310
	/**==================================* abstract methods *====================================**/
3311
	
3312
	/*********************** paths/urls *************************/
3313
	
3314
	/**
3315
	 * Return parent directory path
3316
	 *
3317
	 * @param  string  $path  file path
3318
	 * @return string
3319
	 * @author Dmitry (dio) Levashov
3320
	 **/
3321
	abstract protected function _dirname($path);
3322
3323
	/**
3324
	 * Return file name
3325
	 *
3326
	 * @param  string  $path  file path
3327
	 * @return string
3328
	 * @author Dmitry (dio) Levashov
3329
	 **/
3330
	abstract protected function _basename($path);
3331
3332
	/**
3333
	 * Join dir name and file name and return full path.
3334
	 * Some drivers (db) use int as path - so we give to concat path to driver itself
3335
	 *
3336
	 * @param  string  $dir   dir path
3337
	 * @param  string  $name  file name
3338
	 * @return string
3339
	 * @author Dmitry (dio) Levashov
3340
	 **/
3341
	abstract protected function _joinPath($dir, $name);
3342
3343
	/**
3344
	 * Return normalized path 
3345
	 *
3346
	 * @param  string  $path  file path
3347
	 * @return string
3348
	 * @author Dmitry (dio) Levashov
3349
	 **/
3350
	abstract protected function _normpath($path);
3351
3352
	/**
3353
	 * Return file path related to root dir
3354
	 *
3355
	 * @param  string  $path  file path
3356
	 * @return string
3357
	 * @author Dmitry (dio) Levashov
3358
	 **/
3359
	abstract protected function _relpath($path);
3360
	
3361
	/**
3362
	 * Convert path related to root dir into real path
3363
	 *
3364
	 * @param  string  $path  rel file path
3365
	 * @return string
3366
	 * @author Dmitry (dio) Levashov
3367
	 **/
3368
	abstract protected function _abspath($path);
3369
	
3370
	/**
3371
	 * Return fake path started from root dir.
3372
	 * Required to show path on client side.
3373
	 *
3374
	 * @param  string  $path  file path
3375
	 * @return string
3376
	 * @author Dmitry (dio) Levashov
3377
	 **/
3378
	abstract protected function _path($path);
3379
	
3380
	/**
3381
	 * Return true if $path is children of $parent
3382
	 *
3383
	 * @param  string  $path    path to check
3384
	 * @param  string  $parent  parent path
3385
	 * @return bool
3386
	 * @author Dmitry (dio) Levashov
3387
	 **/
3388
	abstract protected function _inpath($path, $parent);
3389
	
3390
	/**
3391
	 * Return stat for given path.
3392
	 * Stat contains following fields:
3393
	 * - (int)    size    file size in b. required
3394
	 * - (int)    ts      file modification time in unix time. required
3395
	 * - (string) mime    mimetype. required for folders, others - optionally
3396
	 * - (bool)   read    read permissions. required
3397
	 * - (bool)   write   write permissions. required
3398
	 * - (bool)   locked  is object locked. optionally
3399
	 * - (bool)   hidden  is object hidden. optionally
3400
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
3401
	 * - (string) target  for symlinks - link target path. optionally
3402
	 *
3403
	 * If file does not exists - returns empty array or false.
3404
	 *
3405
	 * @param  string  $path    file path 
3406
	 * @return array|false
3407
	 * @author Dmitry (dio) Levashov
3408
	 **/
3409
	abstract protected function _stat($path);
3410
	
3411
3412
	/***************** file stat ********************/
3413
3414
		
3415
	/**
3416
	 * Return true if path is dir and has at least one childs directory
3417
	 *
3418
	 * @param  string  $path  dir path
3419
	 * @return bool
3420
	 * @author Dmitry (dio) Levashov
3421
	 **/
3422
	abstract protected function _subdirs($path);
3423
	
3424
	/**
3425
	 * Return object width and height
3426
	 * Ususaly used for images, but can be realize for video etc...
3427
	 *
3428
	 * @param  string  $path  file path
3429
	 * @param  string  $mime  file mime type
3430
	 * @return string
3431
	 * @author Dmitry (dio) Levashov
3432
	 **/
3433
	abstract protected function _dimensions($path, $mime);
3434
	
3435
	/******************** file/dir content *********************/
3436
3437
	/**
3438
	 * Return files list in directory
3439
	 *
3440
	 * @param  string  $path  dir path
3441
	 * @return array
3442
	 * @author Dmitry (dio) Levashov
3443
	 **/
3444
	abstract protected function _scandir($path);
3445
	
3446
	/**
3447
	 * Open file and return file pointer
3448
	 *
3449
	 * @param  string  $path  file path
3450
	 * @param  bool    $write open file for writing
3451
	 * @return resource|false
3452
	 * @author Dmitry (dio) Levashov
3453
	 **/
3454
	abstract protected function _fopen($path, $mode="rb");
3455
	
3456
	/**
3457
	 * Close opened file
3458
	 * 
3459
	 * @param  resource  $fp    file pointer
3460
	 * @param  string    $path  file path
3461
	 * @return bool
3462
	 * @author Dmitry (dio) Levashov
3463
	 **/
3464
	abstract protected function _fclose($fp, $path='');
3465
	
3466
	/********************  file/dir manipulations *************************/
3467
	
3468
	/**
3469
	 * Create dir and return created dir path or false on failed
3470
	 *
3471
	 * @param  string  $path  parent dir path
3472
	 * @param string  $name  new directory name
3473
	 * @return string|bool
3474
	 * @author Dmitry (dio) Levashov
3475
	 **/
3476
	abstract protected function _mkdir($path, $name);
3477
	
3478
	/**
3479
	 * Create file and return it's path or false on failed
3480
	 *
3481
	 * @param  string  $path  parent dir path
3482
	 * @param string  $name  new file name
3483
	 * @return string|bool
3484
	 * @author Dmitry (dio) Levashov
3485
	 **/
3486
	abstract protected function _mkfile($path, $name);
3487
	
3488
	/**
3489
	 * Create symlink
3490
	 *
3491
	 * @param  string  $source     file to link to
3492
	 * @param  string  $targetDir  folder to create link in
3493
	 * @param  string  $name       symlink name
3494
	 * @return bool
3495
	 * @author Dmitry (dio) Levashov
3496
	 **/
3497
	abstract protected function _symlink($source, $targetDir, $name);
3498
	
3499
	/**
3500
	 * Copy file into another file (only inside one volume)
3501
	 *
3502
	 * @param  string  $source  source file path
3503
	 * @param  string  $target  target dir path
3504
	 * @param  string  $name    file name
3505
	 * @return bool
3506
	 * @author Dmitry (dio) Levashov
3507
	 **/
3508
	abstract protected function _copy($source, $targetDir, $name);
3509
	
3510
	/**
3511
	 * Move file into another parent dir.
3512
	 * Return new file path or false.
3513
	 *
3514
	 * @param  string  $source  source file path
3515
	 * @param  string  $target  target dir path
3516
	 * @param  string  $name    file name
3517
	 * @return string|bool
3518
	 * @author Dmitry (dio) Levashov
3519
	 **/
3520
	abstract protected function _move($source, $targetDir, $name);
3521
	
3522
	/**
3523
	 * Remove file
3524
	 *
3525
	 * @param  string  $path  file path
3526
	 * @return bool
3527
	 * @author Dmitry (dio) Levashov
3528
	 **/
3529
	abstract protected function _unlink($path);
3530
3531
	/**
3532
	 * Remove dir
3533
	 *
3534
	 * @param  string  $path  dir path
3535
	 * @return bool
3536
	 * @author Dmitry (dio) Levashov
3537
	 **/
3538
	abstract protected function _rmdir($path);
3539
3540
	/**
3541
	 * Create new file and write into it from file pointer.
3542
	 * Return new file path or false on error.
3543
	 *
3544
	 * @param  resource  $fp   file pointer
3545
	 * @param  string    $dir  target dir path
3546
	 * @param  string    $name file name
3547
	 * @param  array     $stat file stat (required by some virtual fs)
3548
	 * @return bool|string
3549
	 * @author Dmitry (dio) Levashov
3550
	 **/
3551
	abstract protected function _save($fp, $dir, $name, $stat);
3552
	
3553
	/**
3554
	 * Get file contents
3555
	 *
3556
	 * @param  string  $path  file path
3557
	 * @return string|false
3558
	 * @author Dmitry (dio) Levashov
3559
	 **/
3560
	abstract protected function _getContents($path);
3561
	
3562
	/**
3563
	 * Write a string to a file
3564
	 *
3565
	 * @param  string  $path     file path
3566
	 * @param  string  $content  new file content
3567
	 * @return bool
3568
	 * @author Dmitry (dio) Levashov
3569
	 **/
3570
	abstract protected function _filePutContents($path, $content);
3571
3572
	/**
3573
	 * Extract files from archive
3574
	 *
3575
	 * @param  string  $path file path
3576
	 * @param  array   $arc  archiver options
3577
	 * @return bool
3578
	 * @author Dmitry (dio) Levashov, 
3579
	 * @author Alexey Sukhotin
3580
	 **/
3581
	abstract protected function _extract($path, $arc);
3582
3583
	/**
3584
	 * Create archive and return its path
3585
	 *
3586
	 * @param  string  $dir    target dir
3587
	 * @param  array   $files  files names list
3588
	 * @param  string  $name   archive name
3589
	 * @param  array   $arc    archiver options
3590
	 * @return string|bool
3591
	 * @author Dmitry (dio) Levashov, 
3592
	 * @author Alexey Sukhotin
3593
	 **/
3594
	abstract protected function _archive($dir, $files, $name, $arc);
3595
3596
	/**
3597
	 * Detect available archivers
3598
	 *
3599
	 * @return void
3600
	 * @author Dmitry (dio) Levashov, 
3601
	 * @author Alexey Sukhotin
3602
	 **/
3603
	abstract protected function _checkArchivers();
3604
	
3605
} // END class
3606