Issues (661)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

php/elFinderVolumeMySQL.class.php (8 issues)

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
2
3
/**
4
 * Simple elFinder driver for MySQL.
5
 *
6
 * @author Dmitry (dio) Levashov
7
 **/
8
class elFinderVolumeMySQL extends elFinderVolumeDriver {
9
	
10
	/**
11
	 * Driver id
12
	 * Must be started from letter and contains [a-z0-9]
13
	 * Used as part of volume id
14
	 *
15
	 * @var string
16
	 **/
17
	protected $driverId = 'm';
18
	
19
	/**
20
	 * Database object
21
	 *
22
	 * @var mysqli
23
	 **/
24
	protected $db = null;
25
	
26
	/**
27
	 * Tables to store files
28
	 *
29
	 * @var string
30
	 **/
31
	protected $tbf = '';
32
	
33
	/**
34
	 * Directory for tmp files
35
	 * If not set driver will try to use tmbDir as tmpDir
36
	 *
37
	 * @var string
38
	 **/
39
	protected $tmpPath = '';
40
	
41
	/**
42
	 * Numbers of sql requests (for debug)
43
	 *
44
	 * @var int
45
	 **/
46
	protected $sqlCnt = 0;
47
	
48
	/**
49
	 * Last db error message
50
	 *
51
	 * @var string
52
	 **/
53
	protected $dbError = '';
54
	
55
	/**
56
	 * Constructor
57
	 * Extend options with required fields
58
	 *
59
	 * @return void
60
	 * @author Dmitry (dio) Levashov
61
	 **/
62
	public function __construct() {
63
		$opts = array(
64
			'host'          => 'localhost',
65
			'user'          => '',
66
			'pass'          => '',
67
			'db'            => '',
68
			'port'          => null,
69
			'socket'        => null,
70
			'files_table'   => 'elfinder_file',
71
			'tmbPath'       => '',
72
			'tmpPath'       => '',
73
			'rootCssClass'  => 'elfinder-navbar-root-sql'
74
		);
75
		$this->options = array_merge($this->options, $opts);
76
		$this->options['mimeDetect'] = 'internal';
77
	}
78
	
79
	/*********************************************************************/
80
	/*                        INIT AND CONFIGURE                         */
81
	/*********************************************************************/
82
	
83
	/**
84
	 * Prepare driver before mount volume.
85
	 * Connect to db, check required tables and fetch root path
86
	 *
87
	 * @return bool
88
	 * @author Dmitry (dio) Levashov
89
	 **/
90
	protected function init() {
91
		
92
		if (!($this->options['host'] || $this->options['socket'])
93
		||  !$this->options['user'] 
94
		||  !$this->options['pass'] 
95
		||  !$this->options['db']
96
		||  !$this->options['path']
97
		||  !$this->options['files_table']) {
98
			return false;
99
		}
100
		
101
		
102
		$this->db = new mysqli($this->options['host'], $this->options['user'], $this->options['pass'], $this->options['db'], $this->options['port'], $this->options['socket']);
103
		if ($this->db->connect_error || @mysqli_connect_error()) {
104
			return false;
105
		}
106
		
107
		$this->db->set_charset('utf8');
108
109
		if ($res = $this->db->query('SHOW TABLES')) {
110
			while ($row = $res->fetch_array()) {
111
				if ($row[0] == $this->options['files_table']) {
112
					$this->tbf = $this->options['files_table'];
113
					break;
114
				}
115
			}
116
		}
117
118
		if (!$this->tbf) {
119
			return false;
120
		}
121
122
		$this->updateCache($this->options['path'], $this->_stat($this->options['path']));
123
124
		return true;
125
	}
126
127
128
129
	/**
130
	 * Set tmp path
131
	 *
132
	 * @return void
133
	 * @author Dmitry (dio) Levashov
134
	 **/
135
	protected function configure() {
136
		parent::configure();
137
138
		if (($tmp = $this->options['tmpPath'])) {
139 View Code Duplication
			if (!file_exists($tmp)) {
140
				if (@mkdir($tmp)) {
141
					@chmod($tmp, $this->options['tmbPathMode']);
142
				}
143
			}
144
			
145
			$this->tmpPath = is_dir($tmp) && is_writable($tmp) ? $tmp : false;
146
		}
147
		
148
		if (!$this->tmpPath && $this->tmbPath && $this->tmbPathWritable) {
149
			$this->tmpPath = $this->tmbPath;
150
		}
151
152
		$this->mimeDetect = 'internal';
153
	}
154
	
155
	/**
156
	 * Close connection
157
	 *
158
	 * @return void
159
	 * @author Dmitry (dio) Levashov
160
	 **/
161
	public function umount() {
162
		$this->db->close();
163
	}
164
	
165
	/**
166
	 * Return debug info for client
167
	 *
168
	 * @return array
169
	 * @author Dmitry (dio) Levashov
170
	 **/
171
	public function debug() {
172
		$debug = parent::debug();
173
		$debug['sqlCount'] = $this->sqlCnt;
174
		if ($this->dbError) {
175
			$debug['dbError'] = $this->dbError;
176
		}
177
		return $debug;
178
	}
179
180
	/**
181
	 * Perform sql query and return result.
182
	 * Increase sqlCnt and save error if occured
183
	 *
184
	 * @param  string  $sql  query
185
	 * @return misc
186
	 * @author Dmitry (dio) Levashov
187
	 **/
188
	protected function query($sql) {
189
		$this->sqlCnt++;
190
		$res = $this->db->query($sql);
191
		if (!$res) {
192
			$this->dbError = $this->db->error;
193
		}
194
		return $res;
195
	}
196
197
	/**
198
	 * Create empty object with required mimetype
199
	 *
200
	 * @param  string  $path  parent dir path
201
	 * @param  string  $name  object name
202
	 * @param  string  $mime  mime type
203
	 * @return bool
204
	 * @author Dmitry (dio) Levashov
205
	 **/
206
	protected function make($path, $name, $mime) {
207
		$sql = 'INSERT INTO %s (`parent_id`, `name`, `size`, `mtime`, `mime`, `content`, `read`, `write`) VALUES ("%s", "%s", 0, %d, "%s", "", "%d", "%d")';
208
		$sql = sprintf($sql, $this->tbf, $path, $this->db->real_escape_string($name), time(), $mime, $this->defaults['read'], $this->defaults['write']);
209
		// echo $sql;
210
		return $this->query($sql) && $this->db->affected_rows > 0;
211
	}
212
213
	/*********************************************************************/
214
	/*                               FS API                              */
215
	/*********************************************************************/
216
	
217
	/**
218
	 * Cache dir contents
219
	 *
220
	 * @param  string  $path  dir path
221
	 * @return void
222
	 * @author Dmitry Levashov
223
	 **/
224
	protected function cacheDir($path) {
225
		$this->dirsCache[$path] = array();
226
227
		$sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, IF(ch.id, 1, 0) AS dirs 
228
				FROM '.$this->tbf.' AS f 
229
				LEFT JOIN '.$this->tbf.' AS ch ON ch.parent_id=f.id AND ch.mime="directory"
230
				WHERE f.parent_id="'.$path.'"
231
				GROUP BY f.id';
232
				
233
		$res = $this->query($sql);
234
		if ($res) {
235
			while ($row = $res->fetch_assoc()) {
236
				// debug($row);
237
				$id = $row['id'];
238
				if ($row['parent_id']) {
239
					$row['phash'] = $this->encode($row['parent_id']);
240
				} 
241
				
242 View Code Duplication
				if ($row['mime'] == 'directory') {
243
					unset($row['width']);
244
					unset($row['height']);
245
				} else {
246
					unset($row['dirs']);
247
				}
248
				
249
				unset($row['id']);
250
				unset($row['parent_id']);
251
				
252
				
253
				
254
				if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) {
255
					$this->dirsCache[$path][] = $id;
256
				}
257
			}
258
		}
259
		
260
		return $this->dirsCache[$path];
261
	}
