/libraries/joomla/filesystem/file.php

https://gitlab.com/vitaliylukin91/alex-lavka · PHP · 594 lines · 339 code · 90 blank · 165 comment · 54 complexity · 655c83abfff808c84b8f98d029eb30bc MD5 · raw file

  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage FileSystem
  5. *
  6. * @copyright Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. defined('JPATH_PLATFORM') or die;
  10. /**
  11. * A File handling class
  12. *
  13. * @since 11.1
  14. */
  15. class JFile
  16. {
  17. /**
  18. * Gets the extension of a file name
  19. *
  20. * @param string $file The file name
  21. *
  22. * @return string The file extension
  23. *
  24. * @since 11.1
  25. */
  26. public static function getExt($file)
  27. {
  28. $dot = strrpos($file, '.') + 1;
  29. return substr($file, $dot);
  30. }
  31. /**
  32. * Strips the last extension off of a file name
  33. *
  34. * @param string $file The file name
  35. *
  36. * @return string The file name without the extension
  37. *
  38. * @since 11.1
  39. */
  40. public static function stripExt($file)
  41. {
  42. return preg_replace('#\.[^.]*$#', '', $file);
  43. }
  44. /**
  45. * Makes file name safe to use
  46. *
  47. * @param string $file The name of the file [not full path]
  48. *
  49. * @return string The sanitised string
  50. *
  51. * @since 11.1
  52. */
  53. public static function makeSafe($file)
  54. {
  55. // Remove any trailing dots, as those aren't ever valid file names.
  56. $file = rtrim($file, '.');
  57. $regex = array('#(\.){2,}#', '#[^A-Za-z0-9\.\_\- ]#', '#^\.#');
  58. return trim(preg_replace($regex, '', $file));
  59. }
  60. /**
  61. * Copies a file
  62. *
  63. * @param string $src The path to the source file
  64. * @param string $dest The path to the destination file
  65. * @param string $path An optional base path to prefix to the file names
  66. * @param boolean $use_streams True to use streams
  67. *
  68. * @return boolean True on success
  69. *
  70. * @since 11.1
  71. */
  72. public static function copy($src, $dest, $path = null, $use_streams = false)
  73. {
  74. $pathObject = new JFilesystemWrapperPath;
  75. // Prepend a base path if it exists
  76. if ($path)
  77. {
  78. $src = $pathObject->clean($path . '/' . $src);
  79. $dest = $pathObject->clean($path . '/' . $dest);
  80. }
  81. // Check src path
  82. if (!is_readable($src))
  83. {
  84. JLog::add(JText::sprintf('JLIB_FILESYSTEM_ERROR_JFILE_FIND_COPY', $src), JLog::WARNING, 'jerror');
  85. return false;
  86. }
  87. if ($use_streams)
  88. {
  89. $stream = JFactory::getStream();
  90. if (!$stream->copy($src, $dest))
  91. {
  92. JLog::add(JText::sprintf('JLIB_FILESYSTEM_ERROR_JFILE_STREAMS', $src, $dest, $stream->getError()), JLog::WARNING, 'jerror');
  93. return false;
  94. }
  95. return true;
  96. }
  97. else
  98. {
  99. $FTPOptions = JClientHelper::getCredentials('ftp');
  100. if ($FTPOptions['enabled'] == 1)
  101. {
  102. // Connect the FTP client
  103. $ftp = JClientFtp::getInstance($FTPOptions['host'], $FTPOptions['port'], array(), $FTPOptions['user'], $FTPOptions['pass']);
  104. // If the parent folder doesn't exist we must create it
  105. if (!file_exists(dirname($dest)))
  106. {
  107. $folderObject = new JFilesystemWrapperFolder;
  108. $folderObject->create(dirname($dest));
  109. }
  110. // Translate the destination path for the FTP account
  111. $dest = $pathObject->clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $dest), '/');
  112. if (!$ftp->store($src, $dest))
  113. {
  114. // FTP connector throws an error
  115. return false;
  116. }
  117. $ret = true;
  118. }
  119. else
  120. {
  121. if (!@ copy($src, $dest))
  122. {
  123. JLog::add(JText::_('JLIB_FILESYSTEM_ERROR_COPY_FAILED'), JLog::WARNING, 'jerror');
  124. return false;
  125. }
  126. $ret = true;
  127. }
  128. return $ret;
  129. }
  130. }
  131. /**
  132. * Delete a file or array of files
  133. *
  134. * @param mixed $file The file name or an array of file names
  135. *
  136. * @return boolean True on success
  137. *
  138. * @since 11.1
  139. */
  140. public static function delete($file)
  141. {
  142. $FTPOptions = JClientHelper::getCredentials('ftp');
  143. $pathObject = new JFilesystemWrapperPath;
  144. if (is_array($file))
  145. {
  146. $files = $file;
  147. }
  148. else
  149. {
  150. $files[] = $file;
  151. }
  152. // Do NOT use ftp if it is not enabled
  153. if ($FTPOptions['enabled'] == 1)
  154. {
  155. // Connect the FTP client
  156. $ftp = JClientFtp::getInstance($FTPOptions['host'], $FTPOptions['port'], array(), $FTPOptions['user'], $FTPOptions['pass']);
  157. }
  158. foreach ($files as $file)
  159. {
  160. $file = $pathObject->clean($file);
  161. if (!is_file($file))
  162. {
  163. continue;
  164. }
  165. // Try making the file writable first. If it's read-only, it can't be deleted
  166. // on Windows, even if the parent folder is writable
  167. @chmod($file, 0777);
  168. // In case of restricted permissions we zap it one way or the other
  169. // as long as the owner is either the webserver or the ftp
  170. if (@unlink($file))
  171. {
  172. // Do nothing
  173. }
  174. elseif ($FTPOptions['enabled'] == 1)
  175. {
  176. $file = $pathObject->clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $file), '/');
  177. if (!$ftp->delete($file))
  178. {
  179. // FTP connector throws an error
  180. return false;
  181. }
  182. }
  183. else
  184. {
  185. $filename = basename($file);
  186. JLog::add(JText::sprintf('JLIB_FILESYSTEM_DELETE_FAILED', $filename), JLog::WARNING, 'jerror');
  187. return false;
  188. }
  189. }
  190. return true;
  191. }
  192. /**
  193. * Moves a file
  194. *
  195. * @param string $src The path to the source file
  196. * @param string $dest The path to the destination file
  197. * @param string $path An optional base path to prefix to the file names
  198. * @param boolean $use_streams True to use streams
  199. *
  200. * @return boolean True on success
  201. *
  202. * @since 11.1
  203. */
  204. public static function move($src, $dest, $path = '', $use_streams = false)
  205. {
  206. $pathObject = new JFilesystemWrapperPath;
  207. if ($path)
  208. {
  209. $src = $pathObject->clean($path . '/' . $src);
  210. $dest = $pathObject->clean($path . '/' . $dest);
  211. }
  212. // Check src path
  213. if (!is_readable($src))
  214. {
  215. JLog::add(JText::_('JLIB_FILESYSTEM_CANNOT_FIND_SOURCE_FILE'), JLog::WARNING, 'jerror');
  216. return false;
  217. }
  218. if ($use_streams)
  219. {
  220. $stream = JFactory::getStream();
  221. if (!$stream->move($src, $dest))
  222. {
  223. JLog::add(JText::sprintf('JLIB_FILESYSTEM_ERROR_JFILE_MOVE_STREAMS', $stream->getError()), JLog::WARNING, 'jerror');
  224. return false;
  225. }
  226. return true;
  227. }
  228. else
  229. {
  230. $FTPOptions = JClientHelper::getCredentials('ftp');
  231. if ($FTPOptions['enabled'] == 1)
  232. {
  233. // Connect the FTP client
  234. $ftp = JClientFtp::getInstance($FTPOptions['host'], $FTPOptions['port'], array(), $FTPOptions['user'], $FTPOptions['pass']);
  235. // Translate path for the FTP account
  236. $src = $pathObject->clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $src), '/');
  237. $dest = $pathObject->clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $dest), '/');
  238. // Use FTP rename to simulate move
  239. if (!$ftp->rename($src, $dest))
  240. {
  241. JLog::add(JText::_('JLIB_FILESYSTEM_ERROR_RENAME_FILE'), JLog::WARNING, 'jerror');
  242. return false;
  243. }
  244. }
  245. else
  246. {
  247. if (!@ rename($src, $dest))
  248. {
  249. JLog::add(JText::_('JLIB_FILESYSTEM_ERROR_RENAME_FILE'), JLog::WARNING, 'jerror');
  250. return false;
  251. }
  252. }
  253. return true;
  254. }
  255. }
  256. /**
  257. * Read the contents of a file
  258. *
  259. * @param string $filename The full file path
  260. * @param boolean $incpath Use include path
  261. * @param integer $amount Amount of file to read
  262. * @param integer $chunksize Size of chunks to read
  263. * @param integer $offset Offset of the file
  264. *
  265. * @return mixed Returns file contents or boolean False if failed
  266. *
  267. * @since 11.1
  268. * @deprecated 13.3 (Platform) & 4.0 (CMS) - Use the native file_get_contents() instead.
  269. */
  270. public static function read($filename, $incpath = false, $amount = 0, $chunksize = 8192, $offset = 0)
  271. {
  272. JLog::add(__METHOD__ . ' is deprecated. Use native file_get_contents() syntax.', JLog::WARNING, 'deprecated');
  273. $data = null;
  274. if ($amount && $chunksize > $amount)
  275. {
  276. $chunksize = $amount;
  277. }
  278. if (false === $fh = fopen($filename, 'rb', $incpath))
  279. {
  280. JLog::add(JText::sprintf('JLIB_FILESYSTEM_ERROR_READ_UNABLE_TO_OPEN_FILE', $filename), JLog::WARNING, 'jerror');
  281. return false;
  282. }
  283. clearstatcache();
  284. if ($offset)
  285. {
  286. fseek($fh, $offset);
  287. }
  288. if ($fsize = @ filesize($filename))
  289. {
  290. if ($amount && $fsize > $amount)
  291. {
  292. $data = fread($fh, $amount);
  293. }
  294. else
  295. {
  296. $data = fread($fh, $fsize);
  297. }
  298. }
  299. else
  300. {
  301. $data = '';
  302. /*
  303. * While it's:
  304. * 1: Not the end of the file AND
  305. * 2a: No Max Amount set OR
  306. * 2b: The length of the data is less than the max amount we want
  307. */
  308. while (!feof($fh) && (!$amount || strlen($data) < $amount))
  309. {
  310. $data .= fread($fh, $chunksize);
  311. }
  312. }
  313. fclose($fh);
  314. return $data;
  315. }
  316. /**
  317. * Write contents to a file
  318. *
  319. * @param string $file The full file path
  320. * @param string &$buffer The buffer to write
  321. * @param boolean $use_streams Use streams
  322. *
  323. * @return boolean True on success
  324. *
  325. * @since 11.1
  326. */
  327. public static function write($file, &$buffer, $use_streams = false)
  328. {
  329. @set_time_limit(ini_get('max_execution_time'));
  330. // If the destination directory doesn't exist we need to create it
  331. if (!file_exists(dirname($file)))
  332. {
  333. $folderObject = new JFilesystemWrapperFolder;
  334. if ($folderObject->create(dirname($file)) == false)
  335. {
  336. return false;
  337. }
  338. }
  339. if ($use_streams)
  340. {
  341. $stream = JFactory::getStream();
  342. // Beef up the chunk size to a meg
  343. $stream->set('chunksize', (1024 * 1024));
  344. if (!$stream->writeFile($file, $buffer))
  345. {
  346. JLog::add(JText::sprintf('JLIB_FILESYSTEM_ERROR_WRITE_STREAMS', $file, $stream->getError()), JLog::WARNING, 'jerror');
  347. return false;
  348. }
  349. return true;
  350. }
  351. else
  352. {
  353. $FTPOptions = JClientHelper::getCredentials('ftp');
  354. $pathObject = new JFilesystemWrapperPath;
  355. if ($FTPOptions['enabled'] == 1)
  356. {
  357. // Connect the FTP client
  358. $ftp = JClientFtp::getInstance($FTPOptions['host'], $FTPOptions['port'], array(), $FTPOptions['user'], $FTPOptions['pass']);
  359. // Translate path for the FTP account and use FTP write buffer to file
  360. $file = $pathObject->clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $file), '/');
  361. $ret = $ftp->write($file, $buffer);
  362. }
  363. else
  364. {
  365. $file = $pathObject->clean($file);
  366. $ret = is_int(file_put_contents($file, $buffer)) ? true : false;
  367. }
  368. return $ret;
  369. }
  370. }
  371. /**
  372. * Moves an uploaded file to a destination folder
  373. *
  374. * @param string $src The name of the php (temporary) uploaded file
  375. * @param string $dest The path (including filename) to move the uploaded file to
  376. * @param boolean $use_streams True to use streams
  377. * @param boolean $allow_unsafe Allow the upload of unsafe files
  378. * @param boolean $safeFileOptions Options to JFilterInput::isSafeFile
  379. *
  380. * @return boolean True on success
  381. *
  382. * @since 11.1
  383. */
  384. public static function upload($src, $dest, $use_streams = false, $allow_unsafe = false, $safeFileOptions = array())
  385. {
  386. if (!$allow_unsafe)
  387. {
  388. $descriptor = array(
  389. 'tmp_name' => $src,
  390. 'name' => basename($dest),
  391. 'type' => '',
  392. 'error' => '',
  393. 'size' => '',
  394. );
  395. $isSafe = JFilterInput::isSafeFile($descriptor, $safeFileOptions);
  396. if (!$isSafe)
  397. {
  398. JLog::add(JText::sprintf('JLIB_FILESYSTEM_ERROR_WARNFS_ERR03', $dest), JLog::WARNING, 'jerror');
  399. return false;
  400. }
  401. }
  402. // Ensure that the path is valid and clean
  403. $pathObject = new JFilesystemWrapperPath;
  404. $dest = $pathObject->clean($dest);
  405. // Create the destination directory if it does not exist
  406. $baseDir = dirname($dest);
  407. if (!file_exists($baseDir))
  408. {
  409. $folderObject = new JFilesystemWrapperFolder;
  410. $folderObject->create($baseDir);
  411. }
  412. if ($use_streams)
  413. {
  414. $stream = JFactory::getStream();
  415. if (!$stream->upload($src, $dest))
  416. {
  417. JLog::add(JText::sprintf('JLIB_FILESYSTEM_ERROR_UPLOAD', $stream->getError()), JLog::WARNING, 'jerror');
  418. return false;
  419. }
  420. return true;
  421. }
  422. else
  423. {
  424. $FTPOptions = JClientHelper::getCredentials('ftp');
  425. $ret = false;
  426. if ($FTPOptions['enabled'] == 1)
  427. {
  428. // Connect the FTP client
  429. $ftp = JClientFtp::getInstance($FTPOptions['host'], $FTPOptions['port'], array(), $FTPOptions['user'], $FTPOptions['pass']);
  430. // Translate path for the FTP account
  431. $dest = $pathObject->clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $dest), '/');
  432. // Copy the file to the destination directory
  433. if (is_uploaded_file($src) && $ftp->store($src, $dest))
  434. {
  435. unlink($src);
  436. $ret = true;
  437. }
  438. else
  439. {
  440. JLog::add(JText::_('JLIB_FILESYSTEM_ERROR_WARNFS_ERR02'), JLog::WARNING, 'jerror');
  441. }
  442. }
  443. else
  444. {
  445. if (is_writeable($baseDir) && move_uploaded_file($src, $dest))
  446. {
  447. // Short circuit to prevent file permission errors
  448. if ($pathObject->setPermissions($dest))
  449. {
  450. $ret = true;
  451. }
  452. else
  453. {
  454. JLog::add(JText::_('JLIB_FILESYSTEM_ERROR_WARNFS_ERR01'), JLog::WARNING, 'jerror');
  455. }
  456. }
  457. else
  458. {
  459. JLog::add(JText::_('JLIB_FILESYSTEM_ERROR_WARNFS_ERR02'), JLog::WARNING, 'jerror');
  460. }
  461. }
  462. return $ret;
  463. }
  464. }
  465. /**
  466. * Wrapper for the standard file_exists function
  467. *
  468. * @param string $file File path
  469. *
  470. * @return boolean True if path is a file
  471. *
  472. * @since 11.1
  473. */
  474. public static function exists($file)
  475. {
  476. $pathObject = new JFilesystemWrapperPath;
  477. return is_file($pathObject->clean($file));
  478. }
  479. /**
  480. * Returns the name, without any path.
  481. *
  482. * @param string $file File path
  483. *
  484. * @return string filename
  485. *
  486. * @since 11.1
  487. * @deprecated 13.3 (Platform) & 4.0 (CMS) - Use basename() instead.
  488. */
  489. public static function getName($file)
  490. {
  491. JLog::add(__METHOD__ . ' is deprecated. Use native basename() syntax.', JLog::WARNING, 'deprecated');
  492. // Convert back slashes to forward slashes
  493. $file = str_replace('\\', '/', $file);
  494. $slash = strrpos($file, '/');
  495. if ($slash !== false)
  496. {
  497. return substr($file, $slash + 1);
  498. }
  499. else
  500. {
  501. return $file;
  502. }
  503. }
  504. }