PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://gitlab.com/morganestes/wordpress-develop
PHP | 524 lines | 290 code | 80 blank | 154 comment | 62 complexity | 88f88550adcff611687114af933160a9 MD5 | raw file
  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. * @var ftp
  18. */
  19. public $ftp;
  20. /**
  21. * @param array $opt
  22. */
  23. public function __construct( $opt = '' ) {
  24. $this->method = 'ftpsockets';
  25. $this->errors = new WP_Error();
  26. // Check if possible to use ftp functions.
  27. if ( ! @include_once( ABSPATH . 'wp-admin/includes/class-ftp.php' ) ) {
  28. return;
  29. }
  30. $this->ftp = new ftp();
  31. if ( empty( $opt['port'] ) ) {
  32. $this->options['port'] = 21;
  33. } else {
  34. $this->options['port'] = (int) $opt['port'];
  35. }
  36. if ( empty( $opt['hostname'] ) ) {
  37. $this->errors->add( 'empty_hostname', __( 'FTP hostname is required' ) );
  38. } else {
  39. $this->options['hostname'] = $opt['hostname'];
  40. }
  41. // Check if the options provided are OK.
  42. if ( empty( $opt['username'] ) ) {
  43. $this->errors->add( 'empty_username', __( 'FTP username is required' ) );
  44. } else {
  45. $this->options['username'] = $opt['username'];
  46. }
  47. if ( empty( $opt['password'] ) ) {
  48. $this->errors->add( 'empty_password', __( 'FTP password is required' ) );
  49. } else {
  50. $this->options['password'] = $opt['password'];
  51. }
  52. }
  53. /**
  54. * @return bool
  55. */
  56. public function connect() {
  57. if ( ! $this->ftp ) {
  58. return false;
  59. }
  60. $this->ftp->setTimeout( FS_CONNECT_TIMEOUT );
  61. if ( ! $this->ftp->SetServer( $this->options['hostname'], $this->options['port'] ) ) {
  62. $this->errors->add(
  63. 'connect',
  64. /* translators: %s: hostname:port */
  65. sprintf(
  66. __( 'Failed to connect to FTP Server %s' ),
  67. $this->options['hostname'] . ':' . $this->options['port']
  68. )
  69. );
  70. return false;
  71. }
  72. if ( ! $this->ftp->connect() ) {
  73. $this->errors->add(
  74. 'connect',
  75. /* translators: %s: hostname:port */
  76. sprintf(
  77. __( 'Failed to connect to FTP Server %s' ),
  78. $this->options['hostname'] . ':' . $this->options['port']
  79. )
  80. );
  81. return false;
  82. }
  83. if ( ! $this->ftp->login( $this->options['username'], $this->options['password'] ) ) {
  84. $this->errors->add(
  85. 'auth',
  86. /* translators: %s: username */
  87. sprintf(
  88. __( 'Username/Password incorrect for %s' ),
  89. $this->options['username']
  90. )
  91. );
  92. return false;
  93. }
  94. $this->ftp->SetType( FTP_BINARY );
  95. $this->ftp->Passive( true );
  96. $this->ftp->setTimeout( FS_TIMEOUT );
  97. return true;
  98. }
  99. /**
  100. * Retrieves the file contents.
  101. *
  102. * @since 2.5.0
  103. *
  104. * @param string $file Filename.
  105. * @return string|false File contents on success, false if no temp file could be opened,
  106. * or if the file doesn't exist.
  107. */
  108. public function get_contents( $file ) {
  109. if ( ! $this->exists( $file ) ) {
  110. return false;
  111. }
  112. $temp = wp_tempnam( $file );
  113. if ( ! $temphandle = fopen( $temp, 'w+' ) ) {
  114. unlink( $temp );
  115. return false;
  116. }
  117. mbstring_binary_safe_encoding();
  118. if ( ! $this->ftp->fget( $temphandle, $file ) ) {
  119. fclose( $temphandle );
  120. unlink( $temp );
  121. reset_mbstring_encoding();
  122. return ''; // Blank document, File does exist, It's just blank.
  123. }
  124. reset_mbstring_encoding();
  125. fseek( $temphandle, 0 ); // Skip back to the start of the file being written to
  126. $contents = '';
  127. while ( ! feof( $temphandle ) ) {
  128. $contents .= fread( $temphandle, 8192 );
  129. }
  130. fclose( $temphandle );
  131. unlink( $temp );
  132. return $contents;
  133. }
  134. /**
  135. * @param string $file
  136. * @return array
  137. */
  138. public function get_contents_array( $file ) {
  139. return explode( "\n", $this->get_contents( $file ) );
  140. }
  141. /**
  142. * @param string $file
  143. * @param string $contents
  144. * @param int|bool $mode
  145. * @return bool
  146. */
  147. public function put_contents( $file, $contents, $mode = false ) {
  148. $temp = wp_tempnam( $file );
  149. if ( ! $temphandle = @fopen( $temp, 'w+' ) ) {
  150. unlink( $temp );
  151. return false;
  152. }
  153. // The FTP class uses string functions internally during file download/upload
  154. mbstring_binary_safe_encoding();
  155. $bytes_written = fwrite( $temphandle, $contents );
  156. if ( false === $bytes_written || $bytes_written != strlen( $contents ) ) {
  157. fclose( $temphandle );
  158. unlink( $temp );
  159. reset_mbstring_encoding();
  160. return false;
  161. }
  162. fseek( $temphandle, 0 ); // Skip back to the start of the file being written to
  163. $ret = $this->ftp->fput( $file, $temphandle );
  164. reset_mbstring_encoding();
  165. fclose( $temphandle );
  166. unlink( $temp );
  167. $this->chmod( $file, $mode );
  168. return $ret;
  169. }
  170. /**
  171. * @return string
  172. */
  173. public function cwd() {
  174. $cwd = $this->ftp->pwd();
  175. if ( $cwd ) {
  176. $cwd = trailingslashit( $cwd );
  177. }
  178. return $cwd;
  179. }
  180. /**
  181. * @param string $file
  182. * @return bool
  183. */
  184. public function chdir( $file ) {
  185. return $this->ftp->chdir( $file );
  186. }
  187. /**
  188. * @param string $file
  189. * @param int|bool $mode
  190. * @param bool $recursive
  191. * @return bool
  192. */
  193. public function chmod( $file, $mode = false, $recursive = false ) {
  194. if ( ! $mode ) {
  195. if ( $this->is_file( $file ) ) {
  196. $mode = FS_CHMOD_FILE;
  197. } elseif ( $this->is_dir( $file ) ) {
  198. $mode = FS_CHMOD_DIR;
  199. } else {
  200. return false;
  201. }
  202. }
  203. // chmod any sub-objects if recursive.
  204. if ( $recursive && $this->is_dir( $file ) ) {
  205. $filelist = $this->dirlist( $file );
  206. foreach ( (array) $filelist as $filename => $filemeta ) {
  207. $this->chmod( $file . '/' . $filename, $mode, $recursive );
  208. }
  209. }
  210. // chmod the file or directory
  211. return $this->ftp->chmod( $file, $mode );
  212. }
  213. /**
  214. * @param string $file
  215. * @return string
  216. */
  217. public function owner( $file ) {
  218. $dir = $this->dirlist( $file );
  219. return $dir[ $file ]['owner'];
  220. }
  221. /**
  222. * @param string $file
  223. * @return string
  224. */
  225. public function getchmod( $file ) {
  226. $dir = $this->dirlist( $file );
  227. return $dir[ $file ]['permsn'];
  228. }
  229. /**
  230. * @param string $file
  231. * @return string
  232. */
  233. public function group( $file ) {
  234. $dir = $this->dirlist( $file );
  235. return $dir[ $file ]['group'];
  236. }
  237. /**
  238. * @param string $source
  239. * @param string $destination
  240. * @param bool $overwrite
  241. * @param int|bool $mode
  242. * @return bool
  243. */
  244. public function copy( $source, $destination, $overwrite = false, $mode = false ) {
  245. if ( ! $overwrite && $this->exists( $destination ) ) {
  246. return false;
  247. }
  248. $content = $this->get_contents( $source );
  249. if ( false === $content ) {
  250. return false;
  251. }
  252. return $this->put_contents( $destination, $content, $mode );
  253. }
  254. /**
  255. * @param string $source
  256. * @param string $destination
  257. * @param bool $overwrite
  258. * @return bool
  259. */
  260. public function move( $source, $destination, $overwrite = false ) {
  261. return $this->ftp->rename( $source, $destination );
  262. }
  263. /**
  264. * @param string $file
  265. * @param bool $recursive
  266. * @param string $type
  267. * @return bool
  268. */
  269. public function delete( $file, $recursive = false, $type = false ) {
  270. if ( empty( $file ) ) {
  271. return false;
  272. }
  273. if ( 'f' == $type || $this->is_file( $file ) ) {
  274. return $this->ftp->delete( $file );
  275. }
  276. if ( ! $recursive ) {
  277. return $this->ftp->rmdir( $file );
  278. }
  279. return $this->ftp->mdel( $file );
  280. }
  281. /**
  282. * @param string $file
  283. * @return bool
  284. */
  285. public function exists( $file ) {
  286. $list = $this->ftp->nlist( $file );
  287. if ( empty( $list ) && $this->is_dir( $file ) ) {
  288. return true; // File is an empty directory.
  289. }
  290. return ! empty( $list ); //empty list = no file, so invert.
  291. // Return $this->ftp->is_exists($file); has issues with ABOR+426 responses on the ncFTPd server.
  292. }
  293. /**
  294. * @param string $file
  295. * @return bool
  296. */
  297. public function is_file( $file ) {
  298. if ( $this->is_dir( $file ) ) {
  299. return false;
  300. }
  301. if ( $this->exists( $file ) ) {
  302. return true;
  303. }
  304. return false;
  305. }
  306. /**
  307. * @param string $path
  308. * @return bool
  309. */
  310. public function is_dir( $path ) {
  311. $cwd = $this->cwd();
  312. if ( $this->chdir( $path ) ) {
  313. $this->chdir( $cwd );
  314. return true;
  315. }
  316. return false;
  317. }
  318. /**
  319. * @param string $file
  320. * @return bool
  321. */
  322. public function is_readable( $file ) {
  323. return true;
  324. }
  325. /**
  326. * @param string $file
  327. * @return bool
  328. */
  329. public function is_writable( $file ) {
  330. return true;
  331. }
  332. /**
  333. * @param string $file
  334. * @return bool
  335. */
  336. public function atime( $file ) {
  337. return false;
  338. }
  339. /**
  340. * @param string $file
  341. * @return int
  342. */
  343. public function mtime( $file ) {
  344. return $this->ftp->mdtm( $file );
  345. }
  346. /**
  347. * @param string $file
  348. * @return int
  349. */
  350. public function size( $file ) {
  351. return $this->ftp->filesize( $file );
  352. }
  353. /**
  354. * @param string $file
  355. * @param int $time
  356. * @param int $atime
  357. * @return bool
  358. */
  359. public function touch( $file, $time = 0, $atime = 0 ) {
  360. return false;
  361. }
  362. /**
  363. * @param string $path
  364. * @param mixed $chmod
  365. * @param mixed $chown
  366. * @param mixed $chgrp
  367. * @return bool
  368. */
  369. public function mkdir( $path, $chmod = false, $chown = false, $chgrp = false ) {
  370. $path = untrailingslashit( $path );
  371. if ( empty( $path ) ) {
  372. return false;
  373. }
  374. if ( ! $this->ftp->mkdir( $path ) ) {
  375. return false;
  376. }
  377. if ( ! $chmod ) {
  378. $chmod = FS_CHMOD_DIR;
  379. }
  380. $this->chmod( $path, $chmod );
  381. return true;
  382. }
  383. /**
  384. * @param string $path
  385. * @param bool $recursive
  386. * @return bool
  387. */
  388. public function rmdir( $path, $recursive = false ) {
  389. return $this->delete( $path, $recursive );
  390. }
  391. /**
  392. * @param string $path
  393. * @param bool $include_hidden
  394. * @param bool $recursive
  395. * @return bool|array
  396. */
  397. public function dirlist( $path = '.', $include_hidden = true, $recursive = false ) {
  398. if ( $this->is_file( $path ) ) {
  399. $limit_file = basename( $path );
  400. $path = dirname( $path ) . '/';
  401. } else {
  402. $limit_file = false;
  403. }
  404. mbstring_binary_safe_encoding();
  405. $list = $this->ftp->dirlist( $path );
  406. if ( empty( $list ) && ! $this->exists( $path ) ) {
  407. reset_mbstring_encoding();
  408. return false;
  409. }
  410. $ret = array();
  411. foreach ( $list as $struc ) {
  412. if ( '.' == $struc['name'] || '..' == $struc['name'] ) {
  413. continue;
  414. }
  415. if ( ! $include_hidden && '.' == $struc['name'][0] ) {
  416. continue;
  417. }
  418. if ( $limit_file && $struc['name'] != $limit_file ) {
  419. continue;
  420. }
  421. if ( 'd' == $struc['type'] ) {
  422. if ( $recursive ) {
  423. $struc['files'] = $this->dirlist( $path . '/' . $struc['name'], $include_hidden, $recursive );
  424. } else {
  425. $struc['files'] = array();
  426. }
  427. }
  428. // Replace symlinks formatted as "source -> target" with just the source name
  429. if ( $struc['islink'] ) {
  430. $struc['name'] = preg_replace( '/(\s*->\s*.*)$/', '', $struc['name'] );
  431. }
  432. // Add the Octal representation of the file permissions
  433. $struc['permsn'] = $this->getnumchmodfromh( $struc['perms'] );
  434. $ret[ $struc['name'] ] = $struc;
  435. }
  436. reset_mbstring_encoding();
  437. return $ret;
  438. }
  439. /**
  440. */
  441. public function __destruct() {
  442. $this->ftp->quit();
  443. }
  444. }