262
263
	/**
264
	 * Return array of parents paths (ids)
265
	 *
266
	 * @param  int   $path  file path (id)
267
	 * @return array
268
	 * @author Dmitry (dio) Levashov
269
	 **/
270
	protected function getParents($path) {
271
		$parents = array();
272
273
		while ($path) {
274
			if ($file = $this->stat($path)) {
275
				array_unshift($parents, $path);
276
				$path = isset($file['phash']) ? $this->decode($file['phash']) : false;
277
			}
278
		}
279
		
280
		if (count($parents)) {
281
			array_pop($parents);
282
		}
283
		return $parents;
284
	}
285
286
	/**
287
	 * Return correct file path for LOAD_FILE method
288
	 *
289
	 * @param  string $path  file path (id)
290
	 * @return string
291
	 * @author Troex Nevelin
292
	 **/
293
	protected function loadFilePath($path) {
294
		$realPath = realpath($path);
295
		if (DIRECTORY_SEPARATOR == '\\') { // windows
296
			$realPath = str_replace('\\', '\\\\', $realPath);
297
		}
298
		return $this->db->real_escape_string($realPath);
299
	}
300
301
	/**
302
	 * Recursive files search
303
	 *
304
	 * @param  string  $path   dir path
305
	 * @param  string  $q      search string
306
	 * @param  array   $mimes
307
	 * @return array
308
	 * @author Dmitry (dio) Levashov
309
	 **/
