home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-admin / includes / class-wp-filesystem-ssh2.php < prev    next >
Encoding:
PHP Script  |  2017-07-26  |  14.6 KB  |  600 lines

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