elFinderVolumeMySQL.class.php 29 KB

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