310
	protected function doSearch($path, $q, $mimes) {
311
		$dirs = array();
312
		$timeout = $this->options['searchTimeout']? $this->searchStart + $this->options['searchTimeout'] : 0;
313
		
314
		if ($path != $this->root) {
315
			$inpath = array(intval($path));
316
			while($inpath) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $inpath of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
317
				$in = '('.join(',', $inpath).')';
318
				$inpath = array();
319
				$sql = 'SELECT f.id FROM %s AS f WHERE f.parent_id IN '.$in.' AND `mime` = \'directory\'';
320
				$sql = sprintf($sql, $this->tbf);
321
				if ($res = $this->query($sql)) {
322
					$_dir = array();
0 ignored issues
show
$_dir is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
323
					while ($dat = $res->fetch_assoc()) {
324
						$inpath[] = $dat['id'];
325
					}
326
					$dirs = array_merge($dirs, $inpath);
327
				}
328
			}
329
		}
330
331
		$result = array();
332
		
333
		if ($mimes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $mimes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
334
			$whrs = array();
335
			foreach($mimes as $mime) {
336
				if (strpos($mime, '/') === false) {
337
					$whrs[] = sprintf('f.mime LIKE "%s/%%"', $this->db->real_escape_string($mime));
338
				} else {
339
					$whrs[] = sprintf('f.mime = "%s"', $this->db->real_escape_string($mime));
340
				}
341
			}
342
			$whr = join(' OR ', $whrs);
343
		} else {
344
			$whr = sprintf('f.name RLIKE "%s"', $this->db->real_escape_string($q));
345
		}
346
		if ($dirs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $dirs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
347
			$whr = '(' . $whr . ') AND (`parent_id` IN (' . join(',', $dirs) . '))';
348
		}
349
		
350
		$sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, 0 AS dirs 
351
				FROM %s AS f 
352
				WHERE %s';
353
		
354
		$sql = sprintf($sql, $this->tbf, $whr);
355
		
356
		if (($res = $this->query($sql))) {
357
			while ($row = $res->fetch_assoc()) {
358 View Code Duplication
				if ($timeout && $timeout < time()) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
359
					$this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path)));
360
					break;
361
				}
362
				
363
				if (!$this->mimeAccepted($row['mime'], $mimes)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->mimeAccepted($row['mime'], $mimes) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
364
					continue;
365
				}
366
				$id = $row['id'];
367
				if ($row['parent_id']) {
368
					$row['phash'] = $this->encode($row['parent_id']);
369
				} 
370
				$row['path'] = $this->_path($id);
371
372 View Code Duplication
				if ($row['mime'] == 'directory') {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
373
					unset($row['width']);
374
					unset($row['height']);
375
				} else {
376
					unset($row['dirs']);
377
				}
378
379
				unset($row['id']);
380
				unset($row['parent_id']);
381
382
				if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) {
383
					$result[] = $stat;
384
				}
385
			}
386
		}
387
		
388
		return $result;
389
	}
390
391
392
	/*********************** paths/urls *************************/
393
	
394
	/**
395
	 * Return parent directory path
396
	 *
397
	 * @param  string  $path  file path
398
	 * @return string
399
	 * @author Dmitry (dio) Levashov
400
	 **/
401
	protected function _dirname($path) {
402
		return ($stat = $this->stat($path)) ? ($stat['phash'] ? $this->decode($stat['phash']) : $this->root) : false;
403
	}
