1<?php 2 3/** 4 * Simple elFinder driver for MySQL. 5 * 6 * @author Dmitry (dio) Levashov 7 **/ 8class elFinderVolumeMySQL extends elFinderVolumeDriver 9{ 10 11 /** 12 * Driver id 13 * Must be started from letter and contains [a-z0-9] 14 * Used as part of volume id 15 * 16 * @var string 17 **/ 18 protected $driverId = 'm'; 19 20 /** 21 * Database object 22 * 23 * @var mysqli 24 **/ 25 protected $db = null; 26 27 /** 28 * Tables to store files 29 * 30 * @var string 31 **/ 32 protected $tbf = ''; 33 34 /** 35 * Directory for tmp files 36 * If not set driver will try to use tmbDir as tmpDir 37 * 38 * @var string 39 **/ 40 protected $tmpPath = ''; 41 42 /** 43 * Numbers of sql requests (for debug) 44 * 45 * @var int 46 **/ 47 protected $sqlCnt = 0; 48 49 /** 50 * Last db error message 51 * 52 * @var string 53 **/ 54 protected $dbError = ''; 55 56 /** 57 * This root has parent id 58 * 59 * @var boolean 60 */ 61 protected $rootHasParent = false; 62 63 /** 64 * Constructor 65 * Extend options with required fields 66 * 67 * @author Dmitry (dio) Levashov 68 */ 69 public function __construct() 70 { 71 $opts = array( 72 'host' => 'localhost', 73 'user' => '', 74 'pass' => '', 75 'db' => '', 76 'port' => null, 77 'socket' => null, 78 'files_table' => 'elfinder_file', 79 'tmbPath' => '', 80 'tmpPath' => '', 81 'rootCssClass' => 'elfinder-navbar-root-sql', 82 'noSessionCache' => array('hasdirs'), 83 'isLocalhost' => false 84 ); 85 $this->options = array_merge($this->options, $opts); 86 $this->options['mimeDetect'] = 'internal'; 87 } 88 89 /*********************************************************************/ 90 /* INIT AND CONFIGURE */ 91 /*********************************************************************/ 92 93 /** 94 * Prepare driver before mount volume. 95 * Connect to db, check required tables and fetch root path 96 * 97 * @return bool 98 * @author Dmitry (dio) Levashov 99 **/ 100 protected function init() 101 { 102 103 if (!($this->options['host'] || $this->options['socket']) 104 || !$this->options['user'] 105 || !$this->options['pass'] 106 || !$this->options['db'] 107 || !$this->options['path'] 108 || !$this->options['files_table']) { 109 return $this->setError('Required options "host", "socket", "user", "pass", "db", "path" or "files_table" are undefined.'); 110 } 111 112 $err = null; 113 if ($this->db = @new mysqli($this->options['host'], $this->options['user'], $this->options['pass'], $this->options['db'], $this->options['port'], $this->options['socket'])) { 114 if ($this->db && $this->db->connect_error) { 115 $err = $this->db->connect_error; 116 } 117 } else { 118 $err = mysqli_connect_error(); 119 } 120 if ($err) { 121 return $this->setError(array('Unable to connect to MySQL server.', $err)); 122 } 123 124 if (!$this->needOnline && empty($this->ARGS['init'])) { 125 $this->db->close(); 126 $this->db = null; 127 return true; 128 } 129 130 $this->db->set_charset('utf8'); 131 132 if ($res = $this->db->query('SHOW TABLES')) { 133 while ($row = $res->fetch_array()) { 134 if ($row[0] == $this->options['files_table']) { 135 $this->tbf = $this->options['files_table']; 136 break; 137 } 138 } 139 } 140 141 if (!$this->tbf) { 142 return $this->setError('The specified database table cannot be found.'); 143 } 144 145 $this->updateCache($this->options['path'], $this->_stat($this->options['path'])); 146 147 // enable command archive 148 $this->options['useRemoteArchive'] = true; 149 150 // check isLocalhost 151 $this->isLocalhost = $this->options['isLocalhost'] || $this->options['host'] === 'localhost' || $this->options['host'] === '127.0.0.1' || $this->options['host'] === '::1'; 152 153 return true; 154 } 155 156 157 /** 158 * Set tmp path 159 * 160 * @return void 161 * @throws elFinderAbortException 162 * @author Dmitry (dio) Levashov 163 */ 164 protected function configure() 165 { 166 parent::configure(); 167 168 if (($tmp = $this->options['tmpPath'])) { 169 if (!file_exists($tmp)) { 170 if (mkdir($tmp)) { 171 chmod($tmp, $this->options['tmbPathMode']); 172 } 173 } 174 175 $this->tmpPath = is_dir($tmp) && is_writable($tmp) ? $tmp : false; 176 } 177 if (!$this->tmpPath && ($tmp = elFinder::getStaticVar('commonTempPath'))) { 178 $this->tmpPath = $tmp; 179 } 180 181 // fallback of $this->tmp 182 if (!$this->tmpPath && $this->tmbPathWritable) { 183 $this->tmpPath = $this->tmbPath; 184 } 185 186 $this->mimeDetect = 'internal'; 187 } 188 189 /** 190 * Close connection 191 * 192 * @return void 193 * @author Dmitry (dio) Levashov 194 **/ 195 public function umount() 196 { 197 $this->db && $this->db->close(); 198 } 199 200 /** 201 * Return debug info for client 202 * 203 * @return array 204 * @author Dmitry (dio) Levashov 205 **/ 206 public function debug() 207 { 208 $debug = parent::debug(); 209 $debug['sqlCount'] = $this->sqlCnt; 210 if ($this->dbError) { 211 $debug['dbError'] = $this->dbError; 212 } 213 return $debug; 214 } 215 216 /** 217 * Perform sql query and return result. 218 * Increase sqlCnt and save error if occured 219 * 220 * @param string $sql query 221 * 222 * @return bool|mysqli_result 223 * @author Dmitry (dio) Levashov 224 */ 225 protected function query($sql) 226 { 227 $this->sqlCnt++; 228 $res = $this->db->query($sql); 229 if (!$res) { 230 $this->dbError = $this->db->error; 231 } 232 return $res; 233 } 234 235 /** 236 * Create empty object with required mimetype 237 * 238 * @param string $path parent dir path 239 * @param string $name object name 240 * @param string $mime mime type 241 * 242 * @return bool 243 * @author Dmitry (dio) Levashov 244 **/ 245 protected function make($path, $name, $mime) 246 { 247 $sql = 'INSERT INTO %s (`parent_id`, `name`, `size`, `mtime`, `mime`, `content`, `read`, `write`, `locked`, `hidden`, `width`, `height`) VALUES (\'%s\', \'%s\', 0, %d, \'%s\', \'\', \'%d\', \'%d\', \'%d\', \'%d\', 0, 0)'; 248 $sql = sprintf($sql, $this->tbf, $path, $this->db->real_escape_string($name), time(), $mime, $this->defaults['read'], $this->defaults['write'], $this->defaults['locked'], $this->defaults['hidden']); 249 // echo $sql; 250 return $this->query($sql) && $this->db->affected_rows > 0; 251 } 252 253 /*********************************************************************/ 254 /* FS API */ 255 /*********************************************************************/ 256 257 /** 258 * Cache dir contents 259 * 260 * @param string $path dir path 261 * 262 * @return string 263 * @author Dmitry Levashov 264 **/ 265 protected function cacheDir($path) 266 { 267 $this->dirsCache[$path] = array(); 268 269 $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 270 FROM ' . $this->tbf . ' AS f 271 LEFT JOIN ' . $this->tbf . ' AS ch ON ch.parent_id=f.id AND ch.mime=\'directory\' 272 WHERE f.parent_id=\'' . $path . '\' 273 GROUP BY f.id, ch.id'; 274 275 $res = $this->query($sql); 276 if ($res) { 277 while ($row = $res->fetch_assoc()) { 278 $id = $row['id']; 279 if ($row['parent_id'] && $id != $this->root) { 280 $row['phash'] = $this->encode($row['parent_id']); 281 } 282 283 if ($row['mime'] == 'directory') { 284 unset($row['width']); 285 unset($row['height']); 286 $row['size'] = 0; 287 } else { 288 unset($row['dirs']); 289 } 290 291 unset($row['id']); 292 unset($row['parent_id']); 293 294 295 if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) { 296 $this->dirsCache[$path][] = $id; 297 } 298 } 299 } 300 301 return $this->dirsCache[$path]; 302 } 303 304 /** 305 * Return array of parents paths (ids) 306 * 307 * @param int $path file path (id) 308 * 309 * @return array 310 * @author Dmitry (dio) Levashov 311 **/ 312 protected function getParents($path) 313 { 314 $parents = array(); 315 316 while ($path) { 317 if ($file = $this->stat($path)) { 318 array_unshift($parents, $path); 319 $path = isset($file['phash']) ? $this->decode($file['phash']) : false; 320 } 321 } 322 323 if (count($parents)) { 324 array_pop($parents); 325 } 326 return $parents; 327 } 328 329 /** 330 * Return correct file path for LOAD_FILE method 331 * 332 * @param string $path file path (id) 333 * 334 * @return string 335 * @author Troex Nevelin 336 **/ 337 protected function loadFilePath($path) 338 { 339 $realPath = realpath($path); 340 if (DIRECTORY_SEPARATOR == '\\') { // windows 341 $realPath = str_replace('\\', '\\\\', $realPath); 342 } 343 return $this->db->real_escape_string($realPath); 344 } 345 346 /** 347 * Recursive files search 348 * 349 * @param string $path dir path 350 * @param string $q search string 351 * @param array $mimes 352 * 353 * @return array 354 * @throws elFinderAbortException 355 * @author Dmitry (dio) Levashov 356 */ 357 protected function doSearch($path, $q, $mimes) 358 { 359 if (!empty($this->doSearchCurrentQuery['matchMethod'])) { 360 // has custom match method use elFinderVolumeDriver::doSearch() 361 return parent::doSearch($path, $q, $mimes); 362 } 363 364 $dirs = array(); 365 $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0; 366 367 if ($path != $this->root || $this->rootHasParent) { 368 $dirs = $inpath = array(intval($path)); 369 while ($inpath) { 370 $in = '(' . join(',', $inpath) . ')'; 371 $inpath = array(); 372 $sql = 'SELECT f.id FROM %s AS f WHERE f.parent_id IN ' . $in . ' AND `mime` = \'directory\''; 373 $sql = sprintf($sql, $this->tbf); 374 if ($res = $this->query($sql)) { 375 $_dir = array(); 376 while ($dat = $res->fetch_assoc()) { 377 $inpath[] = $dat['id']; 378 } 379 $dirs = array_merge($dirs, $inpath); 380 } 381 } 382 } 383 384 $result = array(); 385 386 if ($mimes) { 387 $whrs = array(); 388 foreach ($mimes as $mime) { 389 if (strpos($mime, '/') === false) { 390 $whrs[] = sprintf('f.mime LIKE \'%s/%%\'', $this->db->real_escape_string($mime)); 391 } else { 392 $whrs[] = sprintf('f.mime = \'%s\'', $this->db->real_escape_string($mime)); 393 } 394 } 395 $whr = join(' OR ', $whrs); 396 } else { 397 $whr = sprintf('f.name LIKE \'%%%s%%\'', $this->db->real_escape_string($q)); 398 } 399 if ($dirs) { 400 $whr = '(' . $whr . ') AND (`parent_id` IN (' . join(',', $dirs) . '))'; 401 } 402 403 $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 404 FROM %s AS f 405 WHERE %s'; 406 407 $sql = sprintf($sql, $this->tbf, $whr); 408 409 if (($res = $this->query($sql))) { 410 while ($row = $res->fetch_assoc()) { 411 if ($timeout && $timeout < time()) { 412 $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path))); 413 break; 414 } 415 416 if (!$this->mimeAccepted($row['mime'], $mimes)) { 417 continue; 418 } 419 $id = $row['id']; 420 if ($id == $this->root) { 421 continue; 422 } 423 if ($row['parent_id'] && $id != $this->root) { 424 $row['phash'] = $this->encode($row['parent_id']); 425 } 426 $row['path'] = $this->_path($id); 427 428 if ($row['mime'] == 'directory') { 429 unset($row['width']); 430 unset($row['height']); 431 } else { 432 unset($row['dirs']); 433 } 434 435 unset($row['id']); 436 unset($row['parent_id']); 437 438 if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) { 439 $result[] = $stat; 440 } 441 } 442 } 443 return $result; 444 } 445 446 447 /*********************** paths/urls *************************/ 448 449 /** 450 * Return parent directory path 451 * 452 * @param string $path file path 453 * 454 * @return string 455 * @author Dmitry (dio) Levashov 456 **/ 457 protected function _dirname($path) 458 { 459 return ($stat = $this->stat($path)) ? (!empty($stat['phash']) ? $this->decode($stat['phash']) : $this->root) : false; 460 } 461 462 /** 463 * Return file name 464 * 465 * @param string $path file path 466 * 467 * @return string 468 * @author Dmitry (dio) Levashov 469 **/ 470 protected function _basename($path) 471 { 472 return (($stat = $this->stat($path)) && isset($stat['name'])) ? $stat['name'] : false; 473 } 474 475 /** 476 * Join dir name and file name and return full path 477 * 478 * @param string $dir 479 * @param string $name 480 * 481 * @return string 482 * @author Dmitry (dio) Levashov 483 **/ 484 protected function _joinPath($dir, $name) 485 { 486 $sql = 'SELECT id FROM ' . $this->tbf . ' WHERE parent_id=\'' . $dir . '\' AND name=\'' . $this->db->real_escape_string($name) . '\''; 487 488 if (($res = $this->query($sql)) && ($r = $res->fetch_assoc())) { 489 $this->updateCache($r['id'], $this->_stat($r['id'])); 490 return $r['id']; 491 } 492 return -1; 493 } 494 495 /** 496 * Return normalized path, this works the same as os.path.normpath() in Python 497 * 498 * @param string $path path 499 * 500 * @return string 501 * @author Troex Nevelin 502 **/ 503 protected function _normpath($path) 504 { 505 return $path; 506 } 507 508 /** 509 * Return file path related to root dir 510 * 511 * @param string $path file path 512 * 513 * @return string 514 * @author Dmitry (dio) Levashov 515 **/ 516 protected function _relpath($path) 517 { 518 return $path; 519 } 520 521 /** 522 * Convert path related to root dir into real path 523 * 524 * @param string $path file path 525 * 526 * @return string 527 * @author Dmitry (dio) Levashov 528 **/ 529 protected function _abspath($path) 530 { 531 return $path; 532 } 533 534 /** 535 * Return fake path started from root dir 536 * 537 * @param string $path file path 538 * 539 * @return string 540 * @author Dmitry (dio) Levashov 541 **/ 542 protected function _path($path) 543 { 544 if (($file = $this->stat($path)) == false) { 545 return ''; 546 } 547 548 $parentsIds = $this->getParents($path); 549 $path = ''; 550 foreach ($parentsIds as $id) { 551 $dir = $this->stat($id); 552 $path .= $dir['name'] . $this->separator; 553 } 554 return $path . $file['name']; 555 } 556 557 /** 558 * Return true if $path is children of $parent 559 * 560 * @param string $path path to check 561 * @param string $parent parent path 562 * 563 * @return bool 564 * @author Dmitry (dio) Levashov 565 **/ 566 protected function _inpath($path, $parent) 567 { 568 return $path == $parent 569 ? true 570 : in_array($parent, $this->getParents($path)); 571 } 572 573 /***************** file stat ********************/ 574 /** 575 * Return stat for given path. 576 * Stat contains following fields: 577 * - (int) size file size in b. required 578 * - (int) ts file modification time in unix time. required 579 * - (string) mime mimetype. required for folders, others - optionally 580 * - (bool) read read permissions. required 581 * - (bool) write write permissions. required 582 * - (bool) locked is object locked. optionally 583 * - (bool) hidden is object hidden. optionally 584 * - (string) alias for symlinks - link target path relative to root path. optionally 585 * - (string) target for symlinks - link target path. optionally 586 * If file does not exists - returns empty array or false. 587 * 588 * @param string $path file path 589 * 590 * @return array|false 591 * @author Dmitry (dio) Levashov 592 **/ 593 protected function _stat($path) 594 { 595 $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 596 FROM ' . $this->tbf . ' AS f 597 LEFT JOIN ' . $this->tbf . ' AS ch ON ch.parent_id=f.id AND ch.mime=\'directory\' 598 WHERE f.id=\'' . $path . '\' 599 GROUP BY f.id, ch.id'; 600 601 $res = $this->query($sql); 602 603 if ($res) { 604 $stat = $res->fetch_assoc(); 605 if ($stat['id'] == $this->root) { 606 $this->rootHasParent = true; 607 $stat['parent_id'] = ''; 608 } 609 if ($stat['parent_id']) { 610 $stat['phash'] = $this->encode($stat['parent_id']); 611 } 612 if ($stat['mime'] == 'directory') { 613 unset($stat['width']); 614 unset($stat['height']); 615 $stat['size'] = 0; 616 } else { 617 if (!$stat['mime']) { 618 unset($stat['mime']); 619 } 620 unset($stat['dirs']); 621 } 622 unset($stat['id']); 623 unset($stat['parent_id']); 624 return $stat; 625 626 } 627 return array(); 628 } 629 630 /** 631 * Return true if path is dir and has at least one childs directory 632 * 633 * @param string $path dir path 634 * 635 * @return bool 636 * @author Dmitry (dio) Levashov 637 **/ 638 protected function _subdirs($path) 639 { 640 return ($stat = $this->stat($path)) && isset($stat['dirs']) ? $stat['dirs'] : false; 641 } 642 643 /** 644 * Return object width and height 645 * Usualy used for images, but can be realize for video etc... 646 * 647 * @param string $path file path 648 * @param string $mime file mime type 649 * 650 * @return string 651 * @author Dmitry (dio) Levashov 652 **/ 653 protected function _dimensions($path, $mime) 654 { 655 return ($stat = $this->stat($path)) && isset($stat['width']) && isset($stat['height']) ? $stat['width'] . 'x' . $stat['height'] : ''; 656 } 657 658 /******************** file/dir content *********************/ 659 660 /** 661 * Return files list in directory. 662 * 663 * @param string $path dir path 664 * 665 * @return array 666 * @author Dmitry (dio) Levashov 667 **/ 668 protected function _scandir($path) 669 { 670 return isset($this->dirsCache[$path]) 671 ? $this->dirsCache[$path] 672 : $this->cacheDir($path); 673 } 674 675 /** 676 * Open file and return file pointer 677 * 678 * @param string $path file path 679 * @param string $mode open file mode (ignored in this driver) 680 * 681 * @return resource|false 682 * @author Dmitry (dio) Levashov 683 **/ 684 protected function _fopen($path, $mode = 'rb') 685 { 686 $fp = $this->tmpPath 687 ? fopen($this->getTempFile($path), 'w+') 688 : $this->tmpfile(); 689 690 691 if ($fp) { 692 if (($res = $this->query('SELECT content FROM ' . $this->tbf . ' WHERE id=\'' . $path . '\'')) 693 && ($r = $res->fetch_assoc())) { 694 fwrite($fp, $r['content']); 695 rewind($fp); 696 return $fp; 697 } else { 698 $this->_fclose($fp, $path); 699 } 700 } 701 702 return false; 703 } 704 705 /** 706 * Close opened file 707 * 708 * @param resource $fp file pointer 709 * @param string $path 710 * 711 * @return void 712 * @author Dmitry (dio) Levashov 713 */ 714 protected function _fclose($fp, $path = '') 715 { 716 is_resource($fp) && fclose($fp); 717 if ($path) { 718 $file = $this->getTempFile($path); 719 is_file($file) && unlink($file); 720 } 721 } 722 723 /******************** file/dir manipulations *************************/ 724 725 /** 726 * Create dir and return created dir path or false on failed 727 * 728 * @param string $path parent dir path 729 * @param string $name new directory name 730 * 731 * @return string|bool 732 * @author Dmitry (dio) Levashov 733 **/ 734 protected function _mkdir($path, $name) 735 { 736 return $this->make($path, $name, 'directory') ? $this->_joinPath($path, $name) : false; 737 } 738 739 /** 740 * Create file and return it's path or false on failed 741 * 742 * @param string $path parent dir path 743 * @param string $name new file name 744 * 745 * @return string|bool 746 * @author Dmitry (dio) Levashov 747 **/ 748 protected function _mkfile($path, $name) 749 { 750 return $this->make($path, $name, '') ? $this->_joinPath($path, $name) : false; 751 } 752 753 /** 754 * Create symlink. FTP driver does not support symlinks. 755 * 756 * @param string $target link target 757 * @param string $path symlink path 758 * @param string $name 759 * 760 * @return bool 761 * @author Dmitry (dio) Levashov 762 */ 763 protected function _symlink($target, $path, $name) 764 { 765 return false; 766 } 767 768 /** 769 * Copy file into another file 770 * 771 * @param string $source source file path 772 * @param string $targetDir target directory path 773 * @param string $name new file name 774 * 775 * @return bool 776 * @author Dmitry (dio) Levashov 777 **/ 778 protected function _copy($source, $targetDir, $name) 779 { 780 $this->clearcache(); 781 $id = $this->_joinPath($targetDir, $name); 782 783 $sql = $id > 0 784 ? 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) 785 : 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); 786 787 return $this->query($sql); 788 } 789 790 /** 791 * Move file into another parent dir. 792 * Return new file path or false. 793 * 794 * @param string $source source file path 795 * @param $targetDir 796 * @param string $name file name 797 * 798 * @return bool|string 799 * @internal param string $target target dir path 800 * @author Dmitry (dio) Levashov 801 */ 802 protected function _move($source, $targetDir, $name) 803 { 804 $sql = 'UPDATE %s SET parent_id=%d, name=\'%s\' WHERE id=%d LIMIT 1'; 805 $sql = sprintf($sql, $this->tbf, $targetDir, $this->db->real_escape_string($name), $source); 806 return $this->query($sql) && $this->db->affected_rows > 0 ? $source : false; 807 } 808 809 /** 810 * Remove file 811 * 812 * @param string $path file path 813 * 814 * @return bool 815 * @author Dmitry (dio) Levashov 816 **/ 817 protected function _unlink($path) 818 { 819 return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime!=\'directory\' LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows; 820 } 821 822 /** 823 * Remove dir 824 * 825 * @param string $path dir path 826 * 827 * @return bool 828 * @author Dmitry (dio) Levashov 829 **/ 830 protected function _rmdir($path) 831 { 832 return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime=\'directory\' LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows; 833 } 834 835 /** 836 * undocumented function 837 * 838 * @param $path 839 * @param $fp 840 * 841 * @author Dmitry Levashov 842 */ 843 protected function _setContent($path, $fp) 844 { 845 elFinder::rewind($fp); 846 $fstat = fstat($fp); 847 $size = $fstat['size']; 848 849 850 } 851 852 /** 853 * Create new file and write into it from file pointer. 854 * Return new file path or false on error. 855 * 856 * @param resource $fp file pointer 857 * @param string $dir target dir path 858 * @param string $name file name 859 * @param array $stat file stat (required by some virtual fs) 860 * 861 * @return bool|string 862 * @author Dmitry (dio) Levashov 863 **/ 864 protected function _save($fp, $dir, $name, $stat) 865 { 866 $this->clearcache(); 867 868 $mime = !empty($stat['mime']) ? $stat['mime'] : $this->mimetype($name, true); 869 $w = !empty($stat['width']) ? $stat['width'] : 0; 870 $h = !empty($stat['height']) ? $stat['height'] : 0; 871 $ts = !empty($stat['ts']) ? $stat['ts'] : time(); 872 873 $id = $this->_joinPath($dir, $name); 874 if (!isset($stat['size'])) { 875 $stat = fstat($fp); 876 $size = $stat['size']; 877 } else { 878 $size = $stat['size']; 879 } 880 881 if ($this->isLocalhost && ($tmpfile = tempnam($this->tmpPath, $this->id))) { 882 if (($trgfp = fopen($tmpfile, 'wb')) == false) { 883 unlink($tmpfile); 884 } else { 885 elFinder::rewind($fp); 886 stream_copy_to_stream($fp, $trgfp); 887 fclose($trgfp); 888 chmod($tmpfile, 0644); 889 890 $sql = $id > 0 891 ? '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)' 892 : 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, \'%s\', LOAD_FILE(\'%s\'), %d, %d, \'%s\', %d, %d)'; 893 $sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->loadFilePath($tmpfile), $size, $ts, $mime, $w, $h); 894 895 $res = $this->query($sql); 896 unlink($tmpfile); 897 898 if ($res) { 899 return $id > 0 ? $id : $this->db->insert_id; 900 } 901 } 902 } 903 904 905 $content = ''; 906 elFinder::rewind($fp); 907 while (!feof($fp)) { 908 $content .= fread($fp, 8192); 909 } 910 911 $sql = $id > 0 912 ? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES (' . $id . ', %d, \'%s\', \'%s\', %d, %d, \'%s\', %d, %d)' 913 : 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, \'%s\', \'%s\', %d, %d, \'%s\', %d, %d)'; 914 $sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->db->real_escape_string($content), $size, $ts, $mime, $w, $h); 915 916 unset($content); 917 918 if ($this->query($sql)) { 919 return $id > 0 ? $id : $this->db->insert_id; 920 } 921 922 return false; 923 } 924 925 /** 926 * Get file contents 927 * 928 * @param string $path file path 929 * 930 * @return string|false 931 * @author Dmitry (dio) Levashov 932 **/ 933 protected function _getContents($path) 934 { 935 return ($res = $this->query(sprintf('SELECT content FROM %s WHERE id=%d', $this->tbf, $path))) && ($r = $res->fetch_assoc()) ? $r['content'] : false; 936 } 937 938 /** 939 * Write a string to a file 940 * 941 * @param string $path file path 942 * @param string $content new file content 943 * 944 * @return bool 945 * @author Dmitry (dio) Levashov 946 **/ 947 protected function _filePutContents($path, $content) 948 { 949 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)); 950 } 951 952 /** 953 * Detect available archivers 954 * 955 * @return void 956 **/ 957 protected function _checkArchivers() 958 { 959 return; 960 } 961 962 /** 963 * chmod implementation 964 * 965 * @param string $path 966 * @param string $mode 967 * 968 * @return bool 969 */ 970 protected function _chmod($path, $mode) 971 { 972 return false; 973 } 974 975 /** 976 * Unpack archive 977 * 978 * @param string $path archive path 979 * @param array $arc archiver command and arguments (same as in $this->archivers) 980 * 981 * @return void 982 * @author Dmitry (dio) Levashov 983 * @author Alexey Sukhotin 984 **/ 985 protected function _unpack($path, $arc) 986 { 987 return; 988 } 989 990 /** 991 * Extract files from archive 992 * 993 * @param string $path archive path 994 * @param array $arc archiver command and arguments (same as in $this->archivers) 995 * 996 * @return true 997 * @author Dmitry (dio) Levashov, 998 * @author Alexey Sukhotin 999 **/ 1000 protected function _extract($path, $arc) 1001 { 1002 return false; 1003 } 1004 1005 /** 1006 * Create archive and return its path 1007 * 1008 * @param string $dir target dir 1009 * @param array $files files names list 1010 * @param string $name archive name 1011 * @param array $arc archiver options 1012 * 1013 * @return string|bool 1014 * @author Dmitry (dio) Levashov, 1015 * @author Alexey Sukhotin 1016 **/ 1017 protected function _archive($dir, $files, $name, $arc) 1018 { 1019 return false; 1020 } 1021 1022} // END class 1023