PageRenderTime 1020ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-admin/includes/class-wp-filesystem-ftpsockets.php

http://github.com/wordpress/wordpress
PHP | 662 lines | 292 code | 81 blank | 289 comment | 62 complexity | e0fe57ae31cfc4482cd2e5cb8d1d36da MD5 | raw file
Possible License(s): 0BSD
  1. <?php
  2. /**
  3. * WordPress FTP Sockets Filesystem.
  4. *
  5. * @package WordPress
  6. * @subpackage Filesystem
  7. */
  8. /**
  9. * WordPress Filesystem Class for implementing FTP Sockets.
  10. *
  11. * @since 2.5.0
  12. *
  13. * @see WP_Filesystem_Base
  14. */
  15. class WP_Filesystem_ftpsockets extends WP_Filesystem_Base {
  16. /**
  17. * @since 2.5.0
  18. * @var ftp
  19. */
  20. public $ftp;
  21. /**
  22. * Constructor.
  23. *
  24. * @since 2.5.0
  25. *
  26. * @param array $opt
  27. */
  28. public function __construct( $opt = '' ) {
  29. $this->method = 'ftpsockets';
  30. $this->errors = new WP_Error();
  31. // Check if possible to use ftp functions.
  32. if ( ! include_once ABSPATH . 'wp-admin/includes/class-ftp.php' ) {
  33. return;
  34. }
  35. $this->ftp = new ftp();
  36. if ( empty( $opt['port'] ) ) {
  37. $this->options['port'] = 21;
  38. } else {
  39. $this->options['port'] = (int) $opt['port'];
  40. }
  41. if ( empty( $opt['hostname'] ) ) {
  42. $this->errors->add( 'empty_hostname', __( 'FTP hostname is required' ) );
  43. } else {
  44. $this->options['hostname'] = $opt['hostname'];
  45. }
  46. // Check if the options provided are OK.
  47. if ( empty( $opt['username'] ) ) {
  48. $this->errors->add( 'empty_username', __( 'FTP username is required' ) );
  49. } else {
  50. $this->options['username'] = $opt['username'];
  51. }
  52. if ( empty( $opt['password'] ) ) {
  53. $this->errors->add( 'empty_password', __( 'FTP password is required' ) );
  54. } else {
  55. $this->options['password'] = $opt['password'];
  56. }
  57. }
  58. /**
  59. * Connects filesystem.
  60. *
  61. * @since 2.5.0
  62. *
  63. * @return bool True on success, false on failure.
  64. */
  65. public function connect() {
  66. if ( ! $this->ftp ) {
  67. return false;
  68. }
  69. $this->ftp->setTimeout( FS_CONNECT_TIMEOUT );
  70. if ( ! $this->ftp->SetServer( $this->options['hostname'], $this->options['port'] ) ) {
  71. $this->errors->add(
  72. 'connect',
  73. sprintf(
  74. /* translators: %s: hostname:port */
  75. __( 'Failed to connect to FTP Server %s' ),
  76. $this->options['hostname'] . ':' . $this->options['port']
  77. )
  78. );
  79. return false;
  80. }
  81. if ( ! $this->ftp->connect() ) {
  82. $this->errors->add(
  83. 'connect',
  84. sprintf(
  85. /* translators: %s: hostname:port */
  86. __( 'Failed to connect to FTP Server %s' ),
  87. $this->options['hostname'] . ':' . $this->options['port']
  88. )
  89. );
  90. return false;
  91. }
  92. if ( ! $this->ftp->login( $this->options['username'], $this->options['password'] ) ) {
  93. $this->errors->add(
  94. 'auth',
  95. sprintf(
  96. /* translators: %s: Username. */
  97. __( 'Username/Password incorrect for %s' ),
  98. $this->options['username']
  99. )
  100. );
  101. return false;
  102. }
  103. $this->ftp->SetType( FTP_BINARY );
  104. $this->ftp->Passive( true );
  105. $this->ftp->setTimeout( FS_TIMEOUT );
  106. return true;
  107. }
  108. /**
  109. * Reads entire file into a string.
  110. *
  111. * @since 2.5.0
  112. *
  113. * @param string $file Name of the file to read.
  114. * @return string|false Read data on success, false if no temporary file could be opened,
  115. * or if the file couldn't be retrieved.
  116. */
  117. public function get_contents( $file ) {
  118. if ( ! $this->exists( $file ) ) {
  119. return false;
  120. }
  121. $temp = wp_tempnam( $file );
  122. $temphandle = fopen( $temp, 'w+' );
  123. if ( ! $temphandle ) {
  124. unlink( $temp );
  125. return false;
  126. }
  127. mbstring_binary_safe_encoding();
  128. if ( ! $this->ftp->fget( $temphandle, $file ) ) {
  129. fclose( $temphandle );
  130. unlink( $temp );
  131. reset_mbstring_encoding();
  132. return ''; // Blank document. File does exist, it's just blank.
  133. }
  134. reset_mbstring_encoding();
  135. fseek( $temphandle, 0 ); // Skip back to the start of the file being written to.
  136. $contents = '';
  137. while ( ! feof( $temphandle ) ) {
  138. $contents .= fread( $temphandle, 8 * KB_IN_BYTES );
  139. }
  140. fclose( $temphandle );
  141. unlink( $temp );
  142. return $contents;
  143. }
  144. /**
  145. * Reads entire file into an array.
  146. *
  147. * @since 2.5.0
  148. *
  149. * @param string $file Path to the file.
  150. * @return array|false File contents in an array on success, false on failure.
  151. */
  152. public function get_contents_array( $file ) {
  153. return explode( "\n", $this->get_contents( $file ) );
  154. }
  155. /**
  156. * Writes a string to a file.
  157. *
  158. * @since 2.5.0
  159. *
  160. * @param string $file Remote path to the file where to write the data.
  161. * @param string $contents The data to write.
  162. * @param int|false $mode Optional. The file permissions as octal number, usually 0644.
  163. * Default false.
  164. * @return bool True on success, false on failure.
  165. */
  166. public function put_contents( $file, $contents, $mode = false ) {
  167. $temp = wp_tempnam( $file );
  168. $temphandle = @fopen( $temp, 'w+' );
  169. if ( ! $temphandle ) {
  170. unlink( $temp );
  171. return false;
  172. }
  173. // The FTP class uses string functions internally during file download/upload.
  174. mbstring_binary_safe_encoding();
  175. $bytes_written = fwrite( $temphandle, $contents );
  176. if ( false === $bytes_written || strlen( $contents ) != $bytes_written ) {
  177. fclose( $temphandle );
  178. unlink( $temp );
  179. reset_mbstring_encoding();
  180. return false;
  181. }
  182. fseek( $temphandle, 0 ); // Skip back to the start of the file being written to.
  183. $ret = $this->ftp->fput( $file, $temphandle );
  184. reset_mbstring_encoding();
  185. fclose( $temphandle );
  186. unlink( $temp );
  187. $this->chmod( $file, $mode );
  188. return $ret;
  189. }
  190. /**
  191. * Gets the current working directory.
  192. *
  193. * @since 2.5.0
  194. *
  195. * @return string|false The current working directory on success, false on failure.
  196. */
  197. public function cwd() {
  198. $cwd = $this->ftp->pwd();
  199. if ( $cwd ) {
  200. $cwd = trailingslashit( $cwd );
  201. }
  202. return $cwd;
  203. }
  204. /**
  205. * Changes current directory.
  206. *
  207. * @since 2.5.0
  208. *
  209. * @param string $dir The new current directory.
  210. * @return bool True on success, false on failure.
  211. */
  212. public function chdir( $dir ) {
  213. return $this->ftp->chdir( $dir );
  214. }
  215. /**
  216. * Changes filesystem permissions.
  217. *
  218. * @since 2.5.0
  219. *
  220. * @param string $file Path to the file.
  221. * @param int|false $mode Optional. The permissions as octal number, usually 0644 for files,
  222. * 0755 for directories. Default false.
  223. * @param bool $recursive Optional. If set to true, changes file permissions recursively.
  224. * Default false.
  225. * @return bool True on success, false on failure.
  226. */
  227. public function chmod( $file, $mode = false, $recursive = false ) {
  228. if ( ! $mode ) {
  229. if ( $this->is_file( $file ) ) {
  230. $mode = FS_CHMOD_FILE;
  231. } elseif ( $this->is_dir( $file ) ) {
  232. $mode = FS_CHMOD_DIR;
  233. } else {
  234. return false;
  235. }
  236. }
  237. // chmod any sub-objects if recursive.
  238. if ( $recursive && $this->is_dir( $file ) ) {
  239. $filelist = $this->dirlist( $file );
  240. foreach ( (array) $filelist as $filename => $filemeta ) {
  241. $this->chmod( $file . '/' . $filename, $mode, $recursive );
  242. }
  243. }
  244. // chmod the file or directory.
  245. return $this->ftp->chmod( $file, $mode );
  246. }
  247. /**
  248. * Gets the file owner.
  249. *
  250. * @since 2.5.0
  251. *
  252. * @param string $file Path to the file.
  253. * @return string|false Username of the owner on success, false on failure.
  254. */
  255. public function owner( $file ) {
  256. $dir = $this->dirlist( $file );
  257. return $dir[ $file ]['owner'];
  258. }
  259. /**
  260. * Gets the permissions of the specified file or filepath in their octal format.
  261. *
  262. * @since 2.5.0
  263. *
  264. * @param string $file Path to the file.
  265. * @return string Mode of the file (the last 3 digits).
  266. */
  267. public function getchmod( $file ) {
  268. $dir = $this->dirlist( $file );
  269. return $dir[ $file ]['permsn'];
  270. }
  271. /**
  272. * Gets the file's group.
  273. *
  274. * @since 2.5.0
  275. *
  276. * @param string $file Path to the file.
  277. * @return string|false The group on success, false on failure.
  278. */
  279. public function group( $file ) {
  280. $dir = $this->dirlist( $file );
  281. return $dir[ $file ]['group'];
  282. }
  283. /**
  284. * Copies a file.
  285. *
  286. * @since 2.5.0
  287. *
  288. * @param string $source Path to the source file.
  289. * @param string $destination Path to the destination file.
  290. * @param bool $overwrite Optional. Whether to overwrite the destination file if it exists.
  291. * Default false.
  292. * @param int|false $mode Optional. The permissions as octal number, usually 0644 for files,
  293. * 0755 for dirs. Default false.
  294. * @return bool True on success, false on failure.
  295. */
  296. public function copy( $source, $destination, $overwrite = false, $mode = false ) {
  297. if ( ! $overwrite && $this->exists( $destination ) ) {
  298. return false;
  299. }
  300. $content = $this->get_contents( $source );
  301. if ( false === $content ) {
  302. return false;
  303. }
  304. return $this->put_contents( $destination, $content, $mode );
  305. }
  306. /**
  307. * Moves a file.
  308. *
  309. * @since 2.5.0
  310. *
  311. * @param string $source Path to the source file.
  312. * @param string $destination Path to the destination file.
  313. * @param bool $overwrite Optional. Whether to overwrite the destination file if it exists.
  314. * Default false.
  315. * @return bool True on success, false on failure.
  316. */
  317. public function move( $source, $destination, $overwrite = false ) {
  318. return $this->ftp->rename( $source, $destination );
  319. }
  320. /**
  321. * Deletes a file or directory.
  322. *
  323. * @since 2.5.0
  324. *
  325. * @param string $file Path to the file or directory.
  326. * @param bool $recursive Optional. If set to true, deletes files and folders recursively.
  327. * Default false.
  328. * @param string|false $type Type of resource. 'f' for file, 'd' for directory.
  329. * Default false.
  330. * @return bool True on success, false on failure.
  331. */
  332. public function delete( $file, $recursive = false, $type = false ) {
  333. if ( empty( $file ) ) {
  334. return false;
  335. }
  336. if ( 'f' == $type || $this->is_file( $file ) ) {
  337. return $this->ftp->delete( $file );
  338. }
  339. if ( ! $recursive ) {
  340. return $this->ftp->rmdir( $file );
  341. }
  342. return $this->ftp->mdel( $file );
  343. }
  344. /**
  345. * Checks if a file or directory exists.
  346. *
  347. * @since 2.5.0
  348. *
  349. * @param string $file Path to file or directory.
  350. * @return bool Whether $file exists or not.
  351. */
  352. public function exists( $file ) {
  353. $list = $this->ftp->nlist( $file );
  354. if ( empty( $list ) && $this->is_dir( $file ) ) {
  355. return true; // File is an empty directory.
  356. }
  357. return ! empty( $list ); // Empty list = no file, so invert.
  358. // Return $this->ftp->is_exists($file); has issues with ABOR+426 responses on the ncFTPd server.
  359. }
  360. /**
  361. * Checks if resource is a file.
  362. *
  363. * @since 2.5.0
  364. *
  365. * @param string $file File path.
  366. * @return bool Whether $file is a file.
  367. */
  368. public function is_file( $file ) {
  369. if ( $this->is_dir( $file ) ) {
  370. return false;
  371. }
  372. if ( $this->exists( $file ) ) {
  373. return true;
  374. }
  375. return false;
  376. }
  377. /**
  378. * Checks if resource is a directory.
  379. *
  380. * @since 2.5.0
  381. *
  382. * @param string $path Directory path.
  383. * @return bool Whether $path is a directory.
  384. */
  385. public function is_dir( $path ) {
  386. $cwd = $this->cwd();
  387. if ( $this->chdir( $path ) ) {
  388. $this->chdir( $cwd );
  389. return true;
  390. }
  391. return false;
  392. }
  393. /**
  394. * Checks if a file is readable.
  395. *
  396. * @since 2.5.0
  397. *
  398. * @param string $file Path to file.
  399. * @return bool Whether $file is readable.
  400. */
  401. public function is_readable( $file ) {
  402. return true;
  403. }
  404. /**
  405. * Checks if a file or directory is writable.
  406. *
  407. * @since 2.5.0
  408. *
  409. * @param string $file Path to file or directory.
  410. * @return bool Whether $file is writable.
  411. */
  412. public function is_writable( $file ) {
  413. return true;
  414. }
  415. /**
  416. * Gets the file's last access time.
  417. *
  418. * @since 2.5.0
  419. *
  420. * @param string $file Path to file.
  421. * @return int|false Unix timestamp representing last access time, false on failure.
  422. */
  423. public function atime( $file ) {
  424. return false;
  425. }
  426. /**
  427. * Gets the file modification time.
  428. *
  429. * @since 2.5.0
  430. *
  431. * @param string $file Path to file.
  432. * @return int|false Unix timestamp representing modification time, false on failure.
  433. */
  434. public function mtime( $file ) {
  435. return $this->ftp->mdtm( $file );
  436. }
  437. /**
  438. * Gets the file size (in bytes).
  439. *
  440. * @since 2.5.0
  441. *
  442. * @param string $file Path to file.
  443. * @return int|false Size of the file in bytes on success, false on failure.
  444. */
  445. public function size( $file ) {
  446. return $this->ftp->filesize( $file );
  447. }
  448. /**
  449. * Sets the access and modification times of a file.
  450. *
  451. * Note: If $file doesn't exist, it will be created.
  452. *
  453. * @since 2.5.0
  454. *
  455. * @param string $file Path to file.
  456. * @param int $time Optional. Modified time to set for file.
  457. * Default 0.
  458. * @param int $atime Optional. Access time to set for file.
  459. * Default 0.
  460. * @return bool True on success, false on failure.
  461. */
  462. public function touch( $file, $time = 0, $atime = 0 ) {
  463. return false;
  464. }
  465. /**
  466. * Creates a directory.
  467. *
  468. * @since 2.5.0
  469. *
  470. * @param string $path Path for new directory.
  471. * @param int|false $chmod Optional. The permissions as octal number (or false to skip chmod).
  472. * Default false.
  473. * @param string|int $chown Optional. A user name or number (or false to skip chown).
  474. * Default false.
  475. * @param string|int $chgrp Optional. A group name or number (or false to skip chgrp).
  476. * Default false.
  477. * @return bool True on success, false on failure.
  478. */
  479. public function mkdir( $path, $chmod = false, $chown = false, $chgrp = false ) {
  480. $path = untrailingslashit( $path );
  481. if ( empty( $path ) ) {
  482. return false;
  483. }
  484. if ( ! $this->ftp->mkdir( $path ) ) {
  485. return false;
  486. }
  487. if ( ! $chmod ) {
  488. $chmod = FS_CHMOD_DIR;
  489. }
  490. $this->chmod( $path, $chmod );
  491. return true;
  492. }
  493. /**
  494. * Deletes a directory.
  495. *
  496. * @since 2.5.0
  497. *
  498. * @param string $path Path to directory.
  499. * @param bool $recursive Optional. Whether to recursively remove files/directories.
  500. * Default false.
  501. * @return bool True on success, false on failure.
  502. */
  503. public function rmdir( $path, $recursive = false ) {
  504. return $this->delete( $path, $recursive );
  505. }
  506. /**
  507. * Gets details for files in a directory or a specific file.
  508. *
  509. * @since 2.5.0
  510. *
  511. * @param string $path Path to directory or file.
  512. * @param bool $include_hidden Optional. Whether to include details of hidden ("." prefixed) files.
  513. * Default true.
  514. * @param bool $recursive Optional. Whether to recursively include file details in nested directories.
  515. * Default false.
  516. * @return array|false {
  517. * Array of files. False if unable to list directory contents.
  518. *
  519. * @type string $name Name of the file or directory.
  520. * @type string $perms *nix representation of permissions.
  521. * @type int $permsn Octal representation of permissions.
  522. * @type string $owner Owner name or ID.
  523. * @type int $size Size of file in bytes.
  524. * @type int $lastmodunix Last modified unix timestamp.
  525. * @type mixed $lastmod Last modified month (3 letter) and day (without leading 0).
  526. * @type int $time Last modified time.
  527. * @type string $type Type of resource. 'f' for file, 'd' for directory.
  528. * @type mixed $files If a directory and $recursive is true, contains another array of files.
  529. * }
  530. */
  531. public function dirlist( $path = '.', $include_hidden = true, $recursive = false ) {
  532. if ( $this->is_file( $path ) ) {
  533. $limit_file = basename( $path );
  534. $path = dirname( $path ) . '/';
  535. } else {
  536. $limit_file = false;
  537. }
  538. mbstring_binary_safe_encoding();
  539. $list = $this->ftp->dirlist( $path );
  540. if ( empty( $list ) && ! $this->exists( $path ) ) {
  541. reset_mbstring_encoding();
  542. return false;
  543. }
  544. $ret = array();
  545. foreach ( $list as $struc ) {
  546. if ( '.' == $struc['name'] || '..' == $struc['name'] ) {
  547. continue;
  548. }
  549. if ( ! $include_hidden && '.' == $struc['name'][0] ) {
  550. continue;
  551. }
  552. if ( $limit_file && $struc['name'] != $limit_file ) {
  553. continue;
  554. }
  555. if ( 'd' == $struc['type'] ) {
  556. if ( $recursive ) {
  557. $struc['files'] = $this->dirlist( $path . '/' . $struc['name'], $include_hidden, $recursive );
  558. } else {
  559. $struc['files'] = array();
  560. }
  561. }
  562. // Replace symlinks formatted as "source -> target" with just the source name.
  563. if ( $struc['islink'] ) {
  564. $struc['name'] = preg_replace( '/(\s*->\s*.*)$/', '', $struc['name'] );
  565. }
  566. // Add the octal representation of the file permissions.
  567. $struc['permsn'] = $this->getnumchmodfromh( $struc['perms'] );
  568. $ret[ $struc['name'] ] = $struc;
  569. }
  570. reset_mbstring_encoding();
  571. return $ret;
  572. }
  573. /**
  574. * Destructor.
  575. *
  576. * @since 2.5.0
  577. */
  578. public function __destruct() {
  579. $this->ftp->quit();
  580. }
  581. }