404
405
	/**
406
	 * Return file name
407
	 *
408
	 * @param  string  $path  file path
409
	 * @return string
410
	 * @author Dmitry (dio) Levashov
411
	 **/
412
	protected function _basename($path) {
413
		return ($stat = $this->stat($path)) ? $stat['name'] : false;
414
	}
415
416
	/**
417
	 * Join dir name and file name and return full path
418
	 *
419
	 * @param  string  $dir
420
	 * @param  string  $name
421
	 * @return string
422
	 * @author Dmitry (dio) Levashov
423
	 **/
424
	protected function _joinPath($dir, $name) {
425
		$sql = 'SELECT id FROM '.$this->tbf.' WHERE parent_id="'.$dir.'" AND name="'.$this->db->real_escape_string($name).'"';
426
427
		if (($res = $this->query($sql)) && ($r = $res->fetch_assoc())) {
428
			$this->updateCache($r['id'], $this->_stat($r['id']));
429
			return $r['id'];
430
		}
431
		return -1;
432
	}
433
	
434
	/**
435
	 * Return normalized path, this works the same as os.path.normpath() in Python
436
	 *
437
	 * @param  string  $path  path
438
	 * @return string
439
	 * @author Troex Nevelin
440
	 **/
441
	protected function _normpath($path) {
442
		return $path;
443
	}
444
	
445
	/**
446
	 * Return file path related to root dir
447
	 *
448
	 * @param  string  $path  file path
449
	 * @return string
450
	 * @author Dmitry (dio) Levashov
451
	 **/
452
	protected function _relpath($path) {
453
		return $path;
454
	}
455
	
456
	/**
457
	 * Convert path related to root dir into real path
458
	 *
459
	 * @param  string  $path  file path
460
	 * @return string
461
	 * @author Dmitry (dio) Levashov
462
	 **/
463
	protected function _abspath($path) {
464
		return $path;
465
	}
466
	
467
	/**
468
	 * Return fake path started from root dir
469
	 *
470
	 * @param  string  $path  file path
471
	 * @return string
472
	 * @author Dmitry (dio) Levashov
473
	 **/
474
	protected function _path($path) {
475
		if (($file = $this->stat($path)) == false) {
476
			return '';
477
		}
478
		
479
		$parentsIds = $this->getParents($path);
480
		$path = '';
481
		foreach ($parentsIds as $id) {
482
			$dir = $this->stat($id);
483
			$path .= $dir['name'].$this->separator;
484
		}
485
		return $this->rootName.$this->separator.$path.$file['name'];
486
	}
487
	
488
	/**
489
	 * Return true if $path is children of $parent
490
	 *
491
	 * @param  string  $path    path to check
492
	 * @param  string  $parent  parent path
493
	 * @return bool
494
	 * @author Dmitry (dio) Levashov
495
	 **/
496
	protected function _inpath($path, $parent) {
497
		return $path == $parent
498
			? true
499
			: in_array($parent, $this->getParents($path));
500
	}
501
	
502
	/***************** file stat ********************/
503
	/**
504
	 * Return stat for given path.
505
	 * Stat contains following fields:
506
	 * - (int)    size    file size in b. required
507
	 * - (int)    ts      file modification time in unix time. required
508
	 * - (string) mime    mimetype. required for folders, others - optionally
509
	 * - (bool)   read    read permissions. required
510
	 * - (bool)   write   write permissions. required
511
	 * - (bool)   locked  is object locked. optionally
512
	 * - (bool)   hidden  is object hidden. optionally
513
	 * - (string) alias   for symlinks - link target path relative to root path. optionally
514
	 * - (string) target  for symlinks - link target path. optionally
515
	 *
516
	 * If file does not exists - returns empty array or false.
517
	 *
518
	 * @param  string  $path    file path 
519
	 * @return array|false
520
	 * @author Dmitry (dio) Levashov
521
	 **/
