PageRenderTime 39ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://gitlab.com/WPonEB/WPonEB
PHP | 599 lines | 303 code | 62 blank | 234 comment | 72 complexity | 34f24454a4e9892efb483e64407d7c83 MD5 | raw file
  1. <?php
  2. /**
  3. * WordPress Filesystem Class for implementing SSH2
  4. *
  5. * To use this class you must follow these steps for PHP 5.2.6+
  6. *
  7. * @contrib http://kevin.vanzonneveld.net/techblog/article/make_ssh_connections_with_php/ - Installation Notes
  8. *
  9. * Complie libssh2 (Note: Only 0.14 is officaly working with PHP 5.2.6+ right now, But many users have found the latest versions work)
  10. *
  11. * cd /usr/src
  12. * wget http://surfnet.dl.sourceforge.net/sourceforge/libssh2/libssh2-0.14.tar.gz
  13. * tar -zxvf libssh2-0.14.tar.gz
  14. * cd libssh2-0.14/
  15. * ./configure
  16. * make all install
  17. *
  18. * Note: Do not leave the directory yet!
  19. *
  20. * Enter: pecl install -f ssh2
  21. *
  22. * Copy the ssh.so file it creates to your PHP Module Directory.
  23. * Open up your PHP.INI file and look for where extensions are placed.
  24. * Add in your PHP.ini file: extension=ssh2.so
  25. *
  26. * Restart Apache!
  27. * Check phpinfo() streams to confirm that: ssh2.shell, ssh2.exec, ssh2.tunnel, ssh2.scp, ssh2.sftp exist.
  28. *
  29. * Note: as of WordPress 2.8, This utilises the PHP5+ function 'stream_get_contents'
  30. *
  31. * @since 2.7.0
  32. *
  33. * @package WordPress
  34. * @subpackage Filesystem
  35. */
  36. class WP_Filesystem_SSH2 extends WP_Filesystem_Base {
  37. /**
  38. */
  39. public $link = false;
  40. /**
  41. * @var resource
  42. */
  43. public $sftp_link;
  44. public $keys = false;
  45. /**
  46. *
  47. * @param array $opt
  48. */
  49. public function __construct( $opt = '' ) {
  50. $this->method = 'ssh2';
  51. $this->errors = new WP_Error();
  52. //Check if possible to use ssh2 functions.
  53. if ( ! extension_loaded('ssh2') ) {
  54. $this->errors->add('no_ssh2_ext', __('The ssh2 PHP extension is not available'));
  55. return;
  56. }
  57. if ( !function_exists('stream_get_contents') ) {
  58. $this->errors->add(
  59. 'ssh2_php_requirement',
  60. sprintf(
  61. /* translators: %s: stream_get_contents() */
  62. __( 'The ssh2 PHP extension is available, however, we require the PHP5 function %s' ),
  63. '<code>stream_get_contents()</code>'
  64. )
  65. );
  66. return;
  67. }
  68. // Set defaults:
  69. if ( empty($opt['port']) )
  70. $this->options['port'] = 22;
  71. else
  72. $this->options['port'] = $opt['port'];
  73. if ( empty($opt['hostname']) )
  74. $this->errors->add('empty_hostname', __('SSH2 hostname is required'));
  75. else
  76. $this->options['hostname'] = $opt['hostname'];
  77. // Check if the options provided are OK.
  78. if ( !empty ($opt['public_key']) && !empty ($opt['private_key']) ) {
  79. $this->options['public_key'] = $opt['public_key'];
  80. $this->options['private_key'] = $opt['private_key'];
  81. $this->options['hostkey'] = array('hostkey' => 'ssh-rsa');
  82. $this->keys = true;
  83. } elseif ( empty ($opt['username']) ) {
  84. $this->errors->add('empty_username', __('SSH2 username is required'));
  85. }
  86. if ( !empty($opt['username']) )
  87. $this->options['username'] = $opt['username'];
  88. if ( empty ($opt['password']) ) {
  89. // Password can be blank if we are using keys.
  90. if ( !$this->keys )
  91. $this->errors->add('empty_password', __('SSH2 password is required'));
  92. } else {
  93. $this->options['password'] = $opt['password'];
  94. }
  95. }
  96. /**
  97. *
  98. * @return bool
  99. */
  100. public function connect() {
  101. if ( ! $this->keys ) {
  102. $this->link = @ssh2_connect($this->options['hostname'], $this->options['port']);
  103. } else {
  104. $this->link = @ssh2_connect($this->options['hostname'], $this->options['port'], $this->options['hostkey']);
  105. }
  106. if ( ! $this->link ) {
  107. $this->errors->add( 'connect',
  108. /* translators: %s: hostname:port */
  109. sprintf( __( 'Failed to connect to SSH2 Server %s' ),
  110. $this->options['hostname'] . ':' . $this->options['port']
  111. )
  112. );
  113. return false;
  114. }
  115. if ( !$this->keys ) {
  116. if ( ! @ssh2_auth_password($this->link, $this->options['username'], $this->options['password']) ) {
  117. $this->errors->add( 'auth',
  118. /* translators: %s: username */
  119. sprintf( __( 'Username/Password incorrect for %s' ),
  120. $this->options['username']
  121. )
  122. );
  123. return false;
  124. }
  125. } else {
  126. if ( ! @ssh2_auth_pubkey_file($this->link, $this->options['username'], $this->options['public_key'], $this->options['private_key'], $this->options['password'] ) ) {
  127. $this->errors->add( 'auth',
  128. /* translators: %s: username */
  129. sprintf( __( 'Public and Private keys incorrect for %s' ),
  130. $this->options['username']
  131. )
  132. );
  133. return false;
  134. }
  135. }
  136. $this->sftp_link = ssh2_sftp( $this->link );
  137. if ( ! $this->sftp_link ) {
  138. $this->errors->add( 'connect',
  139. /* translators: %s: hostname:port */
  140. sprintf( __( 'Failed to initialize a SFTP subsystem session with the SSH2 Server %s' ),
  141. $this->options['hostname'] . ':' . $this->options['port']
  142. )
  143. );
  144. return false;
  145. }
  146. return true;
  147. }
  148. /**
  149. * Gets the ssh2.sftp PHP stream wrapper path to open for the given file.
  150. *
  151. * This method also works around a PHP bug where the root directory (/) cannot
  152. * be opened by PHP functions, causing a false failure. In order to work around
  153. * this, the path is converted to /./ which is semantically the same as /
  154. * See https://bugs.php.net/bug.php?id=64169 for more details.
  155. *
  156. *
  157. * @since 4.4.0
  158. *
  159. * @param string $path The File/Directory path on the remote server to return
  160. * @return string The ssh2.sftp:// wrapped path to use.
  161. */
  162. public function sftp_path( $path ) {
  163. if ( '/' === $path ) {
  164. $path = '/./';
  165. }
  166. return 'ssh2.sftp://' . $this->sftp_link . '/' . ltrim( $path, '/' );
  167. }
  168. /**
  169. *
  170. * @param string $command
  171. * @param bool $returnbool
  172. * @return bool|string True on success, false on failure. String if the command was executed, `$returnbool`
  173. * is false (default), and data from the resulting stream was retrieved.
  174. */
  175. public function run_command( $command, $returnbool = false ) {
  176. if ( ! $this->link )
  177. return false;
  178. if ( ! ($stream = ssh2_exec($this->link, $command)) ) {
  179. $this->errors->add( 'command',
  180. /* translators: %s: command */
  181. sprintf( __( 'Unable to perform command: %s'),
  182. $command
  183. )
  184. );
  185. } else {
  186. stream_set_blocking( $stream, true );
  187. stream_set_timeout( $stream, FS_TIMEOUT );
  188. $data = stream_get_contents( $stream );
  189. fclose( $stream );
  190. if ( $returnbool )
  191. return ( $data === false ) ? false : '' != trim($data);
  192. else
  193. return $data;
  194. }
  195. return false;
  196. }
  197. /**
  198. *
  199. * @param string $file
  200. * @return string|false
  201. */
  202. public function get_contents( $file ) {
  203. return file_get_contents( $this->sftp_path( $file ) );
  204. }
  205. /**
  206. *
  207. * @param string $file
  208. * @return array
  209. */
  210. public function get_contents_array($file) {
  211. return file( $this->sftp_path( $file ) );
  212. }
  213. /**
  214. *
  215. * @param string $file
  216. * @param string $contents
  217. * @param bool|int $mode
  218. * @return bool
  219. */
  220. public function put_contents($file, $contents, $mode = false ) {
  221. $ret = file_put_contents( $this->sftp_path( $file ), $contents );
  222. if ( $ret !== strlen( $contents ) )
  223. return false;
  224. $this->chmod($file, $mode);
  225. return true;
  226. }
  227. /**
  228. *
  229. * @return bool
  230. */
  231. public function cwd() {
  232. $cwd = ssh2_sftp_realpath( $this->sftp_link, '.' );
  233. if ( $cwd ) {
  234. $cwd = trailingslashit( trim( $cwd ) );
  235. }
  236. return $cwd;
  237. }
  238. /**
  239. *
  240. * @param string $dir
  241. * @return bool|string
  242. */
  243. public function chdir($dir) {
  244. return $this->run_command('cd ' . $dir, true);
  245. }
  246. /**
  247. *
  248. * @param string $file
  249. * @param string $group
  250. * @param bool $recursive
  251. *
  252. * @return bool
  253. */
  254. public function chgrp($file, $group, $recursive = false ) {
  255. if ( ! $this->exists($file) )
  256. return false;
  257. if ( ! $recursive || ! $this->is_dir($file) )
  258. return $this->run_command(sprintf('chgrp %s %s', escapeshellarg($group), escapeshellarg($file)), true);
  259. return $this->run_command(sprintf('chgrp -R %s %s', escapeshellarg($group), escapeshellarg($file)), true);
  260. }
  261. /**
  262. *
  263. * @param string $file
  264. * @param int $mode
  265. * @param bool $recursive
  266. * @return bool|string
  267. */
  268. public function chmod($file, $mode = false, $recursive = false) {
  269. if ( ! $this->exists($file) )
  270. return false;
  271. if ( ! $mode ) {
  272. if ( $this->is_file($file) )
  273. $mode = FS_CHMOD_FILE;
  274. elseif ( $this->is_dir($file) )
  275. $mode = FS_CHMOD_DIR;
  276. else
  277. return false;
  278. }
  279. if ( ! $recursive || ! $this->is_dir($file) )
  280. return $this->run_command(sprintf('chmod %o %s', $mode, escapeshellarg($file)), true);
  281. return $this->run_command(sprintf('chmod -R %o %s', $mode, escapeshellarg($file)), true);
  282. }
  283. /**
  284. * Change the ownership of a file / folder.
  285. *
  286. *
  287. * @param string $file Path to the file.
  288. * @param string|int $owner A user name or number.
  289. * @param bool $recursive Optional. If set True changes file owner recursivly. Default False.
  290. * @return bool True on success or false on failure.
  291. */
  292. public function chown( $file, $owner, $recursive = false ) {
  293. if ( ! $this->exists($file) )
  294. return false;
  295. if ( ! $recursive || ! $this->is_dir($file) )
  296. return $this->run_command(sprintf('chown %s %s', escapeshellarg($owner), escapeshellarg($file)), true);
  297. return $this->run_command(sprintf('chown -R %s %s', escapeshellarg($owner), escapeshellarg($file)), true);
  298. }
  299. /**
  300. *
  301. * @param string $file
  302. * @return string|false
  303. */
  304. public function owner($file) {
  305. $owneruid = @fileowner( $this->sftp_path( $file ) );
  306. if ( ! $owneruid )
  307. return false;
  308. if ( ! function_exists('posix_getpwuid') )
  309. return $owneruid;
  310. $ownerarray = posix_getpwuid($owneruid);
  311. return $ownerarray['name'];
  312. }
  313. /**
  314. *
  315. * @param string $file
  316. * @return string
  317. */
  318. public function getchmod($file) {
  319. return substr( decoct( @fileperms( $this->sftp_path( $file ) ) ), -3 );
  320. }
  321. /**
  322. *
  323. * @param string $file
  324. * @return string|false
  325. */
  326. public function group($file) {
  327. $gid = @filegroup( $this->sftp_path( $file ) );
  328. if ( ! $gid )
  329. return false;
  330. if ( ! function_exists('posix_getgrgid') )
  331. return $gid;
  332. $grouparray = posix_getgrgid($gid);
  333. return $grouparray['name'];
  334. }
  335. /**
  336. *
  337. * @param string $source
  338. * @param string $destination
  339. * @param bool $overwrite
  340. * @param int|bool $mode
  341. * @return bool
  342. */
  343. public function copy($source, $destination, $overwrite = false, $mode = false) {
  344. if ( ! $overwrite && $this->exists($destination) )
  345. return false;
  346. $content = $this->get_contents($source);
  347. if ( false === $content)
  348. return false;
  349. return $this->put_contents($destination, $content, $mode);
  350. }
  351. /**
  352. *
  353. * @param string $source
  354. * @param string $destination
  355. * @param bool $overwrite
  356. * @return bool
  357. */
  358. public function move($source, $destination, $overwrite = false) {
  359. return @ssh2_sftp_rename( $this->sftp_link, $source, $destination );
  360. }
  361. /**
  362. *
  363. * @param string $file
  364. * @param bool $recursive
  365. * @param string|bool $type
  366. * @return bool
  367. */
  368. public function delete($file, $recursive = false, $type = false) {
  369. if ( 'f' == $type || $this->is_file($file) )
  370. return ssh2_sftp_unlink($this->sftp_link, $file);
  371. if ( ! $recursive )
  372. return ssh2_sftp_rmdir($this->sftp_link, $file);
  373. $filelist = $this->dirlist($file);
  374. if ( is_array($filelist) ) {
  375. foreach ( $filelist as $filename => $fileinfo) {
  376. $this->delete($file . '/' . $filename, $recursive, $fileinfo['type']);
  377. }
  378. }
  379. return ssh2_sftp_rmdir($this->sftp_link, $file);
  380. }
  381. /**
  382. *
  383. * @param string $file
  384. * @return bool
  385. */
  386. public function exists($file) {
  387. return file_exists( $this->sftp_path( $file ) );
  388. }
  389. /**
  390. *
  391. * @param string $file
  392. * @return bool
  393. */
  394. public function is_file($file) {
  395. return is_file( $this->sftp_path( $file ) );
  396. }
  397. /**
  398. *
  399. * @param string $path
  400. * @return bool
  401. */
  402. public function is_dir($path) {
  403. return is_dir( $this->sftp_path( $path ) );
  404. }
  405. /**
  406. *
  407. * @param string $file
  408. * @return bool
  409. */
  410. public function is_readable($file) {
  411. return is_readable( $this->sftp_path( $file ) );
  412. }
  413. /**
  414. *
  415. * @param string $file
  416. * @return bool
  417. */
  418. public function is_writable($file) {
  419. // PHP will base it's writable checks on system_user === file_owner, not ssh_user === file_owner
  420. return true;
  421. }
  422. /**
  423. *
  424. * @param string $file
  425. * @return int
  426. */
  427. public function atime($file) {
  428. return fileatime( $this->sftp_path( $file ) );
  429. }
  430. /**
  431. *
  432. * @param string $file
  433. * @return int
  434. */
  435. public function mtime($file) {
  436. return filemtime( $this->sftp_path( $file ) );
  437. }
  438. /**
  439. *
  440. * @param string $file
  441. * @return int
  442. */
  443. public function size($file) {
  444. return filesize( $this->sftp_path( $file ) );
  445. }
  446. /**
  447. *
  448. * @param string $file
  449. * @param int $time
  450. * @param int $atime
  451. */
  452. public function touch($file, $time = 0, $atime = 0) {
  453. //Not implemented.
  454. }
  455. /**
  456. *
  457. * @param string $path
  458. * @param mixed $chmod
  459. * @param mixed $chown
  460. * @param mixed $chgrp
  461. * @return bool
  462. */
  463. public function mkdir($path, $chmod = false, $chown = false, $chgrp = false) {
  464. $path = untrailingslashit($path);
  465. if ( empty($path) )
  466. return false;
  467. if ( ! $chmod )
  468. $chmod = FS_CHMOD_DIR;
  469. if ( ! ssh2_sftp_mkdir($this->sftp_link, $path, $chmod, true) )
  470. return false;
  471. if ( $chown )
  472. $this->chown($path, $chown);
  473. if ( $chgrp )
  474. $this->chgrp($path, $chgrp);
  475. return true;
  476. }
  477. /**
  478. *
  479. * @param string $path
  480. * @param bool $recursive
  481. * @return bool
  482. */
  483. public function rmdir($path, $recursive = false) {
  484. return $this->delete($path, $recursive);
  485. }
  486. /**
  487. *
  488. * @param string $path
  489. * @param bool $include_hidden
  490. * @param bool $recursive
  491. * @return bool|array
  492. */
  493. public function dirlist($path, $include_hidden = true, $recursive = false) {
  494. if ( $this->is_file($path) ) {
  495. $limit_file = basename($path);
  496. $path = dirname($path);
  497. } else {
  498. $limit_file = false;
  499. }
  500. if ( ! $this->is_dir($path) )
  501. return false;
  502. $ret = array();
  503. $dir = @dir( $this->sftp_path( $path ) );
  504. if ( ! $dir )
  505. return false;
  506. while (false !== ($entry = $dir->read()) ) {
  507. $struc = array();
  508. $struc['name'] = $entry;
  509. if ( '.' == $struc['name'] || '..' == $struc['name'] )
  510. continue; //Do not care about these folders.
  511. if ( ! $include_hidden && '.' == $struc['name'][0] )
  512. continue;
  513. if ( $limit_file && $struc['name'] != $limit_file )
  514. continue;
  515. $struc['perms'] = $this->gethchmod($path.'/'.$entry);
  516. $struc['permsn'] = $this->getnumchmodfromh($struc['perms']);
  517. $struc['number'] = false;
  518. $struc['owner'] = $this->owner($path.'/'.$entry);
  519. $struc['group'] = $this->group($path.'/'.$entry);
  520. $struc['size'] = $this->size($path.'/'.$entry);
  521. $struc['lastmodunix']= $this->mtime($path.'/'.$entry);
  522. $struc['lastmod'] = date('M j',$struc['lastmodunix']);
  523. $struc['time'] = date('h:i:s',$struc['lastmodunix']);
  524. $struc['type'] = $this->is_dir($path.'/'.$entry) ? 'd' : 'f';
  525. if ( 'd' == $struc['type'] ) {
  526. if ( $recursive )
  527. $struc['files'] = $this->dirlist($path . '/' . $struc['name'], $include_hidden, $recursive);
  528. else
  529. $struc['files'] = array();
  530. }
  531. $ret[ $struc['name'] ] = $struc;
  532. }
  533. $dir->close();
  534. unset($dir);
  535. return $ret;
  536. }
  537. }