522
	protected function _stat($path) {
523
		$sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, IF(ch.id, 1, 0) AS dirs
524
				FROM '.$this->tbf.' AS f 
525
				LEFT JOIN '.$this->tbf.' AS p ON p.id=f.parent_id
526
				LEFT JOIN '.$this->tbf.' AS ch ON ch.parent_id=f.id AND ch.mime="directory"
527
				WHERE f.id="'.$path.'"
528
				GROUP BY f.id';
529
530
		$res = $this->query($sql);
531
		
532
		if ($res) {
533
			$stat = $res->fetch_assoc();
534
			if ($stat['parent_id']) {
535
				$stat['phash'] = $this->encode($stat['parent_id']);
536
			} 
537 View Code Duplication
			if ($stat['mime'] == 'directory') {
538
				unset($stat['width']);
539
				unset($stat['height']);
540
			} else {
541
				unset($stat['dirs']);
542
			}
543
			unset($stat['id']);
544
			unset($stat['parent_id']);
545
			return $stat;
546
			
547
		}
548
		return array();
549
	}
550
	
551
	/**
552
	 * Return true if path is dir and has at least one childs directory
553
	 *
554
	 * @param  string  $path  dir path
555
	 * @return bool
556
	 * @author Dmitry (dio) Levashov
557
	 **/
558
	protected function _subdirs($path) {
559
		return ($stat = $this->stat($path)) && isset($stat['dirs']) ? $stat['dirs'] : false;
560
	}
561
	
562
	/**
563
	 * Return object width and height
564
	 * Usualy used for images, but can be realize for video etc...
565
	 *
566
	 * @param  string  $path  file path
567
	 * @param  string  $mime  file mime type
568
	 * @return string
569
	 * @author Dmitry (dio) Levashov
570
	 **/
571
	protected function _dimensions($path, $mime) {
572
		return ($stat = $this->stat($path)) && isset($stat['width']) && isset($stat['height']) ? $stat['width'].'x'.$stat['height'] : '';
573
	}
574
	
575
	/******************** file/dir content *********************/
576
		
577
	/**
578
	 * Return files list in directory.
579
	 *
580
	 * @param  string  $path  dir path
581
	 * @return array
582
	 * @author Dmitry (dio) Levashov
583
	 **/
584
	protected function _scandir($path) {
585
		return isset($this->dirsCache[$path])
586
			? $this->dirsCache[$path]
587
			: $this->cacheDir($path);
588
	}
589
		
590
	/**
591
	 * Open file and return file pointer
592
	 *
593
	 * @param  string  $path  file path
594
	 * @param  string  $mode  open file mode (ignored in this driver)
595
	 * @return resource|false
596
	 * @author Dmitry (dio) Levashov
597
	 **/
598
	protected function _fopen($path, $mode='rb') {
599
		$fp = $this->tmbPath
600
			? @fopen($this->getTempFile($path), 'w+')
601
			: @tmpfile();
602
		
603
		
604
		if ($fp) {
605
			if (($res = $this->query('SELECT content FROM '.$this->tbf.' WHERE id="'.$path.'"'))
606
			&& ($r = $res->fetch_assoc())) {
607
				fwrite($fp, $r['content']);
608
				rewind($fp);
609
				return $fp;
610
			} else {
611
				$this->_fclose($fp, $path);
612
			}
613
		}
614
		
615
		return false;
616
	}
617
	
618
	/**
619
	 * Close opened file
620
	 *
621
	 * @param  resource  $fp  file pointer
622
	 * @return bool
623
	 * @author Dmitry (dio) Levashov
624
	 **/
625
	protected function _fclose($fp, $path='') {
626
		@fclose($fp);
627
		if ($path) {
628
			@unlink($this->getTempFile($path));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
629
		}
630
	}
631
	
632
	/********************  file/dir manipulations *************************/
633
	
634
	/**
635
	 * Create dir and return created dir path or false on failed
636
	 *
637
	 * @param  string  $path  parent dir path
638
	 * @param string  $name  new directory name
639
	 * @return string|bool
640
	 * @author Dmitry (dio) Levashov
641
	 **/
642
	protected function _mkdir($path, $name) {
643
		return $this->make($path, $name, 'directory') ? $this->_joinPath($path, $name) : false;
644
	}
645
	
646
	/**
647
	 * Create file and return it's path or false on failed
648
	 *
649
	 * @param  string  $path  parent dir path
650
	 * @param string  $name  new file name
651
	 * @return string|bool
652
	 * @author Dmitry (dio) Levashov
653
	 **/
654
	protected function _mkfile($path, $name) {
655
		return $this->make($path, $name, 'text/plain') ? $this->_joinPath($path, $name) : false;
656
	}
657
	
658
	/**
659
	 * Create symlink. FTP driver does not support symlinks.
660
	 *
661
	 * @param  string  $target  link target
662
	 * @param  string  $path    symlink path
663
	 * @return bool
664
	 * @author Dmitry (dio) Levashov
665
	 **/
666
	protected function _symlink($target, $path, $name) {
667
		return false;
668
	}
669
	
670
	/**
671
	 * Copy file into another file
672
	 *
673
	 * @param  string  $source     source file path
674
	 * @param  string  $targetDir  target directory path
675
	 * @param  string  $name       new file name
676
	 * @return bool
677
	 * @author Dmitry (dio) Levashov
678
	 **/
679
	protected function _copy($source, $targetDir, $name) {
680
		$this->clearcache();
681
		$id = $this->_joinPath($targetDir, $name);
682
683
		$sql = $id > 0
684
			? sprintf('REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden`) (SELECT %d, %d, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden` FROM %s WHERE id=%d)', $this->tbf, $id, $this->_dirname($id), $this->tbf, $source)
685
			: sprintf('INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden`) SELECT %d, "%s", content, size, %d, mime, width, height, `read`, `write`, `locked`, `hidden` FROM %s WHERE id=%d', $this->tbf, $targetDir, $this->db->real_escape_string($name), time(), $this->tbf, $source);
686
687
		return $this->query($sql);
688
	}
689
	
690
	/**
691
	 * Move file into another parent dir.
692
	 * Return new file path or false.
693
	 *
694
	 * @param  string  $source  source file path
695
	 * @param  string  $target  target dir path
696
	 * @param  string  $name    file name
697
	 * @return string|bool
698
	 * @author Dmitry (dio) Levashov
699
	 **/
700
	protected function _move($source, $targetDir, $name) {
701
		$sql = 'UPDATE %s SET parent_id=%d, name="%s" WHERE id=%d LIMIT 1';
702
		$sql = sprintf($sql, $this->tbf, $targetDir, $this->db->real_escape_string($name), $source);
703
		return $this->query($sql) && $this->db->affected_rows > 0 ? $source : false;
704
	}
705
		
706
	/**
707
	 * Remove file
708
	 *
709
	 * @param  string  $path  file path
710
	 * @return bool
711
	 * @author Dmitry (dio) Levashov
712
	 **/
713
	protected function _unlink($path) {
714
		return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime!="directory" LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows;
715
	}
716
717
	/**
718
	 * Remove dir
719
	 *
720
	 * @param  string  $path  dir path
721
	 * @return bool
722
	 * @author Dmitry (dio) Levashov
723
	 **/
724
	protected function _rmdir($path) {
725
		return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime="directory" LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows;
726
	}
727
	
728
	/**
729
	 * undocumented function
730
	 *
731
	 * @return void
732
	 * @author Dmitry Levashov
733
	 **/
734
	protected function _setContent($path, $fp) {
735
		rewind($fp);
736
		$fstat = fstat($fp);
737
		$size = $fstat['size'];
738
		
739
		
740
	}
741
	
742
	/**
743
	 * Create new file and write into it from file pointer.
744
	 * Return new file path or false on error.
745
	 *
746
	 * @param  resource  $fp   file pointer
747
	 * @param  string    $dir  target dir path
748
	 * @param  string    $name file name
749
	 * @param  array     $stat file stat (required by some virtual fs)
750
	 * @return bool|string
751
	 * @author Dmitry (dio) Levashov
752
	 **/
753
	protected function _save($fp, $dir, $name, $stat) {
754
		$this->clearcache();
755
		
756
		$mime = $stat['mime'];
757
		$w = !empty($stat['width'])  ? $stat['width']  : 0;
758
		$h = !empty($stat['height']) ? $stat['height'] : 0;
759
		
760
		$id = $this->_joinPath($dir, $name);
761
		rewind($fp);
762
		$stat = fstat($fp);
763
		$size = $stat['size'];
764
		
765
		if (($tmpfile = tempnam($this->tmpPath, $this->id))) {
766
			if (($trgfp = fopen($tmpfile, 'wb')) == false) {
767
				unlink($tmpfile);
768
			} else {
769
				while (!feof($fp)) {
770
					fwrite($trgfp, fread($fp, 8192));
771
				}
772
				fclose($trgfp);
773
				chmod($tmpfile, 0644);
774
				
775
				$sql = $id > 0
776
					? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES ('.$id.', %d, "%s", LOAD_FILE("%s"), %d, %d, "%s", %d, %d)'
777
					: 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, "%s", LOAD_FILE("%s"), %d, %d, "%s", %d, %d)';
778
				$sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->loadFilePath($tmpfile), $size, time(), $mime, $w, $h);
779
780
				$res = $this->query($sql);
781
				unlink($tmpfile);
782
				
783
				if ($res) {
784
					return $id > 0 ? $id : $this->db->insert_id;
785
				}
786
			}
787
		}
788
789
		
790
		$content = '';
791
		rewind($fp);
792
		while (!feof($fp)) {
793
			$content .= fread($fp, 8192);
794
		}
795
		
796
		$sql = $id > 0
797
			? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES ('.$id.', %d, "%s", "%s", %d, %d, "%s", %d, %d)'
798
			: 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, "%s", "%s", %d, %d, "%s", %d, %d)';
799
		$sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->db->real_escape_string($content), $size, time(), $mime, $w, $h);
800
		
801
		unset($content);
802
803
		if ($this->query($sql)) {
804
			return $id > 0 ? $id : $this->db->insert_id;
805
		}
806
		
807
		return false;
808
	}
809
	
810
	/**
811
	 * Get file contents
812
	 *
813
	 * @param  string  $path  file path
814
	 * @return string|false
815
	 * @author Dmitry (dio) Levashov
816
	 **/
817
	protected function _getContents($path) {
818
		return ($res = $this->query(sprintf('SELECT content FROM %s WHERE id=%d', $this->tbf, $path))) && ($r = $res->fetch_assoc()) ? $r['content'] : false;
819
	}
820
	
821
	/**
822
	 * Write a string to a file
823
	 *
824
	 * @param  string  $path     file path
825
	 * @param  string  $content  new file content
826
	 * @return bool
827
	 * @author Dmitry (dio) Levashov
828
	 **/
829
	protected function _filePutContents($path, $content) {
830
		return $this->query(sprintf('UPDATE %s SET content="%s", size=%d, mtime=%d WHERE id=%d LIMIT 1', $this->tbf, $this->db->real_escape_string($content), strlen($content), time(), $path));
831
	}
832
833
	/**
834
	 * Detect available archivers
835
	 *
836
	 * @return void
837
	 **/
838
	protected function _checkArchivers() {
839
		return;
840
	}
841
842
	/**
843
	 * chmod implementation
844
	 *
845
	 * @return bool
846
	 **/
847
	protected function _chmod($path, $mode) {
848
		return false;
849
	}
850
851
	/**
852
	 * Unpack archive
853
	 *
854
	 * @param  string  $path  archive path
855
	 * @param  array   $arc   archiver command and arguments (same as in $this->archivers)
856
	 * @return void
857
	 * @author Dmitry (dio) Levashov
858
	 * @author Alexey Sukhotin
859
	 **/
860
	protected function _unpack($path, $arc) {
861
		return;
862
	}
863
864
	/**
865
	 * Recursive symlinks search
866
	 *
867
	 * @param  string  $path  file/dir path
868
	 * @return bool
869
	 * @author Dmitry (dio) Levashov
870
	 **/
871
	protected function _findSymlinks($path) {
872
		return false;
873
	}
874
875
	/**
876
	 * Extract files from archive
877
	 *
878
	 * @param  string  $path  archive path
879
	 * @param  array   $arc   archiver command and arguments (same as in $this->archivers)
880
	 * @return true
881
	 * @author Dmitry (dio) Levashov, 
882
	 * @author Alexey Sukhotin
883
	 **/
884
	protected function _extract($path, $arc) {
885
		return false;
886
	}
887
	
888
	/**
889
	 * Create archive and return its path
890
	 *
891
	 * @param  string  $dir    target dir
892
	 * @param  array   $files  files names list
893
	 * @param  string  $name   archive name
894
	 * @param  array   $arc    archiver options
895
	 * @return string|bool
896
	 * @author Dmitry (dio) Levashov, 
897
	 * @author Alexey Sukhotin
898
	 **/
899
	protected function _archive($dir, $files, $name, $arc) {
900
		return false;
901
	}
902
	
903
} // END class 
904