vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php line 441

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pure-PHP implementation of SFTP.
  4.  *
  5.  * PHP version 5
  6.  *
  7.  * Currently only supports SFTPv2 and v3, which, according to wikipedia.org, "is the most widely used version,
  8.  * implemented by the popular OpenSSH SFTP server".  If you want SFTPv4/5/6 support, provide me with access
  9.  * to an SFTPv4/5/6 server.
  10.  *
  11.  * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
  12.  *
  13.  * Here's a short example of how to use this library:
  14.  * <code>
  15.  * <?php
  16.  *    include 'vendor/autoload.php';
  17.  *
  18.  *    $sftp = new \phpseclib3\Net\SFTP('www.domain.tld');
  19.  *    if (!$sftp->login('username', 'password')) {
  20.  *        exit('Login Failed');
  21.  *    }
  22.  *
  23.  *    echo $sftp->pwd() . "\r\n";
  24.  *    $sftp->put('filename.ext', 'hello, world!');
  25.  *    print_r($sftp->nlist());
  26.  * ?>
  27.  * </code>
  28.  *
  29.  * @category  Net
  30.  * @package   SFTP
  31.  * @author    Jim Wigginton <terrafrost@php.net>
  32.  * @copyright 2009 Jim Wigginton
  33.  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  34.  * @link      http://phpseclib.sourceforge.net
  35.  */
  36. namespace phpseclib3\Net;
  37. use phpseclib3\Exception\FileNotFoundException;
  38. use phpseclib3\Common\Functions\Strings;
  39. use phpseclib3\Crypt\Common\AsymmetricKey;
  40. use phpseclib3\System\SSH\Agent;
  41. /**
  42.  * Pure-PHP implementations of SFTP.
  43.  *
  44.  * @package SFTP
  45.  * @author  Jim Wigginton <terrafrost@php.net>
  46.  * @access  public
  47.  */
  48. class SFTP extends SSH2
  49. {
  50.     /**
  51.      * SFTP channel constant
  52.      *
  53.      * \phpseclib3\Net\SSH2::exec() uses 0 and \phpseclib3\Net\SSH2::read() / \phpseclib3\Net\SSH2::write() use 1.
  54.      *
  55.      * @see \phpseclib3\Net\SSH2::send_channel_packet()
  56.      * @see \phpseclib3\Net\SSH2::get_channel_packet()
  57.      * @access private
  58.      */
  59.     const CHANNEL 0x100;
  60.     /**
  61.      * Reads data from a local file.
  62.      *
  63.      * @access public
  64.      * @see \phpseclib3\Net\SFTP::put()
  65.      */
  66.     const SOURCE_LOCAL_FILE 1;
  67.     /**
  68.      * Reads data from a string.
  69.      *
  70.      * @access public
  71.      * @see \phpseclib3\Net\SFTP::put()
  72.      */
  73.     // this value isn't really used anymore but i'm keeping it reserved for historical reasons
  74.     const SOURCE_STRING 2;
  75.     /**
  76.      * Reads data from callback:
  77.      * function callback($length) returns string to proceed, null for EOF
  78.      *
  79.      * @access public
  80.      * @see \phpseclib3\Net\SFTP::put()
  81.      */
  82.     const SOURCE_CALLBACK 16;
  83.     /**
  84.      * Resumes an upload
  85.      *
  86.      * @access public
  87.      * @see \phpseclib3\Net\SFTP::put()
  88.      */
  89.     const RESUME 4;
  90.     /**
  91.      * Append a local file to an already existing remote file
  92.      *
  93.      * @access public
  94.      * @see \phpseclib3\Net\SFTP::put()
  95.      */
  96.     const RESUME_START 8;
  97.     /**
  98.      * Packet Types
  99.      *
  100.      * @see self::__construct()
  101.      * @var array
  102.      * @access private
  103.      */
  104.     private $packet_types = [];
  105.     /**
  106.      * Status Codes
  107.      *
  108.      * @see self::__construct()
  109.      * @var array
  110.      * @access private
  111.      */
  112.     private $status_codes = [];
  113.     /**
  114.      * The Request ID
  115.      *
  116.      * The request ID exists in the off chance that a packet is sent out-of-order.  Of course, this library doesn't support
  117.      * concurrent actions, so it's somewhat academic, here.
  118.      *
  119.      * @var boolean
  120.      * @see self::_send_sftp_packet()
  121.      * @access private
  122.      */
  123.     private $use_request_id false;
  124.     /**
  125.      * The Packet Type
  126.      *
  127.      * The request ID exists in the off chance that a packet is sent out-of-order.  Of course, this library doesn't support
  128.      * concurrent actions, so it's somewhat academic, here.
  129.      *
  130.      * @var int
  131.      * @see self::_get_sftp_packet()
  132.      * @access private
  133.      */
  134.     private $packet_type = -1;
  135.     /**
  136.      * Packet Buffer
  137.      *
  138.      * @var string
  139.      * @see self::_get_sftp_packet()
  140.      * @access private
  141.      */
  142.     private $packet_buffer '';
  143.     /**
  144.      * Extensions supported by the server
  145.      *
  146.      * @var array
  147.      * @see self::_initChannel()
  148.      * @access private
  149.      */
  150.     private $extensions = [];
  151.     /**
  152.      * Server SFTP version
  153.      *
  154.      * @var int
  155.      * @see self::_initChannel()
  156.      * @access private
  157.      */
  158.     private $version;
  159.     /**
  160.      * Current working directory
  161.      *
  162.      * @var string
  163.      * @see self::realpath()
  164.      * @see self::chdir()
  165.      * @access private
  166.      */
  167.     private $pwd false;
  168.     /**
  169.      * Packet Type Log
  170.      *
  171.      * @see self::getLog()
  172.      * @var array
  173.      * @access private
  174.      */
  175.     private $packet_type_log = [];
  176.     /**
  177.      * Packet Log
  178.      *
  179.      * @see self::getLog()
  180.      * @var array
  181.      * @access private
  182.      */
  183.     private $packet_log = [];
  184.     /**
  185.      * Error information
  186.      *
  187.      * @see self::getSFTPErrors()
  188.      * @see self::getLastSFTPError()
  189.      * @var array
  190.      * @access private
  191.      */
  192.     private $sftp_errors = [];
  193.     /**
  194.      * Stat Cache
  195.      *
  196.      * Rather than always having to open a directory and close it immediately there after to see if a file is a directory
  197.      * we'll cache the results.
  198.      *
  199.      * @see self::_update_stat_cache()
  200.      * @see self::_remove_from_stat_cache()
  201.      * @see self::_query_stat_cache()
  202.      * @var array
  203.      * @access private
  204.      */
  205.     private $stat_cache = [];
  206.     /**
  207.      * Max SFTP Packet Size
  208.      *
  209.      * @see self::__construct()
  210.      * @see self::get()
  211.      * @var array
  212.      * @access private
  213.      */
  214.     private $max_sftp_packet;
  215.     /**
  216.      * Stat Cache Flag
  217.      *
  218.      * @see self::disableStatCache()
  219.      * @see self::enableStatCache()
  220.      * @var bool
  221.      * @access private
  222.      */
  223.     private $use_stat_cache true;
  224.     /**
  225.      * Sort Options
  226.      *
  227.      * @see self::_comparator()
  228.      * @see self::setListOrder()
  229.      * @var array
  230.      * @access private
  231.      */
  232.     protected $sortOptions = [];
  233.     /**
  234.      * Canonicalization Flag
  235.      *
  236.      * Determines whether or not paths should be canonicalized before being
  237.      * passed on to the remote server.
  238.      *
  239.      * @see self::enablePathCanonicalization()
  240.      * @see self::disablePathCanonicalization()
  241.      * @see self::realpath()
  242.      * @var bool
  243.      * @access private
  244.      */
  245.     private $canonicalize_paths true;
  246.     /**
  247.      * Request Buffers
  248.      *
  249.      * @see self::_get_sftp_packet()
  250.      * @var array
  251.      * @access private
  252.      */
  253.     var $requestBuffer = array();
  254.     /**
  255.      * Preserve timestamps on file downloads / uploads
  256.      *
  257.      * @see self::get()
  258.      * @see self::put()
  259.      * @var bool
  260.      * @access private
  261.      */
  262.     var $preserveTime false;
  263.     /**
  264.      * Default Constructor.
  265.      *
  266.      * Connects to an SFTP server
  267.      *
  268.      * @param string $host
  269.      * @param int $port
  270.      * @param int $timeout
  271.      * @return \phpseclib3\Net\SFTP
  272.      * @access public
  273.      */
  274.     public function __construct($host$port 22$timeout 10)
  275.     {
  276.         parent::__construct($host$port$timeout);
  277.         $this->max_sftp_packet << 15;
  278.         $this->packet_types = [
  279.             1  => 'NET_SFTP_INIT',
  280.             2  => 'NET_SFTP_VERSION',
  281.             /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+:
  282.                    SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
  283.                pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
  284.             3  => 'NET_SFTP_OPEN',
  285.             4  => 'NET_SFTP_CLOSE',
  286.             5  => 'NET_SFTP_READ',
  287.             6  => 'NET_SFTP_WRITE',
  288.             7  => 'NET_SFTP_LSTAT',
  289.             9  => 'NET_SFTP_SETSTAT',
  290.             11 => 'NET_SFTP_OPENDIR',
  291.             12 => 'NET_SFTP_READDIR',
  292.             13 => 'NET_SFTP_REMOVE',
  293.             14 => 'NET_SFTP_MKDIR',
  294.             15 => 'NET_SFTP_RMDIR',
  295.             16 => 'NET_SFTP_REALPATH',
  296.             17 => 'NET_SFTP_STAT',
  297.             /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+:
  298.                    SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
  299.                pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
  300.             18 => 'NET_SFTP_RENAME',
  301.             19 => 'NET_SFTP_READLINK',
  302.             20 => 'NET_SFTP_SYMLINK',
  303.             101=> 'NET_SFTP_STATUS',
  304.             102=> 'NET_SFTP_HANDLE',
  305.             /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+:
  306.                    SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
  307.                pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
  308.             103=> 'NET_SFTP_DATA',
  309.             104=> 'NET_SFTP_NAME',
  310.             105=> 'NET_SFTP_ATTRS',
  311.             200=> 'NET_SFTP_EXTENDED'
  312.         ];
  313.         $this->status_codes = [
  314.             => 'NET_SFTP_STATUS_OK',
  315.             => 'NET_SFTP_STATUS_EOF',
  316.             => 'NET_SFTP_STATUS_NO_SUCH_FILE',
  317.             => 'NET_SFTP_STATUS_PERMISSION_DENIED',
  318.             => 'NET_SFTP_STATUS_FAILURE',
  319.             => 'NET_SFTP_STATUS_BAD_MESSAGE',
  320.             => 'NET_SFTP_STATUS_NO_CONNECTION',
  321.             => 'NET_SFTP_STATUS_CONNECTION_LOST',
  322.             => 'NET_SFTP_STATUS_OP_UNSUPPORTED',
  323.             => 'NET_SFTP_STATUS_INVALID_HANDLE',
  324.             10 => 'NET_SFTP_STATUS_NO_SUCH_PATH',
  325.             11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS',
  326.             12 => 'NET_SFTP_STATUS_WRITE_PROTECT',
  327.             13 => 'NET_SFTP_STATUS_NO_MEDIA',
  328.             14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM',
  329.             15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED',
  330.             16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL',
  331.             17 => 'NET_SFTP_STATUS_LOCK_CONFLICT',
  332.             18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY',
  333.             19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY',
  334.             20 => 'NET_SFTP_STATUS_INVALID_FILENAME',
  335.             21 => 'NET_SFTP_STATUS_LINK_LOOP',
  336.             22 => 'NET_SFTP_STATUS_CANNOT_DELETE',
  337.             23 => 'NET_SFTP_STATUS_INVALID_PARAMETER',
  338.             24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY',
  339.             25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT',
  340.             26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED',
  341.             27 => 'NET_SFTP_STATUS_DELETE_PENDING',
  342.             28 => 'NET_SFTP_STATUS_FILE_CORRUPT',
  343.             29 => 'NET_SFTP_STATUS_OWNER_INVALID',
  344.             30 => 'NET_SFTP_STATUS_GROUP_INVALID',
  345.             31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK'
  346.         ];
  347.         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
  348.         // the order, in this case, matters quite a lot - see \phpseclib3\Net\SFTP::_parseAttributes() to understand why
  349.         $this->attributes = [
  350.             0x00000001 => 'NET_SFTP_ATTR_SIZE',
  351.             0x00000002 => 'NET_SFTP_ATTR_UIDGID'// defined in SFTPv3, removed in SFTPv4+
  352.             0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
  353.             0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
  354.             // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
  355.             // yields inconsistent behavior depending on how php is compiled.  so we left shift -1 (which, in
  356.             // two's compliment, consists of all 1 bits) by 31.  on 64-bit systems this'll yield 0xFFFFFFFF80000000.
  357.             // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
  358.             (-<< 31) & 0xFFFFFFFF => 'NET_SFTP_ATTR_EXTENDED'
  359.         ];
  360.         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
  361.         // the flag definitions change somewhat in SFTPv5+.  if SFTPv5+ support is added to this library, maybe name
  362.         // the array for that $this->open5_flags and similarly alter the constant names.
  363.         $this->open_flags = [
  364.             0x00000001 => 'NET_SFTP_OPEN_READ',
  365.             0x00000002 => 'NET_SFTP_OPEN_WRITE',
  366.             0x00000004 => 'NET_SFTP_OPEN_APPEND',
  367.             0x00000008 => 'NET_SFTP_OPEN_CREATE',
  368.             0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
  369.             0x00000020 => 'NET_SFTP_OPEN_EXCL'
  370.         ];
  371.         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
  372.         // see \phpseclib3\Net\SFTP::_parseLongname() for an explanation
  373.         $this->file_types = [
  374.             => 'NET_SFTP_TYPE_REGULAR',
  375.             => 'NET_SFTP_TYPE_DIRECTORY',
  376.             => 'NET_SFTP_TYPE_SYMLINK',
  377.             => 'NET_SFTP_TYPE_SPECIAL',
  378.             => 'NET_SFTP_TYPE_UNKNOWN',
  379.             // the following types were first defined for use in SFTPv5+
  380.             // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
  381.             => 'NET_SFTP_TYPE_SOCKET',
  382.             => 'NET_SFTP_TYPE_CHAR_DEVICE',
  383.             => 'NET_SFTP_TYPE_BLOCK_DEVICE',
  384.             => 'NET_SFTP_TYPE_FIFO'
  385.         ];
  386.         $this->define_array(
  387.             $this->packet_types,
  388.             $this->status_codes,
  389.             $this->attributes,
  390.             $this->open_flags,
  391.             $this->file_types
  392.         );
  393.         if (!defined('NET_SFTP_QUEUE_SIZE')) {
  394.             define('NET_SFTP_QUEUE_SIZE'32);
  395.         }
  396.         if (!defined('NET_SFTP_UPLOAD_QUEUE_SIZE')) {
  397.             define('NET_SFTP_UPLOAD_QUEUE_SIZE'1024);
  398.         }
  399.     }
  400.     /**
  401.      * Login
  402.      *
  403.      * @param string $username
  404.      * @param string|AsymmetricKey|array[]|Agent|null ...$args
  405.      * @throws \UnexpectedValueException on receipt of unexpected packets
  406.      * @return bool
  407.      * @access public
  408.      */
  409.     public function login($username, ...$args)
  410.     {
  411.         if (!parent::login(...func_get_args())) {
  412.             return false;
  413.         }
  414.         $this->window_size_server_to_client[self::CHANNEL] = $this->window_size;
  415.         $packet Strings::packSSH2(
  416.             'CsN3',
  417.             NET_SSH2_MSG_CHANNEL_OPEN,
  418.             'session',
  419.             self::CHANNEL,
  420.             $this->window_size,
  421.             0x4000
  422.         );
  423.         $this->send_binary_packet($packet);
  424.         $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN;
  425.         $response $this->get_channel_packet(self::CHANNELtrue);
  426.         if ($response === false) {
  427.             return false;
  428.         }
  429.         $packet Strings::packSSH2(
  430.             'CNsbs',
  431.             NET_SSH2_MSG_CHANNEL_REQUEST,
  432.             $this->server_channels[self::CHANNEL],
  433.             'subsystem',
  434.             true,
  435.             'sftp'
  436.         );
  437.         $this->send_binary_packet($packet);
  438.         $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
  439.         $response $this->get_channel_packet(self::CHANNELtrue);
  440.         if ($response === false) {
  441.             // from PuTTY's psftp.exe
  442.             $command "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" .
  443.                        "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" .
  444.                        "exec sftp-server";
  445.             // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does
  446.             // is redundant
  447.             $packet Strings::packSSH2(
  448.                 'CNsCs',
  449.                 NET_SSH2_MSG_CHANNEL_REQUEST,
  450.                 $this->server_channels[self::CHANNEL],
  451.                 'exec',
  452.                 1,
  453.                 $command
  454.             );
  455.             $this->send_binary_packet($packet);
  456.             $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
  457.             $response $this->get_channel_packet(self::CHANNELtrue);
  458.             if ($response === false) {
  459.                 return false;
  460.             }
  461.         }
  462.         $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
  463.         if (!$this->send_sftp_packet(NET_SFTP_INIT"\0\0\0\3")) {
  464.             return false;
  465.         }
  466.         $response $this->get_sftp_packet();
  467.         if ($this->packet_type != NET_SFTP_VERSION) {
  468.             throw new \UnexpectedValueException('Expected NET_SFTP_VERSION. '
  469.                                               'Got packet type: ' $this->packet_type);
  470.         }
  471.         list($this->version) = Strings::unpackSSH2('N'$response);
  472.         while (!empty($response)) {
  473.             list($key$value) = Strings::unpackSSH2('ss'$response);
  474.             $this->extensions[$key] = $value;
  475.         }
  476.         /*
  477.          SFTPv4+ defines a 'newline' extension.  SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
  478.          however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
  479.          not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
  480.          one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
  481.          'newline@vandyke.com' would.
  482.         */
  483.         /*
  484.         if (isset($this->extensions['newline@vandyke.com'])) {
  485.             $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
  486.             unset($this->extensions['newline@vandyke.com']);
  487.         }
  488.         */
  489.         $this->use_request_id true;
  490.         /*
  491.          A Note on SFTPv4/5/6 support:
  492.          <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.1> states the following:
  493.          "If the client wishes to interoperate with servers that support noncontiguous version
  494.           numbers it SHOULD send '3'"
  495.          Given that the server only sends its version number after the client has already done so, the above
  496.          seems to be suggesting that v3 should be the default version.  This makes sense given that v3 is the
  497.          most popular.
  498.          <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.5> states the following;
  499.          "If the server did not send the "versions" extension, or the version-from-list was not included, the
  500.           server MAY send a status response describing the failure, but MUST then close the channel without
  501.           processing any further requests."
  502.          So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and
  503.          a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4?  If it only implements
  504.          v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed
  505.          in draft-ietf-secsh-filexfer-13 would be quite impossible.  As such, what \phpseclib3\Net\SFTP would do is close the
  506.          channel and reopen it with a new and updated SSH_FXP_INIT packet.
  507.         */
  508.         switch ($this->version) {
  509.             case 2:
  510.             case 3:
  511.                 break;
  512.             default:
  513.                 return false;
  514.         }
  515.         $this->pwd $this->realpath('.');
  516.         $this->update_stat_cache($this->pwd, []);
  517.         return true;
  518.     }
  519.     /**
  520.      * Disable the stat cache
  521.      *
  522.      * @access public
  523.      */
  524.     function disableStatCache()
  525.     {
  526.         $this->use_stat_cache false;
  527.     }
  528.     /**
  529.      * Enable the stat cache
  530.      *
  531.      * @access public
  532.      */
  533.     public function enableStatCache()
  534.     {
  535.         $this->use_stat_cache true;
  536.     }
  537.     /**
  538.      * Clear the stat cache
  539.      *
  540.      * @access public
  541.      */
  542.     public function clearStatCache()
  543.     {
  544.         $this->stat_cache = [];
  545.     }
  546.     /**
  547.      * Enable path canonicalization
  548.      *
  549.      * @access public
  550.      */
  551.     public function enablePathCanonicalization()
  552.     {
  553.         $this->canonicalize_paths true;
  554.     }
  555.     /**
  556.      * Enable path canonicalization
  557.      *
  558.      * @access public
  559.      */
  560.     public function disablePathCanonicalization()
  561.     {
  562.         $this->canonicalize_paths false;
  563.     }
  564.     /**
  565.      * Returns the current directory name
  566.      *
  567.      * @return mixed
  568.      * @access public
  569.      */
  570.     public function pwd()
  571.     {
  572.         return $this->pwd;
  573.     }
  574.     /**
  575.      * Logs errors
  576.      *
  577.      * @param string $response
  578.      * @param int $status
  579.      * @access private
  580.      */
  581.     private function logError($response$status = -1)
  582.     {
  583.         if ($status == -1) {
  584.             list($status) = Strings::unpackSSH2('N'$response);
  585.         }
  586.         list($error) = $this->status_codes[$status];
  587.         if ($this->version 2) {
  588.             list($message) = Strings::unpackSSH2('s'$response);
  589.             $this->sftp_errors[] = "$error$message";
  590.         } else {
  591.             $this->sftp_errors[] = $error;
  592.         }
  593.     }
  594.     /**
  595.      * Canonicalize the Server-Side Path Name
  596.      *
  597.      * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it.  Returns
  598.      * the absolute (canonicalized) path.
  599.      *
  600.      * If canonicalize_paths has been disabled using disablePathCanonicalization(), $path is returned as-is.
  601.      *
  602.      * @see self::chdir()
  603.      * @see self::disablePathCanonicalization()
  604.      * @param string $path
  605.      * @throws \UnexpectedValueException on receipt of unexpected packets
  606.      * @return mixed
  607.      * @access public
  608.      */
  609.     public function realpath($path)
  610.     {
  611.         if (!$this->canonicalize_paths) {
  612.             return $path;
  613.         }
  614.         if ($this->pwd === false) {
  615.             // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
  616.             if (!$this->send_sftp_packet(NET_SFTP_REALPATHStrings::packSSH2('s'$path))) {
  617.                 return false;
  618.             }
  619.             $response $this->get_sftp_packet();
  620.             switch ($this->packet_type) {
  621.                 case NET_SFTP_NAME:
  622.                     // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following
  623.                     // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks
  624.                     // at is the first part and that part is defined the same in SFTP versions 3 through 6.
  625.                     list(, $filename) = Strings::unpackSSH2('Ns'$response);
  626.                     return $filename;
  627.                 case NET_SFTP_STATUS:
  628.                     $this->logError($response);
  629.                     return false;
  630.                 default:
  631.                     throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. '
  632.                                                       'Got packet type: ' $this->packet_type);
  633.             }
  634.         }
  635.         if (!strlen($path) || $path[0] != '/') {
  636.             $path $this->pwd '/' $path;
  637.         }
  638.         $path explode('/'$path);
  639.         $new = [];
  640.         foreach ($path as $dir) {
  641.             if (!strlen($dir)) {
  642.                 continue;
  643.             }
  644.             switch ($dir) {
  645.                 case '..':
  646.                     array_pop($new);
  647.                 case '.':
  648.                     break;
  649.                 default:
  650.                     $new[] = $dir;
  651.             }
  652.         }
  653.         return '/' implode('/'$new);
  654.     }
  655.     /**
  656.      * Changes the current directory
  657.      *
  658.      * @param string $dir
  659.      * @throws \UnexpectedValueException on receipt of unexpected packets
  660.      * @return bool
  661.      * @access public
  662.      */
  663.     public function chdir($dir)
  664.     {
  665.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  666.             return false;
  667.         }
  668.         // assume current dir if $dir is empty
  669.         if ($dir === '') {
  670.             $dir './';
  671.         // suffix a slash if needed
  672.         } elseif ($dir[strlen($dir) - 1] != '/') {
  673.             $dir.= '/';
  674.         }
  675.         $dir $this->realpath($dir);
  676.         // confirm that $dir is, in fact, a valid directory
  677.         if ($this->use_stat_cache && is_array($this->query_stat_cache($dir))) {
  678.             $this->pwd $dir;
  679.             return true;
  680.         }
  681.         // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us
  682.         // the currently logged in user has the appropriate permissions or not. maybe you could see if
  683.         // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy
  684.         // way to get those with SFTP
  685.         if (!$this->send_sftp_packet(NET_SFTP_OPENDIRStrings::packSSH2('s'$dir))) {
  686.             return false;
  687.         }
  688.         // see \phpseclib3\Net\SFTP::nlist() for a more thorough explanation of the following
  689.         $response $this->get_sftp_packet();
  690.         switch ($this->packet_type) {
  691.             case NET_SFTP_HANDLE:
  692.                 $handle substr($response4);
  693.                 break;
  694.             case NET_SFTP_STATUS:
  695.                 $this->logError($response);
  696.                 return false;
  697.             default:
  698.                 throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS' .
  699.                                                     'Got packet type: ' $this->packet_type);
  700.         }
  701.         if (!$this->close_handle($handle)) {
  702.             return false;
  703.         }
  704.         $this->update_stat_cache($dir, []);
  705.         $this->pwd $dir;
  706.         return true;
  707.     }
  708.     /**
  709.      * Returns a list of files in the given directory
  710.      *
  711.      * @param string $dir
  712.      * @param bool $recursive
  713.      * @return mixed
  714.      * @access public
  715.      */
  716.     public function nlist($dir '.'$recursive false)
  717.     {
  718.         return $this->nlist_helper($dir$recursive'');
  719.     }
  720.     /**
  721.      * Helper method for nlist
  722.      *
  723.      * @param string $dir
  724.      * @param bool $recursive
  725.      * @param string $relativeDir
  726.      * @return mixed
  727.      * @access private
  728.      */
  729.     private function nlist_helper($dir$recursive$relativeDir)
  730.     {
  731.         $files $this->readlist($dirfalse);
  732.         if (!$recursive || $files === false) {
  733.             return $files;
  734.         }
  735.         $result = [];
  736.         foreach ($files as $value) {
  737.             if ($value == '.' || $value == '..') {
  738.                 $result[] = $relativeDir $value;
  739.                 continue;
  740.             }
  741.             if (is_array($this->query_stat_cache($this->realpath($dir '/' $value)))) {
  742.                 $temp $this->nlist_helper($dir '/' $valuetrue$relativeDir $value '/');
  743.                 $temp is_array($temp) ? $temp : [];
  744.                 $result array_merge($result$temp);
  745.             } else {
  746.                 $result[] = $relativeDir $value;
  747.             }
  748.         }
  749.         return $result;
  750.     }
  751.     /**
  752.      * Returns a detailed list of files in the given directory
  753.      *
  754.      * @param string $dir
  755.      * @param bool $recursive
  756.      * @return mixed
  757.      * @access public
  758.      */
  759.     public function rawlist($dir '.'$recursive false)
  760.     {
  761.         $files $this->readlist($dirtrue);
  762.         if (!$recursive || $files === false) {
  763.             return $files;
  764.         }
  765.         static $depth 0;
  766.         foreach ($files as $key => $value) {
  767.             if ($depth != && $key == '..') {
  768.                 unset($files[$key]);
  769.                 continue;
  770.             }
  771.             $is_directory false;
  772.             if ($key != '.' && $key != '..') {
  773.                 if ($this->use_stat_cache) {
  774.                     $is_directory is_array($this->query_stat_cache($this->realpath($dir '/' $key)));
  775.                 } else {
  776.                     $stat $this->lstat($dir '/' $key);
  777.                     $is_directory $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY;
  778.                 }
  779.             }
  780.             if ($is_directory) {
  781.                 $depth++;
  782.                 $files[$key] = $this->rawlist($dir '/' $keytrue);
  783.                 $depth--;
  784.             } else {
  785.                 $files[$key] = (object) $value;
  786.             }
  787.         }
  788.         return $files;
  789.     }
  790.     /**
  791.      * Reads a list, be it detailed or not, of files in the given directory
  792.      *
  793.      * @param string $dir
  794.      * @param bool $raw
  795.      * @return mixed
  796.      * @throws \UnexpectedValueException on receipt of unexpected packets
  797.      * @access private
  798.      */
  799.     private function readlist($dir$raw true)
  800.     {
  801.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  802.             return false;
  803.         }
  804.         $dir $this->realpath($dir '/');
  805.         if ($dir === false) {
  806.             return false;
  807.         }
  808.         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
  809.         if (!$this->send_sftp_packet(NET_SFTP_OPENDIRStrings::packSSH2('s'$dir))) {
  810.             return false;
  811.         }
  812.         $response $this->get_sftp_packet();
  813.         switch ($this->packet_type) {
  814.             case NET_SFTP_HANDLE:
  815.                 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
  816.                 // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
  817.                 // represent the length of the string and leave it at that
  818.                 $handle substr($response4);
  819.                 break;
  820.             case NET_SFTP_STATUS:
  821.                 // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
  822.                 $this->logError($response);
  823.                 return false;
  824.             default:
  825.                 throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
  826.                                                   'Got packet type: ' $this->packet_type);
  827.         }
  828.         $this->update_stat_cache($dir, []);
  829.         $contents = [];
  830.         while (true) {
  831.             // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
  832.             // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many
  833.             // SSH_MSG_CHANNEL_DATA messages is not known to me.
  834.             if (!$this->send_sftp_packet(NET_SFTP_READDIRStrings::packSSH2('s'$handle))) {
  835.                 return false;
  836.             }
  837.             $response $this->get_sftp_packet();
  838.             switch ($this->packet_type) {
  839.                 case NET_SFTP_NAME:
  840.                     list($count) = Strings::unpackSSH2('N'$response);
  841.                     for ($i 0$i $count$i++) {
  842.                         list($shortname$longname) = Strings::unpackSSH2('ss'$response);
  843.                         $attributes $this->parseAttributes($response);
  844.                         if (!isset($attributes['type'])) {
  845.                             $fileType $this->parseLongname($longname);
  846.                             if ($fileType) {
  847.                                 $attributes['type'] = $fileType;
  848.                             }
  849.                         }
  850.                         $contents[$shortname] = $attributes + ['filename' => $shortname];
  851.                         if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) {
  852.                             $this->update_stat_cache($dir '/' $shortname, []);
  853.                         } else {
  854.                             if ($shortname == '..') {
  855.                                 $temp $this->realpath($dir '/..') . '/.';
  856.                             } else {
  857.                                 $temp $dir '/' $shortname;
  858.                             }
  859.                             $this->update_stat_cache($temp, (object) ['lstat' => $attributes]);
  860.                         }
  861.                         // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
  862.                         // final SSH_FXP_STATUS packet should tell us that, already.
  863.                     }
  864.                     break;
  865.                 case NET_SFTP_STATUS:
  866.                     list($status) = Strings::unpackSSH2('N'$response);
  867.                     if ($status != NET_SFTP_STATUS_EOF) {
  868.                         $this->logError($response$status);
  869.                         return false;
  870.                     }
  871.                     break 2;
  872.                 default:
  873.                     throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. '
  874.                                                       'Got packet type: ' $this->packet_type);
  875.             }
  876.         }
  877.         if (!$this->close_handle($handle)) {
  878.             return false;
  879.         }
  880.         if (count($this->sortOptions)) {
  881.             uasort($contents, [&$this'comparator']);
  882.         }
  883.         return $raw $contents array_map('strval'array_keys($contents));
  884.     }
  885.     /**
  886.      * Compares two rawlist entries using parameters set by setListOrder()
  887.      *
  888.      * Intended for use with uasort()
  889.      *
  890.      * @param array $a
  891.      * @param array $b
  892.      * @return int
  893.      * @access private
  894.      */
  895.     private function comparator($a$b)
  896.     {
  897.         switch (true) {
  898.             case $a['filename'] === '.' || $b['filename'] === '.':
  899.                 if ($a['filename'] === $b['filename']) {
  900.                     return 0;
  901.                 }
  902.                 return $a['filename'] === '.' ? -1;
  903.             case $a['filename'] === '..' || $b['filename'] === '..':
  904.                 if ($a['filename'] === $b['filename']) {
  905.                     return 0;
  906.                 }
  907.                 return $a['filename'] === '..' ? -1;
  908.             case isset($a['type']) && $a['type'] === NET_SFTP_TYPE_DIRECTORY:
  909.                 if (!isset($b['type'])) {
  910.                     return 1;
  911.                 }
  912.                 if ($b['type'] !== $a['type']) {
  913.                     return -1;
  914.                 }
  915.                 break;
  916.             case isset($b['type']) && $b['type'] === NET_SFTP_TYPE_DIRECTORY:
  917.                 return 1;
  918.         }
  919.         foreach ($this->sortOptions as $sort => $order) {
  920.             if (!isset($a[$sort]) || !isset($b[$sort])) {
  921.                 if (isset($a[$sort])) {
  922.                     return -1;
  923.                 }
  924.                 if (isset($b[$sort])) {
  925.                     return 1;
  926.                 }
  927.                 return 0;
  928.             }
  929.             switch ($sort) {
  930.                 case 'filename':
  931.                     $result strcasecmp($a['filename'], $b['filename']);
  932.                     if ($result) {
  933.                         return $order === SORT_DESC ? -$result $result;
  934.                     }
  935.                     break;
  936.                 case 'mode':
  937.                     $a[$sort]&= 07777;
  938.                     $b[$sort]&= 07777;
  939.                 default:
  940.                     if ($a[$sort] === $b[$sort]) {
  941.                         break;
  942.                     }
  943.                     return $order === SORT_ASC $a[$sort] - $b[$sort] : $b[$sort] - $a[$sort];
  944.             }
  945.         }
  946.     }
  947.     /**
  948.      * Defines how nlist() and rawlist() will be sorted - if at all.
  949.      *
  950.      * If sorting is enabled directories and files will be sorted independently with
  951.      * directories appearing before files in the resultant array that is returned.
  952.      *
  953.      * Any parameter returned by stat is a valid sort parameter for this function.
  954.      * Filename comparisons are case insensitive.
  955.      *
  956.      * Examples:
  957.      *
  958.      * $sftp->setListOrder('filename', SORT_ASC);
  959.      * $sftp->setListOrder('size', SORT_DESC, 'filename', SORT_ASC);
  960.      * $sftp->setListOrder(true);
  961.      *    Separates directories from files but doesn't do any sorting beyond that
  962.      * $sftp->setListOrder();
  963.      *    Don't do any sort of sorting
  964.      *
  965.      * @param string[] ...$args
  966.      * @access public
  967.      */
  968.     public function setListOrder(...$args)
  969.     {
  970.         $this->sortOptions = [];
  971.         if (empty($args)) {
  972.             return;
  973.         }
  974.         $len count($args) & 0x7FFFFFFE;
  975.         for ($i 0$i $len$i+=2) {
  976.             $this->sortOptions[$args[$i]] = $args[$i 1];
  977.         }
  978.         if (!count($this->sortOptions)) {
  979.             $this->sortOptions = ['bogus' => true];
  980.         }
  981.     }
  982.     /**
  983.      * Save files / directories to cache
  984.      *
  985.      * @param string $path
  986.      * @param mixed $value
  987.      * @access private
  988.      */
  989.     private function update_stat_cache($path$value)
  990.     {
  991.         if ($this->use_stat_cache === false) {
  992.             return;
  993.         }
  994.         // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($path, '/'))
  995.         $dirs explode('/'preg_replace('#^/|/(?=/)|/$#'''$path));
  996.         $temp = &$this->stat_cache;
  997.         $max count($dirs) - 1;
  998.         foreach ($dirs as $i => $dir) {
  999.             // if $temp is an object that means one of two things.
  1000.             //  1. a file was deleted and changed to a directory behind phpseclib's back
  1001.             //  2. it's a symlink. when lstat is done it's unclear what it's a symlink to
  1002.             if (is_object($temp)) {
  1003.                 $temp = [];
  1004.             }
  1005.             if (!isset($temp[$dir])) {
  1006.                 $temp[$dir] = [];
  1007.             }
  1008.             if ($i === $max) {
  1009.                 if (is_object($temp[$dir]) && is_object($value)) {
  1010.                     if (!isset($value->stat) && isset($temp[$dir]->stat)) {
  1011.                         $value->stat $temp[$dir]->stat;
  1012.                     }
  1013.                     if (!isset($value->lstat) && isset($temp[$dir]->lstat)) {
  1014.                         $value->lstat $temp[$dir]->lstat;
  1015.                     }
  1016.                 }
  1017.                 $temp[$dir] = $value;
  1018.                 break;
  1019.             }
  1020.             $temp = &$temp[$dir];
  1021.         }
  1022.     }
  1023.     /**
  1024.      * Remove files / directories from cache
  1025.      *
  1026.      * @param string $path
  1027.      * @return bool
  1028.      * @access private
  1029.      */
  1030.     private function remove_from_stat_cache($path)
  1031.     {
  1032.         $dirs explode('/'preg_replace('#^/|/(?=/)|/$#'''$path));
  1033.         $temp = &$this->stat_cache;
  1034.         $max count($dirs) - 1;
  1035.         foreach ($dirs as $i => $dir) {
  1036.             if (!is_array($temp)) {
  1037.                 return false;
  1038.             }
  1039.             if ($i === $max) {
  1040.                 unset($temp[$dir]);
  1041.                 return true;
  1042.             }
  1043.             if (!isset($temp[$dir])) {
  1044.                 return false;
  1045.             }
  1046.             $temp = &$temp[$dir];
  1047.         }
  1048.     }
  1049.     /**
  1050.      * Checks cache for path
  1051.      *
  1052.      * Mainly used by file_exists
  1053.      *
  1054.      * @param string $path
  1055.      * @return mixed
  1056.      * @access private
  1057.      */
  1058.     private function query_stat_cache($path)
  1059.     {
  1060.         $dirs explode('/'preg_replace('#^/|/(?=/)|/$#'''$path));
  1061.         $temp = &$this->stat_cache;
  1062.         foreach ($dirs as $dir) {
  1063.             if (!is_array($temp)) {
  1064.                 return null;
  1065.             }
  1066.             if (!isset($temp[$dir])) {
  1067.                 return null;
  1068.             }
  1069.             $temp = &$temp[$dir];
  1070.         }
  1071.         return $temp;
  1072.     }
  1073.     /**
  1074.      * Returns general information about a file.
  1075.      *
  1076.      * Returns an array on success and false otherwise.
  1077.      *
  1078.      * @param string $filename
  1079.      * @return mixed
  1080.      * @access public
  1081.      */
  1082.     public function stat($filename)
  1083.     {
  1084.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  1085.             return false;
  1086.         }
  1087.         $filename $this->realpath($filename);
  1088.         if ($filename === false) {
  1089.             return false;
  1090.         }
  1091.         if ($this->use_stat_cache) {
  1092.             $result $this->query_stat_cache($filename);
  1093.             if (is_array($result) && isset($result['.']) && isset($result['.']->stat)) {
  1094.                 return $result['.']->stat;
  1095.             }
  1096.             if (is_object($result) && isset($result->stat)) {
  1097.                 return $result->stat;
  1098.             }
  1099.         }
  1100.         $stat $this->stat_helper($filenameNET_SFTP_STAT);
  1101.         if ($stat === false) {
  1102.             $this->remove_from_stat_cache($filename);
  1103.             return false;
  1104.         }
  1105.         if (isset($stat['type'])) {
  1106.             if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
  1107.                 $filename.= '/.';
  1108.             }
  1109.             $this->update_stat_cache($filename, (object) ['stat' => $stat]);
  1110.             return $stat;
  1111.         }
  1112.         $pwd $this->pwd;
  1113.         $stat['type'] = $this->chdir($filename) ?
  1114.             NET_SFTP_TYPE_DIRECTORY :
  1115.             NET_SFTP_TYPE_REGULAR;
  1116.         $this->pwd $pwd;
  1117.         if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
  1118.             $filename.= '/.';
  1119.         }
  1120.         $this->update_stat_cache($filename, (object) ['stat' => $stat]);
  1121.         return $stat;
  1122.     }
  1123.     /**
  1124.      * Returns general information about a file or symbolic link.
  1125.      *
  1126.      * Returns an array on success and false otherwise.
  1127.      *
  1128.      * @param string $filename
  1129.      * @return mixed
  1130.      * @access public
  1131.      */
  1132.     public function lstat($filename)
  1133.     {
  1134.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  1135.             return false;
  1136.         }
  1137.         $filename $this->realpath($filename);
  1138.         if ($filename === false) {
  1139.             return false;
  1140.         }
  1141.         if ($this->use_stat_cache) {
  1142.             $result $this->query_stat_cache($filename);
  1143.             if (is_array($result) && isset($result['.']) && isset($result['.']->lstat)) {
  1144.                 return $result['.']->lstat;
  1145.             }
  1146.             if (is_object($result) && isset($result->lstat)) {
  1147.                 return $result->lstat;
  1148.             }
  1149.         }
  1150.         $lstat $this->stat_helper($filenameNET_SFTP_LSTAT);
  1151.         if ($lstat === false) {
  1152.             $this->remove_from_stat_cache($filename);
  1153.             return false;
  1154.         }
  1155.         if (isset($lstat['type'])) {
  1156.             if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
  1157.                 $filename.= '/.';
  1158.             }
  1159.             $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
  1160.             return $lstat;
  1161.         }
  1162.         $stat $this->stat_helper($filenameNET_SFTP_STAT);
  1163.         if ($lstat != $stat) {
  1164.             $lstat array_merge($lstat, ['type' => NET_SFTP_TYPE_SYMLINK]);
  1165.             $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
  1166.             return $stat;
  1167.         }
  1168.         $pwd $this->pwd;
  1169.         $lstat['type'] = $this->chdir($filename) ?
  1170.             NET_SFTP_TYPE_DIRECTORY :
  1171.             NET_SFTP_TYPE_REGULAR;
  1172.         $this->pwd $pwd;
  1173.         if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
  1174.             $filename.= '/.';
  1175.         }
  1176.         $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
  1177.         return $lstat;
  1178.     }
  1179.     /**
  1180.      * Returns general information about a file or symbolic link
  1181.      *
  1182.      * Determines information without calling \phpseclib3\Net\SFTP::realpath().
  1183.      * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT.
  1184.      *
  1185.      * @param string $filename
  1186.      * @param int $type
  1187.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1188.      * @return mixed
  1189.      * @access private
  1190.      */
  1191.     private function stat_helper($filename$type)
  1192.     {
  1193.         // SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
  1194.         $packet Strings::packSSH2('s'$filename);
  1195.         if (!$this->send_sftp_packet($type$packet)) {
  1196.             return false;
  1197.         }
  1198.         $response $this->get_sftp_packet();
  1199.         switch ($this->packet_type) {
  1200.             case NET_SFTP_ATTRS:
  1201.                 return $this->parseAttributes($response);
  1202.             case NET_SFTP_STATUS:
  1203.                 $this->logError($response);
  1204.                 return false;
  1205.         }
  1206.         throw new \UnexpectedValueException('Expected NET_SFTP_ATTRS or NET_SFTP_STATUS. '
  1207.                                           'Got packet type: ' $this->packet_type);
  1208.     }
  1209.     /**
  1210.      * Truncates a file to a given length
  1211.      *
  1212.      * @param string $filename
  1213.      * @param int $new_size
  1214.      * @return bool
  1215.      * @access public
  1216.      */
  1217.     public function truncate($filename$new_size)
  1218.     {
  1219.         $attr pack('N3'NET_SFTP_ATTR_SIZE$new_size 4294967296$new_size); // 4294967296 == 0x100000000 == 1<<32
  1220.         return $this->setstat($filename$attrfalse);
  1221.     }
  1222.     /**
  1223.      * Sets access and modification time of file.
  1224.      *
  1225.      * If the file does not exist, it will be created.
  1226.      *
  1227.      * @param string $filename
  1228.      * @param int $time
  1229.      * @param int $atime
  1230.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1231.      * @return bool
  1232.      * @access public
  1233.      */
  1234.     public function touch($filename$time null$atime null)
  1235.     {
  1236.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  1237.             return false;
  1238.         }
  1239.         $filename $this->realpath($filename);
  1240.         if ($filename === false) {
  1241.             return false;
  1242.         }
  1243.         if (!isset($time)) {
  1244.             $time time();
  1245.         }
  1246.         if (!isset($atime)) {
  1247.             $atime $time;
  1248.         }
  1249.         $flags NET_SFTP_OPEN_WRITE NET_SFTP_OPEN_CREATE NET_SFTP_OPEN_EXCL;
  1250.         $attr pack('N3'NET_SFTP_ATTR_ACCESSTIME$time$atime);
  1251.         $packet Strings::packSSH2('sN'$filename$flags) . $attr;
  1252.         if (!$this->send_sftp_packet(NET_SFTP_OPEN$packet)) {
  1253.             return false;
  1254.         }
  1255.         $response $this->get_sftp_packet();
  1256.         switch ($this->packet_type) {
  1257.             case NET_SFTP_HANDLE:
  1258.                 return $this->close_handle(substr($response4));
  1259.             case NET_SFTP_STATUS:
  1260.                 $this->logError($response);
  1261.                 break;
  1262.             default:
  1263.                 throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
  1264.                                                   'Got packet type: ' $this->packet_type);
  1265.         }
  1266.         return $this->setstat($filename$attrfalse);
  1267.     }
  1268.     /**
  1269.      * Changes file or directory owner
  1270.      *
  1271.      * Returns true on success or false on error.
  1272.      *
  1273.      * @param string $filename
  1274.      * @param int $uid
  1275.      * @param bool $recursive
  1276.      * @return bool
  1277.      * @access public
  1278.      */
  1279.     public function chown($filename$uid$recursive false)
  1280.     {
  1281.         // quoting from <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
  1282.         // "if the owner or group is specified as -1, then that ID is not changed"
  1283.         $attr pack('N3'NET_SFTP_ATTR_UIDGID$uid, -1);
  1284.         return $this->setstat($filename$attr$recursive);
  1285.     }
  1286.     /**
  1287.      * Changes file or directory group
  1288.      *
  1289.      * Returns true on success or false on error.
  1290.      *
  1291.      * @param string $filename
  1292.      * @param int $gid
  1293.      * @param bool $recursive
  1294.      * @return bool
  1295.      * @access public
  1296.      */
  1297.     public function chgrp($filename$gid$recursive false)
  1298.     {
  1299.         $attr pack('N3'NET_SFTP_ATTR_UIDGID, -1$gid);
  1300.         return $this->setstat($filename$attr$recursive);
  1301.     }
  1302.     /**
  1303.      * Set permissions on a file.
  1304.      *
  1305.      * Returns the new file permissions on success or false on error.
  1306.      * If $recursive is true than this just returns true or false.
  1307.      *
  1308.      * @param int $mode
  1309.      * @param string $filename
  1310.      * @param bool $recursive
  1311.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1312.      * @return mixed
  1313.      * @access public
  1314.      */
  1315.     public function chmod($mode$filename$recursive false)
  1316.     {
  1317.         if (is_string($mode) && is_int($filename)) {
  1318.             $temp $mode;
  1319.             $mode $filename;
  1320.             $filename $temp;
  1321.         }
  1322.         $attr pack('N2'NET_SFTP_ATTR_PERMISSIONS$mode 07777);
  1323.         if (!$this->setstat($filename$attr$recursive)) {
  1324.             return false;
  1325.         }
  1326.         if ($recursive) {
  1327.             return true;
  1328.         }
  1329.         $filename $this->realpath($filename);
  1330.         // rather than return what the permissions *should* be, we'll return what they actually are.  this will also
  1331.         // tell us if the file actually exists.
  1332.         // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
  1333.         $packet pack('Na*'strlen($filename), $filename);
  1334.         if (!$this->send_sftp_packet(NET_SFTP_STAT$packet)) {
  1335.             return false;
  1336.         }
  1337.         $response $this->get_sftp_packet();
  1338.         switch ($this->packet_type) {
  1339.             case NET_SFTP_ATTRS:
  1340.                 $attrs $this->parseAttributes($response);
  1341.                 return $attrs['mode'];
  1342.             case NET_SFTP_STATUS:
  1343.                 $this->logError($response);
  1344.                 return false;
  1345.         }
  1346.         throw new \UnexpectedValueException('Expected NET_SFTP_ATTRS or NET_SFTP_STATUS. '
  1347.                                           'Got packet type: ' $this->packet_type);
  1348.     }
  1349.     /**
  1350.      * Sets information about a file
  1351.      *
  1352.      * @param string $filename
  1353.      * @param string $attr
  1354.      * @param bool $recursive
  1355.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1356.      * @return bool
  1357.      * @access private
  1358.      */
  1359.     private function setstat($filename$attr$recursive)
  1360.     {
  1361.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  1362.             return false;
  1363.         }
  1364.         $filename $this->realpath($filename);
  1365.         if ($filename === false) {
  1366.             return false;
  1367.         }
  1368.         $this->remove_from_stat_cache($filename);
  1369.         if ($recursive) {
  1370.             $i 0;
  1371.             $result $this->setstat_recursive($filename$attr$i);
  1372.             $this->read_put_responses($i);
  1373.             return $result;
  1374.         }
  1375.         // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to
  1376.         // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
  1377.         if (!$this->send_sftp_packet(NET_SFTP_SETSTATStrings::packSSH2('s'$filename) . $attr)) {
  1378.             return false;
  1379.         }
  1380.         /*
  1381.          "Because some systems must use separate system calls to set various attributes, it is possible that a failure
  1382.           response will be returned, but yet some of the attributes may be have been successfully modified.  If possible,
  1383.           servers SHOULD avoid this situation; however, clients MUST be aware that this is possible."
  1384.           -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
  1385.         */
  1386.         $response $this->get_sftp_packet();
  1387.         if ($this->packet_type != NET_SFTP_STATUS) {
  1388.             throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
  1389.                                               'Got packet type: ' $this->packet_type);
  1390.         }
  1391.         list($status) = Strings::unpackSSH2('N'$response);
  1392.         if ($status != NET_SFTP_STATUS_OK) {
  1393.             $this->logError($response$status);
  1394.             return false;
  1395.         }
  1396.         return true;
  1397.     }
  1398.     /**
  1399.      * Recursively sets information on directories on the SFTP server
  1400.      *
  1401.      * Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
  1402.      *
  1403.      * @param string $path
  1404.      * @param string $attr
  1405.      * @param int $i
  1406.      * @return bool
  1407.      * @access private
  1408.      */
  1409.     private function setstat_recursive($path$attr, &$i)
  1410.     {
  1411.         if (!$this->read_put_responses($i)) {
  1412.             return false;
  1413.         }
  1414.         $i 0;
  1415.         $entries $this->readlist($pathtrue);
  1416.         if ($entries === false) {
  1417.             return $this->setstat($path$attrfalse);
  1418.         }
  1419.         // normally $entries would have at least . and .. but it might not if the directories
  1420.         // permissions didn't allow reading
  1421.         if (empty($entries)) {
  1422.             return false;
  1423.         }
  1424.         unset($entries['.'], $entries['..']);
  1425.         foreach ($entries as $filename => $props) {
  1426.             if (!isset($props['type'])) {
  1427.                 return false;
  1428.             }
  1429.             $temp $path '/' $filename;
  1430.             if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
  1431.                 if (!$this->setstat_recursive($temp$attr$i)) {
  1432.                     return false;
  1433.                 }
  1434.             } else {
  1435.                 if (!$this->send_sftp_packet(NET_SFTP_SETSTATStrings::packSSH2('s'$temp) . $attr)) {
  1436.                     return false;
  1437.                 }
  1438.                 $i++;
  1439.                 if ($i >= NET_SFTP_QUEUE_SIZE) {
  1440.                     if (!$this->read_put_responses($i)) {
  1441.                         return false;
  1442.                     }
  1443.                     $i 0;
  1444.                 }
  1445.             }
  1446.         }
  1447.         if (!$this->send_sftp_packet(NET_SFTP_SETSTATStrings::packSSH2('s'$path) . $attr)) {
  1448.             return false;
  1449.         }
  1450.         $i++;
  1451.         if ($i >= NET_SFTP_QUEUE_SIZE) {
  1452.             if (!$this->read_put_responses($i)) {
  1453.                 return false;
  1454.             }
  1455.             $i 0;
  1456.         }
  1457.         return true;
  1458.     }
  1459.     /**
  1460.      * Return the target of a symbolic link
  1461.      *
  1462.      * @param string $link
  1463.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1464.      * @return mixed
  1465.      * @access public
  1466.      */
  1467.     public function readlink($link)
  1468.     {
  1469.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  1470.             return false;
  1471.         }
  1472.         $link $this->realpath($link);
  1473.         if (!$this->send_sftp_packet(NET_SFTP_READLINKStrings::packSSH2('s'$link))) {
  1474.             return false;
  1475.         }
  1476.         $response $this->get_sftp_packet();
  1477.         switch ($this->packet_type) {
  1478.             case NET_SFTP_NAME:
  1479.                 break;
  1480.             case NET_SFTP_STATUS:
  1481.                 $this->logError($response);
  1482.                 return false;
  1483.             default:
  1484.                 throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. '
  1485.                                                   'Got packet type: ' $this->packet_type);
  1486.         }
  1487.         list($count) = Strings::unpackSSH2('N'$response);
  1488.         // the file isn't a symlink
  1489.         if (!$count) {
  1490.             return false;
  1491.         }
  1492.         list($filename) = Strings::unpackSSH2('s'$response);
  1493.         return $filename;
  1494.     }
  1495.     /**
  1496.      * Create a symlink
  1497.      *
  1498.      * symlink() creates a symbolic link to the existing target with the specified name link.
  1499.      *
  1500.      * @param string $target
  1501.      * @param string $link
  1502.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1503.      * @return bool
  1504.      * @access public
  1505.      */
  1506.     public function symlink($target$link)
  1507.     {
  1508.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  1509.             return false;
  1510.         }
  1511.         //$target = $this->realpath($target);
  1512.         $link $this->realpath($link);
  1513.         $packet Strings::packSSH2('ss'$target$link);
  1514.         if (!$this->send_sftp_packet(NET_SFTP_SYMLINK$packet)) {
  1515.             return false;
  1516.         }
  1517.         $response $this->get_sftp_packet();
  1518.         if ($this->packet_type != NET_SFTP_STATUS) {
  1519.             throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
  1520.                                               'Got packet type: ' $this->packet_type);
  1521.         }
  1522.         list($status) = Strings::unpackSSH2('N'$response);
  1523.         if ($status != NET_SFTP_STATUS_OK) {
  1524.             $this->logError($response$status);
  1525.             return false;
  1526.         }
  1527.         return true;
  1528.     }
  1529.     /**
  1530.      * Creates a directory.
  1531.      *
  1532.      * @param string $dir
  1533.      * @param int $mode
  1534.      * @param bool $recursive
  1535.      * @return bool
  1536.      * @access public
  1537.      */
  1538.     public function mkdir($dir$mode = -1$recursive false)
  1539.     {
  1540.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  1541.             return false;
  1542.         }
  1543.         $dir $this->realpath($dir);
  1544.         if ($recursive) {
  1545.             $dirs explode('/'preg_replace('#/(?=/)|/$#'''$dir));
  1546.             if (empty($dirs[0])) {
  1547.                 array_shift($dirs);
  1548.                 $dirs[0] = '/' $dirs[0];
  1549.             }
  1550.             for ($i 0$i count($dirs); $i++) {
  1551.                 $temp array_slice($dirs0$i 1);
  1552.                 $temp implode('/'$temp);
  1553.                 $result $this->mkdir_helper($temp$mode);
  1554.             }
  1555.             return $result;
  1556.         }
  1557.         return $this->mkdir_helper($dir$mode);
  1558.     }
  1559.     /**
  1560.      * Helper function for directory creation
  1561.      *
  1562.      * @param string $dir
  1563.      * @param int $mode
  1564.      * @return bool
  1565.      * @access private
  1566.      */
  1567.     private function mkdir_helper($dir$mode)
  1568.     {
  1569.         // send SSH_FXP_MKDIR without any attributes (that's what the \0\0\0\0 is doing)
  1570.         if (!$this->send_sftp_packet(NET_SFTP_MKDIRStrings::packSSH2('s'$dir) . "\0\0\0\0")) {
  1571.             return false;
  1572.         }
  1573.         $response $this->get_sftp_packet();
  1574.         if ($this->packet_type != NET_SFTP_STATUS) {
  1575.             throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
  1576.                                               'Got packet type: ' $this->packet_type);
  1577.         }
  1578.         list($status) = Strings::unpackSSH2('N'$response);
  1579.         if ($status != NET_SFTP_STATUS_OK) {
  1580.             $this->logError($response$status);
  1581.             return false;
  1582.         }
  1583.         if ($mode !== -1) {
  1584.             $this->chmod($mode$dir);
  1585.         }
  1586.         return true;
  1587.     }
  1588.     /**
  1589.      * Removes a directory.
  1590.      *
  1591.      * @param string $dir
  1592.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1593.      * @return bool
  1594.      * @access public
  1595.      */
  1596.     public function rmdir($dir)
  1597.     {
  1598.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  1599.             return false;
  1600.         }
  1601.         $dir $this->realpath($dir);
  1602.         if ($dir === false) {
  1603.             return false;
  1604.         }
  1605.         if (!$this->send_sftp_packet(NET_SFTP_RMDIRStrings::packSSH2('s'$dir))) {
  1606.             return false;
  1607.         }
  1608.         $response $this->get_sftp_packet();
  1609.         if ($this->packet_type != NET_SFTP_STATUS) {
  1610.             throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
  1611.                                               'Got packet type: ' $this->packet_type);
  1612.         }
  1613.         list($status) = Strings::unpackSSH2('N'$response);
  1614.         if ($status != NET_SFTP_STATUS_OK) {
  1615.             // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
  1616.             $this->logError($response$status);
  1617.             return false;
  1618.         }
  1619.         $this->remove_from_stat_cache($dir);
  1620.         // the following will do a soft delete, which would be useful if you deleted a file
  1621.         // and then tried to do a stat on the deleted file. the above, in contrast, does
  1622.         // a hard delete
  1623.         //$this->update_stat_cache($dir, false);
  1624.         return true;
  1625.     }
  1626.     /**
  1627.      * Uploads a file to the SFTP server.
  1628.      *
  1629.      * By default, \phpseclib3\Net\SFTP::put() does not read from the local filesystem.  $data is dumped directly into $remote_file.
  1630.      * So, for example, if you set $data to 'filename.ext' and then do \phpseclib3\Net\SFTP::get(), you will get a file, twelve bytes
  1631.      * long, containing 'filename.ext' as its contents.
  1632.      *
  1633.      * Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior.  With self::SOURCE_LOCAL_FILE, $remote_file will
  1634.      * contain as many bytes as filename.ext does on your local filesystem.  If your filename.ext is 1MB then that is how
  1635.      * large $remote_file will be, as well.
  1636.      *
  1637.      * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number of bytes to return, and returns a string if there is some data or null if there is no more data
  1638.      *
  1639.      * If $data is a resource then it'll be used as a resource instead.
  1640.      *
  1641.      * Currently, only binary mode is supported.  As such, if the line endings need to be adjusted, you will need to take
  1642.      * care of that, yourself.
  1643.      *
  1644.      * $mode can take an additional two parameters - self::RESUME and self::RESUME_START. These are bitwise AND'd with
  1645.      * $mode. So if you want to resume upload of a 300mb file on the local file system you'd set $mode to the following:
  1646.      *
  1647.      * self::SOURCE_LOCAL_FILE | self::RESUME
  1648.      *
  1649.      * If you wanted to simply append the full contents of a local file to the full contents of a remote file you'd replace
  1650.      * self::RESUME with self::RESUME_START.
  1651.      *
  1652.      * If $mode & (self::RESUME | self::RESUME_START) then self::RESUME_START will be assumed.
  1653.      *
  1654.      * $start and $local_start give you more fine grained control over this process and take precident over self::RESUME
  1655.      * when they're non-negative. ie. $start could let you write at the end of a file (like self::RESUME) or in the middle
  1656.      * of one. $local_start could let you start your reading from the end of a file (like self::RESUME_START) or in the
  1657.      * middle of one.
  1658.      *
  1659.      * Setting $local_start to > 0 or $mode | self::RESUME_START doesn't do anything unless $mode | self::SOURCE_LOCAL_FILE.
  1660.      *
  1661.      * {@internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - \phpseclib3\Net\SFTP::setMode().}
  1662.      *
  1663.      * @param string $remote_file
  1664.      * @param string|resource $data
  1665.      * @param int $mode
  1666.      * @param int $start
  1667.      * @param int $local_start
  1668.      * @param callable|null $progressCallback
  1669.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1670.      * @throws \BadFunctionCallException if you're uploading via a callback and the callback function is invalid
  1671.      * @throws \phpseclib3\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist
  1672.      * @return bool
  1673.      * @access public
  1674.      */
  1675.     public function put($remote_file$data$mode self::SOURCE_STRING$start = -1$local_start = -1$progressCallback null)
  1676.     {
  1677.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  1678.             return false;
  1679.         }
  1680.         $remote_file $this->realpath($remote_file);
  1681.         if ($remote_file === false) {
  1682.             return false;
  1683.         }
  1684.         $flags NET_SFTP_OPEN_WRITE NET_SFTP_OPEN_CREATE;
  1685.         // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
  1686.         // in practice, it doesn't seem to do that.
  1687.         //$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
  1688.         if ($start >= 0) {
  1689.             $offset $start;
  1690.         } elseif ($mode self::RESUME) {
  1691.             // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called
  1692.             $size $this->stat($remote_file)['size'];
  1693.             $offset $size !== false $size 0;
  1694.         } else {
  1695.             $offset 0;
  1696.             $flags|= NET_SFTP_OPEN_TRUNCATE;
  1697.         }
  1698.         $this->remove_from_stat_cache($remote_file);
  1699.         $packet Strings::packSSH2('sNN'$remote_file$flags0);
  1700.         if (!$this->send_sftp_packet(NET_SFTP_OPEN$packet)) {
  1701.             return false;
  1702.         }
  1703.         $response $this->get_sftp_packet();
  1704.         switch ($this->packet_type) {
  1705.             case NET_SFTP_HANDLE:
  1706.                 $handle substr($response4);
  1707.                 break;
  1708.             case NET_SFTP_STATUS:
  1709.                 $this->logError($response);
  1710.                 return false;
  1711.             default:
  1712.                 throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
  1713.                                                   'Got packet type: ' $this->packet_type);
  1714.         }
  1715.         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
  1716.         $dataCallback false;
  1717.         switch (true) {
  1718.             case $mode self::SOURCE_CALLBACK:
  1719.                 if (!is_callable($data)) {
  1720.                     throw new \BadFunctionCallException("\$data should be is_callable() if you specify SOURCE_CALLBACK flag");
  1721.                 }
  1722.                 $dataCallback $data;
  1723.                 // do nothing
  1724.                 break;
  1725.             case is_resource($data):
  1726.                 $mode $mode & ~self::SOURCE_LOCAL_FILE;
  1727.                 $info stream_get_meta_data($data);
  1728.                 if ($info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') {
  1729.                     $fp fopen('php://memory''w+');
  1730.                     stream_copy_to_stream($data$fp);
  1731.                     rewind($fp);
  1732.                 } else {
  1733.                     $fp $data;
  1734.                 }
  1735.                 break;
  1736.             case $mode self::SOURCE_LOCAL_FILE:
  1737.                 if (!is_file($data)) {
  1738.                     throw new FileNotFoundException("$data is not a valid file");
  1739.                 }
  1740.                 $fp = @fopen($data'rb');
  1741.                 if (!$fp) {
  1742.                     return false;
  1743.                 }
  1744.         }
  1745.         if (isset($fp)) {
  1746.             $stat fstat($fp);
  1747.             $size = !empty($stat) ? $stat['size'] : 0;
  1748.             if ($local_start >= 0) {
  1749.                 fseek($fp$local_start);
  1750.                 $size-= $local_start;
  1751.             }
  1752.         } elseif ($dataCallback) {
  1753.             $size 0;
  1754.         } else {
  1755.             $size strlen($data);
  1756.         }
  1757.         $sent 0;
  1758.         $size $size ? ($size 0x7FFFFFFF) + 0x80000000 $size;
  1759.         $sftp_packet_size $this->max_sftp_packet;
  1760.         // make the SFTP packet be exactly the SFTP packet size by including the bytes in the NET_SFTP_WRITE packets "header"
  1761.         $sftp_packet_size-= strlen($handle) + 25;
  1762.         $i $j 0;
  1763.         while ($dataCallback || ($size === || $sent $size)) {
  1764.             if ($dataCallback) {
  1765.                 $temp $dataCallback($sftp_packet_size);
  1766.                 if (is_null($temp)) {
  1767.                     break;
  1768.                 }
  1769.             } else {
  1770.                 $temp = isset($fp) ? fread($fp$sftp_packet_size) : substr($data$sent$sftp_packet_size);
  1771.                 if ($temp === false || $temp === '') {
  1772.                     break;
  1773.                 }
  1774.             }
  1775.             $subtemp $offset $sent;
  1776.             $packet pack('Na*N3a*'strlen($handle), $handle$subtemp 4294967296$subtempstrlen($temp), $temp);
  1777.             if (!$this->send_sftp_packet(NET_SFTP_WRITE$packet$j)) {
  1778.                 if ($mode self::SOURCE_LOCAL_FILE) {
  1779.                     fclose($fp);
  1780.                 }
  1781.                 return false;
  1782.             }
  1783.             $sent+= strlen($temp);
  1784.             if (is_callable($progressCallback)) {
  1785.                 $progressCallback($sent);
  1786.             }
  1787.             $i++;
  1788.             $j++;
  1789.             if ($i == NET_SFTP_UPLOAD_QUEUE_SIZE) {
  1790.                 if (!$this->read_put_responses($i)) {
  1791.                     $i 0;
  1792.                     break;
  1793.                 }
  1794.                 $i 0;
  1795.             }
  1796.         }
  1797.         if (!$this->read_put_responses($i)) {
  1798.             if ($mode self::SOURCE_LOCAL_FILE) {
  1799.                 fclose($fp);
  1800.             }
  1801.             $this->close_handle($handle);
  1802.             return false;
  1803.         }
  1804.         if ($mode self::SOURCE_LOCAL_FILE) {
  1805.             if ($this->preserveTime) {
  1806.                 $stat fstat($fp);
  1807.                 $this->touch($remote_file$stat['mtime'], $stat['atime']);
  1808.             }
  1809.             if (isset($fp) && is_resource($fp)) {
  1810.                 fclose($fp);
  1811.             }
  1812.         }
  1813.         return $this->close_handle($handle);
  1814.     }
  1815.     /**
  1816.      * Reads multiple successive SSH_FXP_WRITE responses
  1817.      *
  1818.      * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i
  1819.      * SSH_FXP_WRITEs, in succession, and then reading $i responses.
  1820.      *
  1821.      * @param int $i
  1822.      * @return bool
  1823.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1824.      * @access private
  1825.      */
  1826.     private function read_put_responses($i)
  1827.     {
  1828.         while ($i--) {
  1829.             $response $this->get_sftp_packet();
  1830.             if ($this->packet_type != NET_SFTP_STATUS) {
  1831.                 throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
  1832.                                                   'Got packet type: ' $this->packet_type);
  1833.             }
  1834.             list($status) = Strings::unpackSSH2('N'$response);
  1835.             if ($status != NET_SFTP_STATUS_OK) {
  1836.                 $this->logError($response$status);
  1837.                 break;
  1838.             }
  1839.         }
  1840.         return $i 0;
  1841.     }
  1842.     /**
  1843.      * Close handle
  1844.      *
  1845.      * @param string $handle
  1846.      * @return bool
  1847.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1848.      * @access private
  1849.      */
  1850.     private function close_handle($handle)
  1851.     {
  1852.         if (!$this->send_sftp_packet(NET_SFTP_CLOSEpack('Na*'strlen($handle), $handle))) {
  1853.             return false;
  1854.         }
  1855.         // "The client MUST release all resources associated with the handle regardless of the status."
  1856.         //  -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
  1857.         $response $this->get_sftp_packet();
  1858.         if ($this->packet_type != NET_SFTP_STATUS) {
  1859.             throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
  1860.                                               'Got packet type: ' $this->packet_type);
  1861.         }
  1862.         list($status) = Strings::unpackSSH2('N'$response);
  1863.         if ($status != NET_SFTP_STATUS_OK) {
  1864.             $this->logError($response$status);
  1865.             return false;
  1866.         }
  1867.         return true;
  1868.     }
  1869.     /**
  1870.      * Downloads a file from the SFTP server.
  1871.      *
  1872.      * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
  1873.      * the operation was unsuccessful.  If $local_file is defined, returns true or false depending on the success of the
  1874.      * operation.
  1875.      *
  1876.      * $offset and $length can be used to download files in chunks.
  1877.      *
  1878.      * @param string $remote_file
  1879.      * @param string|bool|resource|callable $local_file
  1880.      * @param int $offset
  1881.      * @param int $length
  1882.      * @param callable|null $progressCallback
  1883.      * @throws \UnexpectedValueException on receipt of unexpected packets
  1884.      * @return mixed
  1885.      * @access public
  1886.      */
  1887.     public function get($remote_file$local_file false$offset 0$length = -1$progressCallback null)
  1888.     {
  1889.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  1890.             return false;
  1891.         }
  1892.         $remote_file $this->realpath($remote_file);
  1893.         if ($remote_file === false) {
  1894.             return false;
  1895.         }
  1896.         $packet pack('Na*N2'strlen($remote_file), $remote_fileNET_SFTP_OPEN_READ0);
  1897.         if (!$this->send_sftp_packet(NET_SFTP_OPEN$packet)) {
  1898.             return false;
  1899.         }
  1900.         $response $this->get_sftp_packet();
  1901.         switch ($this->packet_type) {
  1902.             case NET_SFTP_HANDLE:
  1903.                 $handle substr($response4);
  1904.                 break;
  1905.             case NET_SFTP_STATUS// presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
  1906.                 $this->logError($response);
  1907.                 return false;
  1908.             default:
  1909.                 throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
  1910.                                                   'Got packet type: ' $this->packet_type);
  1911.         }
  1912.         if (is_resource($local_file)) {
  1913.             $fp $local_file;
  1914.             $stat fstat($fp);
  1915.             $res_offset $stat['size'];
  1916.         } else {
  1917.             $res_offset 0;
  1918.             if ($local_file !== false && !is_callable($local_file)) {
  1919.                 $fp fopen($local_file'wb');
  1920.                 if (!$fp) {
  1921.                     return false;
  1922.                 }
  1923.             } else {
  1924.                 $content '';
  1925.             }
  1926.         }
  1927.         $fclose_check $local_file !== false && !is_callable($local_file) && !is_resource($local_file);
  1928.         $start $offset;
  1929.         $read 0;
  1930.         while (true) {
  1931.             $i 0;
  1932.             while ($i NET_SFTP_QUEUE_SIZE && ($length || $read $length)) {
  1933.                 $tempoffset $start $read;
  1934.                 $packet_size $length min($this->max_sftp_packet$length $read) : $this->max_sftp_packet;
  1935.                 $packet Strings::packSSH2('sN3'$handle$tempoffset 4294967296$tempoffset$packet_size);
  1936.                 if (!$this->send_sftp_packet(NET_SFTP_READ$packet$i)) {
  1937.                     if ($fclose_check) {
  1938.                         fclose($fp);
  1939.                     }
  1940.                     return false;
  1941.                 }
  1942.                 $packet null;
  1943.                 $read+= $packet_size;
  1944.                 $i++;
  1945.             }
  1946.             if (!$i) {
  1947.                 break;
  1948.             }
  1949.             $packets_sent $i 1;
  1950.             $clear_responses false;
  1951.             while ($i 0) {
  1952.                 $i--;
  1953.                 if ($clear_responses) {
  1954.                     $this->get_sftp_packet($packets_sent $i);
  1955.                     continue;
  1956.                 } else {
  1957.                     $response $this->get_sftp_packet($packets_sent $i);
  1958.                 }
  1959.                 switch ($this->packet_type) {
  1960.                     case NET_SFTP_DATA:
  1961.                         $temp substr($response4);
  1962.                         $offset+= strlen($temp);
  1963.                         if ($local_file === false) {
  1964.                             $content.= $temp;
  1965.                         } elseif (is_callable($local_file)) {
  1966.                             $local_file($temp);
  1967.                         } else {
  1968.                             fputs($fp$temp);
  1969.                         }
  1970.                         if (is_callable($progressCallback)) {
  1971.                             call_user_func($progressCallback$offset);
  1972.                         }
  1973.                         $temp null;
  1974.                         break;
  1975.                     case NET_SFTP_STATUS:
  1976.                         // could, in theory, return false if !strlen($content) but we'll hold off for the time being
  1977.                         $this->logError($response);
  1978.                         $clear_responses true// don't break out of the loop yet, so we can read the remaining responses
  1979.                         break;
  1980.                     default:
  1981.                         if ($fclose_check) {
  1982.                             fclose($fp);
  1983.                         }
  1984.                         throw new \UnexpectedValueException('Expected NET_SFTP_DATA or NET_SFTP_STATUS. '
  1985.                                                           'Got packet type: ' $this->packet_type);
  1986.                 }
  1987.                 $response null;
  1988.             }
  1989.             if ($clear_responses) {
  1990.                 break;
  1991.             }
  1992.         }
  1993.         if ($length && $length <= $offset $start) {
  1994.             if ($local_file === false) {
  1995.                 $content substr($content0$length);
  1996.             } else {
  1997.                 ftruncate($fp$length $res_offset);
  1998.             }
  1999.         }
  2000.         if ($fclose_check) {
  2001.             fclose($fp);
  2002.             if ($this->preserveTime) {
  2003.                 $stat $this->stat($remote_file);
  2004.                 touch($local_file$stat['mtime'], $stat['atime']);
  2005.             }
  2006.         }
  2007.         if (!$this->close_handle($handle)) {
  2008.             return false;
  2009.         }
  2010.         // if $content isn't set that means a file was written to
  2011.         return isset($content) ? $content true;
  2012.     }
  2013.     /**
  2014.      * Deletes a file on the SFTP server.
  2015.      *
  2016.      * @param string $path
  2017.      * @param bool $recursive
  2018.      * @return bool
  2019.      * @throws \UnexpectedValueException on receipt of unexpected packets
  2020.      * @access public
  2021.      */
  2022.     public function delete($path$recursive true)
  2023.     {
  2024.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  2025.             return false;
  2026.         }
  2027.         if (is_object($path)) {
  2028.             // It's an object. Cast it as string before we check anything else.
  2029.             $path = (string) $path;
  2030.         }
  2031.         if (!is_string($path) || $path == '') {
  2032.             return false;
  2033.         }
  2034.         $path $this->realpath($path);
  2035.         if ($path === false) {
  2036.             return false;
  2037.         }
  2038.         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
  2039.         if (!$this->send_sftp_packet(NET_SFTP_REMOVEpack('Na*'strlen($path), $path))) {
  2040.             return false;
  2041.         }
  2042.         $response $this->get_sftp_packet();
  2043.         if ($this->packet_type != NET_SFTP_STATUS) {
  2044.             throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
  2045.                                               'Got packet type: ' $this->packet_type);
  2046.         }
  2047.         // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
  2048.         list($status) = Strings::unpackSSH2('N'$response);
  2049.         if ($status != NET_SFTP_STATUS_OK) {
  2050.             $this->logError($response$status);
  2051.             if (!$recursive) {
  2052.                 return false;
  2053.             }
  2054.             $i 0;
  2055.             $result $this->delete_recursive($path$i);
  2056.             $this->read_put_responses($i);
  2057.             return $result;
  2058.         }
  2059.         $this->remove_from_stat_cache($path);
  2060.         return true;
  2061.     }
  2062.     /**
  2063.      * Recursively deletes directories on the SFTP server
  2064.      *
  2065.      * Minimizes directory lookups and SSH_FXP_STATUS requests for speed.
  2066.      *
  2067.      * @param string $path
  2068.      * @param int $i
  2069.      * @return bool
  2070.      * @access private
  2071.      */
  2072.     private function delete_recursive($path, &$i)
  2073.     {
  2074.         if (!$this->read_put_responses($i)) {
  2075.             return false;
  2076.         }
  2077.         $i 0;
  2078.         $entries $this->readlist($pathtrue);
  2079.         // normally $entries would have at least . and .. but it might not if the directories
  2080.         // permissions didn't allow reading
  2081.         if (empty($entries)) {
  2082.             return false;
  2083.         }
  2084.         unset($entries['.'], $entries['..']);
  2085.         foreach ($entries as $filename => $props) {
  2086.             if (!isset($props['type'])) {
  2087.                 return false;
  2088.             }
  2089.             $temp $path '/' $filename;
  2090.             if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
  2091.                 if (!$this->delete_recursive($temp$i)) {
  2092.                     return false;
  2093.                 }
  2094.             } else {
  2095.                 if (!$this->send_sftp_packet(NET_SFTP_REMOVEStrings::packSSH2('s'$temp))) {
  2096.                     return false;
  2097.                 }
  2098.                 $this->remove_from_stat_cache($temp);
  2099.                 $i++;
  2100.                 if ($i >= NET_SFTP_QUEUE_SIZE) {
  2101.                     if (!$this->read_put_responses($i)) {
  2102.                         return false;
  2103.                     }
  2104.                     $i 0;
  2105.                 }
  2106.             }
  2107.         }
  2108.         if (!$this->send_sftp_packet(NET_SFTP_RMDIRStrings::packSSH2('s'$path))) {
  2109.             return false;
  2110.         }
  2111.         $this->remove_from_stat_cache($path);
  2112.         $i++;
  2113.         if ($i >= NET_SFTP_QUEUE_SIZE) {
  2114.             if (!$this->read_put_responses($i)) {
  2115.                 return false;
  2116.             }
  2117.             $i 0;
  2118.         }
  2119.         return true;
  2120.     }
  2121.     /**
  2122.      * Checks whether a file or directory exists
  2123.      *
  2124.      * @param string $path
  2125.      * @return bool
  2126.      * @access public
  2127.      */
  2128.     public function file_exists($path)
  2129.     {
  2130.         if ($this->use_stat_cache) {
  2131.             $path $this->realpath($path);
  2132.             $result $this->query_stat_cache($path);
  2133.             if (isset($result)) {
  2134.                 // return true if $result is an array or if it's an stdClass object
  2135.                 return $result !== false;
  2136.             }
  2137.         }
  2138.         return $this->stat($path) !== false;
  2139.     }
  2140.     /**
  2141.      * Tells whether the filename is a directory
  2142.      *
  2143.      * @param string $path
  2144.      * @return bool
  2145.      * @access public
  2146.      */
  2147.     public function is_dir($path)
  2148.     {
  2149.         $result $this->get_stat_cache_prop($path'type');
  2150.         if ($result === false) {
  2151.             return false;
  2152.         }
  2153.         return $result === NET_SFTP_TYPE_DIRECTORY;
  2154.     }
  2155.     /**
  2156.      * Tells whether the filename is a regular file
  2157.      *
  2158.      * @param string $path
  2159.      * @return bool
  2160.      * @access public
  2161.      */
  2162.     public function is_file($path)
  2163.     {
  2164.         $result $this->get_stat_cache_prop($path'type');
  2165.         if ($result === false) {
  2166.             return false;
  2167.         }
  2168.         return $result === NET_SFTP_TYPE_REGULAR;
  2169.     }
  2170.     /**
  2171.      * Tells whether the filename is a symbolic link
  2172.      *
  2173.      * @param string $path
  2174.      * @return bool
  2175.      * @access public
  2176.      */
  2177.     public function is_link($path)
  2178.     {
  2179.         $result $this->get_lstat_cache_prop($path'type');
  2180.         if ($result === false) {
  2181.             return false;
  2182.         }
  2183.         return $result === NET_SFTP_TYPE_SYMLINK;
  2184.     }
  2185.     /**
  2186.      * Tells whether a file exists and is readable
  2187.      *
  2188.      * @param string $path
  2189.      * @return bool
  2190.      * @access public
  2191.      */
  2192.     public function is_readable($path)
  2193.     {
  2194.         $packet Strings::packSSH2('sNN'$this->realpath($path), NET_SFTP_OPEN_READ0);
  2195.         if (!$this->send_sftp_packet(NET_SFTP_OPEN$packet)) {
  2196.             return false;
  2197.         }
  2198.         $response $this->get_sftp_packet();
  2199.         switch ($this->packet_type) {
  2200.             case NET_SFTP_HANDLE:
  2201.                 return true;
  2202.             case NET_SFTP_STATUS// presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
  2203.                 return false;
  2204.             default:
  2205.                 throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
  2206.                                                   'Got packet type: ' $this->packet_type);
  2207.         }
  2208.     }
  2209.     /**
  2210.      * Tells whether the filename is writable
  2211.      *
  2212.      * @param string $path
  2213.      * @return bool
  2214.      * @access public
  2215.      */
  2216.     public function is_writable($path)
  2217.     {
  2218.         $packet Strings::packSSH2('sNN'$this->realpath($path), NET_SFTP_OPEN_WRITE0);
  2219.         if (!$this->send_sftp_packet(NET_SFTP_OPEN$packet)) {
  2220.             return false;
  2221.         }
  2222.         $response $this->get_sftp_packet();
  2223.         switch ($this->packet_type) {
  2224.             case NET_SFTP_HANDLE:
  2225.                 return true;
  2226.             case NET_SFTP_STATUS// presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
  2227.                 return false;
  2228.             default:
  2229.                 throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS. '
  2230.                                                   'Got packet type: ' $this->packet_type);
  2231.         }
  2232.     }
  2233.     /**
  2234.      * Tells whether the filename is writeable
  2235.      *
  2236.      * Alias of is_writable
  2237.      *
  2238.      * @param string $path
  2239.      * @return bool
  2240.      * @access public
  2241.      */
  2242.     public function is_writeable($path)
  2243.     {
  2244.         return $this->is_writable($path);
  2245.     }
  2246.     /**
  2247.      * Gets last access time of file
  2248.      *
  2249.      * @param string $path
  2250.      * @return mixed
  2251.      * @access public
  2252.      */
  2253.     public function fileatime($path)
  2254.     {
  2255.         return $this->get_stat_cache_prop($path'atime');
  2256.     }
  2257.     /**
  2258.      * Gets file modification time
  2259.      *
  2260.      * @param string $path
  2261.      * @return mixed
  2262.      * @access public
  2263.      */
  2264.     public function filemtime($path)
  2265.     {
  2266.         return $this->get_stat_cache_prop($path'mtime');
  2267.     }
  2268.     /**
  2269.      * Gets file permissions
  2270.      *
  2271.      * @param string $path
  2272.      * @return mixed
  2273.      * @access public
  2274.      */
  2275.     public function fileperms($path)
  2276.     {
  2277.         return $this->get_stat_cache_prop($path'mode');
  2278.     }
  2279.     /**
  2280.      * Gets file owner
  2281.      *
  2282.      * @param string $path
  2283.      * @return mixed
  2284.      * @access public
  2285.      */
  2286.     public function fileowner($path)
  2287.     {
  2288.         return $this->get_stat_cache_prop($path'uid');
  2289.     }
  2290.     /**
  2291.      * Gets file group
  2292.      *
  2293.      * @param string $path
  2294.      * @return mixed
  2295.      * @access public
  2296.      */
  2297.     public function filegroup($path)
  2298.     {
  2299.         return $this->get_stat_cache_prop($path'gid');
  2300.     }
  2301.     /**
  2302.      * Gets file size
  2303.      *
  2304.      * @param string $path
  2305.      * @return mixed
  2306.      * @access public
  2307.      */
  2308.     public function filesize($path)
  2309.     {
  2310.         return $this->get_stat_cache_prop($path'size');
  2311.     }
  2312.     /**
  2313.      * Gets file type
  2314.      *
  2315.      * @param string $path
  2316.      * @return mixed
  2317.      * @access public
  2318.      */
  2319.     public function filetype($path)
  2320.     {
  2321.         $type $this->get_stat_cache_prop($path'type');
  2322.         if ($type === false) {
  2323.             return false;
  2324.         }
  2325.         switch ($type) {
  2326.             case NET_SFTP_TYPE_BLOCK_DEVICE:
  2327.                 return 'block';
  2328.             case NET_SFTP_TYPE_CHAR_DEVICE:
  2329.                 return 'char';
  2330.             case NET_SFTP_TYPE_DIRECTORY:
  2331.                 return 'dir';
  2332.             case NET_SFTP_TYPE_FIFO:
  2333.                 return 'fifo';
  2334.             case NET_SFTP_TYPE_REGULAR:
  2335.                 return 'file';
  2336.             case NET_SFTP_TYPE_SYMLINK:
  2337.                 return 'link';
  2338.             default:
  2339.                 return false;
  2340.         }
  2341.     }
  2342.     /**
  2343.      * Return a stat properity
  2344.      *
  2345.      * Uses cache if appropriate.
  2346.      *
  2347.      * @param string $path
  2348.      * @param string $prop
  2349.      * @return mixed
  2350.      * @access private
  2351.      */
  2352.     private function get_stat_cache_prop($path$prop)
  2353.     {
  2354.         return $this->get_xstat_cache_prop($path$prop'stat');
  2355.     }
  2356.     /**
  2357.      * Return an lstat properity
  2358.      *
  2359.      * Uses cache if appropriate.
  2360.      *
  2361.      * @param string $path
  2362.      * @param string $prop
  2363.      * @return mixed
  2364.      * @access private
  2365.      */
  2366.     private function get_lstat_cache_prop($path$prop)
  2367.     {
  2368.         return $this->get_xstat_cache_prop($path$prop'lstat');
  2369.     }
  2370.     /**
  2371.      * Return a stat or lstat properity
  2372.      *
  2373.      * Uses cache if appropriate.
  2374.      *
  2375.      * @param string $path
  2376.      * @param string $prop
  2377.      * @param string $type
  2378.      * @return mixed
  2379.      * @access private
  2380.      */
  2381.     private function get_xstat_cache_prop($path$prop$type)
  2382.     {
  2383.         if ($this->use_stat_cache) {
  2384.             $path $this->realpath($path);
  2385.             $result $this->query_stat_cache($path);
  2386.             if (is_object($result) && isset($result->$type)) {
  2387.                 return $result->{$type}[$prop];
  2388.             }
  2389.         }
  2390.         $result $this->$type($path);
  2391.         if ($result === false || !isset($result[$prop])) {
  2392.             return false;
  2393.         }
  2394.         return $result[$prop];
  2395.     }
  2396.     /**
  2397.      * Renames a file or a directory on the SFTP server
  2398.      *
  2399.      * @param string $oldname
  2400.      * @param string $newname
  2401.      * @return bool
  2402.      * @throws \UnexpectedValueException on receipt of unexpected packets
  2403.      * @access public
  2404.      */
  2405.     public function rename($oldname$newname)
  2406.     {
  2407.         if (!($this->bitmap SSH2::MASK_LOGIN)) {
  2408.             return false;
  2409.         }
  2410.         $oldname $this->realpath($oldname);
  2411.         $newname $this->realpath($newname);
  2412.         if ($oldname === false || $newname === false) {
  2413.             return false;
  2414.         }
  2415.         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
  2416.         $packet Strings::packSSH2('ss'$oldname$newname);
  2417.         if (!$this->send_sftp_packet(NET_SFTP_RENAME$packet)) {
  2418.             return false;
  2419.         }
  2420.         $response $this->get_sftp_packet();
  2421.         if ($this->packet_type != NET_SFTP_STATUS) {
  2422.             throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
  2423.                                               'Got packet type: ' $this->packet_type);
  2424.         }
  2425.         // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
  2426.         list($status) = Strings::unpackSSH2('N'$response);
  2427.         if ($status != NET_SFTP_STATUS_OK) {
  2428.             $this->logError($response$status);
  2429.             return false;
  2430.         }
  2431.         // don't move the stat cache entry over since this operation could very well change the
  2432.         // atime and mtime attributes
  2433.         //$this->update_stat_cache($newname, $this->query_stat_cache($oldname));
  2434.         $this->remove_from_stat_cache($oldname);
  2435.         $this->remove_from_stat_cache($newname);
  2436.         return true;
  2437.     }
  2438.     /**
  2439.      * Parse Attributes
  2440.      *
  2441.      * See '7.  File Attributes' of draft-ietf-secsh-filexfer-13 for more info.
  2442.      *
  2443.      * @param string $response
  2444.      * @return array
  2445.      * @access private
  2446.      */
  2447.     protected function parseAttributes(&$response)
  2448.     {
  2449.         $attr = [];
  2450.         list($flags) = Strings::unpackSSH2('N'$response);
  2451.         // SFTPv4+ have a type field (a byte) that follows the above flag field
  2452.         foreach ($this->attributes as $key => $value) {
  2453.             switch ($flags $key) {
  2454.                 case NET_SFTP_ATTR_SIZE// 0x00000001
  2455.                     // The size attribute is defined as an unsigned 64-bit integer.
  2456.                     // The following will use floats on 32-bit platforms, if necessary.
  2457.                     // As can be seen in the BigInteger class, floats are generally
  2458.                     // IEEE 754 binary64 "double precision" on such platforms and
  2459.                     // as such can represent integers of at least 2^50 without loss
  2460.                     // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
  2461.                     list($upper$size) = Strings::unpackSSH2('NN'$response);
  2462.                     $attr['size'] = $upper 4294967296 $upper 0;
  2463.                     $attr['size']+= $size ? ($size 0x7FFFFFFF) + 0x80000000 $size;
  2464.                     break;
  2465.                 case NET_SFTP_ATTR_UIDGID// 0x00000002 (SFTPv3 only)
  2466.                     list($attr['uid'], $attr['gid']) = Strings::unpackSSH2('NN'$response);
  2467.                     break;
  2468.                 case NET_SFTP_ATTR_PERMISSIONS// 0x00000004
  2469.                     list($attr['mode']) = Strings::unpackSSH2('N'$response);
  2470.                     $fileType $this->parseMode($attr['mode']);
  2471.                     if ($fileType !== false) {
  2472.                         $attr+= ['type' => $fileType];
  2473.                     }
  2474.                     break;
  2475.                 case NET_SFTP_ATTR_ACCESSTIME// 0x00000008
  2476.                     list($attr['atime'], $attr['mtime']) = Strings::unpackSSH2('NN'$response);
  2477.                     break;
  2478.                 case NET_SFTP_ATTR_EXTENDED// 0x80000000
  2479.                     list($count) = Strings::unpackSSH2('N'$response);
  2480.                     for ($i 0$i $count$i++) {
  2481.                         list($key$value) = Strings::unpackSSH2('ss'$response);
  2482.                         $attr[$key] = $value;
  2483.                     }
  2484.             }
  2485.         }
  2486.         return $attr;
  2487.     }
  2488.     /**
  2489.      * Attempt to identify the file type
  2490.      *
  2491.      * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway
  2492.      *
  2493.      * @param int $mode
  2494.      * @return int
  2495.      * @access private
  2496.      */
  2497.     private function parseMode($mode)
  2498.     {
  2499.         // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12
  2500.         // see, also, http://linux.die.net/man/2/stat
  2501.         switch ($mode 0170000) {// ie. 1111 0000 0000 0000
  2502.             case 0000000// no file type specified - figure out the file type using alternative means
  2503.                 return false;
  2504.             case 0040000:
  2505.                 return NET_SFTP_TYPE_DIRECTORY;
  2506.             case 0100000:
  2507.                 return NET_SFTP_TYPE_REGULAR;
  2508.             case 0120000:
  2509.                 return NET_SFTP_TYPE_SYMLINK;
  2510.             // new types introduced in SFTPv5+
  2511.             // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
  2512.             case 0010000// named pipe (fifo)
  2513.                 return NET_SFTP_TYPE_FIFO;
  2514.             case 0020000// character special
  2515.                 return NET_SFTP_TYPE_CHAR_DEVICE;
  2516.             case 0060000// block special
  2517.                 return NET_SFTP_TYPE_BLOCK_DEVICE;
  2518.             case 0140000// socket
  2519.                 return NET_SFTP_TYPE_SOCKET;
  2520.             case 0160000// whiteout
  2521.                 // "SPECIAL should be used for files that are of
  2522.                 //  a known type which cannot be expressed in the protocol"
  2523.                 return NET_SFTP_TYPE_SPECIAL;
  2524.             default:
  2525.                 return NET_SFTP_TYPE_UNKNOWN;
  2526.         }
  2527.     }
  2528.     /**
  2529.      * Parse Longname
  2530.      *
  2531.      * SFTPv3 doesn't provide any easy way of identifying a file type.  You could try to open
  2532.      * a file as a directory and see if an error is returned or you could try to parse the
  2533.      * SFTPv3-specific longname field of the SSH_FXP_NAME packet.  That's what this function does.
  2534.      * The result is returned using the
  2535.      * {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}.
  2536.      *
  2537.      * If the longname is in an unrecognized format bool(false) is returned.
  2538.      *
  2539.      * @param string $longname
  2540.      * @return mixed
  2541.      * @access private
  2542.      */
  2543.     private function parseLongname($longname)
  2544.     {
  2545.         // http://en.wikipedia.org/wiki/Unix_file_types
  2546.         // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions
  2547.         if (preg_match('#^[^/]([r-][w-][xstST-]){3}#'$longname)) {
  2548.             switch ($longname[0]) {
  2549.                 case '-':
  2550.                     return NET_SFTP_TYPE_REGULAR;
  2551.                 case 'd':
  2552.                     return NET_SFTP_TYPE_DIRECTORY;
  2553.                 case 'l':
  2554.                     return NET_SFTP_TYPE_SYMLINK;
  2555.                 default:
  2556.                     return NET_SFTP_TYPE_SPECIAL;
  2557.             }
  2558.         }
  2559.         return false;
  2560.     }
  2561.     /**
  2562.      * Sends SFTP Packets
  2563.      *
  2564.      * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info.
  2565.      *
  2566.      * @param int $type
  2567.      * @param string $data
  2568.      * @param int $request_id
  2569.      * @see self::_get_sftp_packet()
  2570.      * @see self::send_channel_packet()
  2571.      * @return bool
  2572.      * @access private
  2573.      */
  2574.     private function send_sftp_packet($type$data$request_id 1)
  2575.     {
  2576.         // in SSH2.php the timeout is cumulative per function call. eg. exec() will
  2577.         // timeout after 10s. but for SFTP.php it's cumulative per packet
  2578.         $this->curTimeout $this->timeout;
  2579.         $packet $this->use_request_id ?
  2580.             pack('NCNa*'strlen($data) + 5$type$request_id$data) :
  2581.             pack('NCa*',  strlen($data) + 1$type$data);
  2582.         $start microtime(true);
  2583.         $result $this->send_channel_packet(self::CHANNEL$packet);
  2584.         $stop microtime(true);
  2585.         if (defined('NET_SFTP_LOGGING')) {
  2586.             $packet_type '-> ' $this->packet_types[$type] .
  2587.                            ' (' round($stop $start4) . 's)';
  2588.             if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
  2589.                 switch (PHP_SAPI) {
  2590.                     case 'cli':
  2591.                         $start $stop "\r\n";
  2592.                         break;
  2593.                     default:
  2594.                         $start '<pre>';
  2595.                         $stop '</pre>';
  2596.                 }
  2597.                 echo $start $this->format_log([$data], [$packet_type]) . $stop;
  2598.                 @flush();
  2599.                 @ob_flush();
  2600.             } else {
  2601.                 $this->packet_type_log[] = $packet_type;
  2602.                 if (NET_SFTP_LOGGING == self::LOG_COMPLEX) {
  2603.                     $this->packet_log[] = $data;
  2604.                 }
  2605.             }
  2606.         }
  2607.         return $result;
  2608.     }
  2609.     /**
  2610.      * Resets a connection for re-use
  2611.      *
  2612.      * @param int $reason
  2613.      * @access private
  2614.      */
  2615.     protected function reset_connection($reason)
  2616.     {
  2617.         parent::reset_connection($reason);
  2618.         $this->use_request_id false;
  2619.         $this->pwd false;
  2620.         $this->requestBuffer = [];
  2621.     }
  2622.     /**
  2623.      * Receives SFTP Packets
  2624.      *
  2625.      * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info.
  2626.      *
  2627.      * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present.
  2628.      * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA
  2629.      * messages containing one SFTP packet.
  2630.      *
  2631.      * @see self::_send_sftp_packet()
  2632.      * @return string
  2633.      * @access private
  2634.      */
  2635.     private function get_sftp_packet($request_id null)
  2636.     {
  2637.         if (isset($request_id) && isset($this->requestBuffer[$request_id])) {
  2638.             $this->packet_type $this->requestBuffer[$request_id]['packet_type'];
  2639.             $temp $this->requestBuffer[$request_id]['packet'];
  2640.             unset($this->requestBuffer[$request_id]);
  2641.             return $temp;
  2642.         }
  2643.         // in SSH2.php the timeout is cumulative per function call. eg. exec() will
  2644.         // timeout after 10s. but for SFTP.php it's cumulative per packet
  2645.         $this->curTimeout $this->timeout;
  2646.         $start microtime(true);
  2647.         // SFTP packet length
  2648.         while (strlen($this->packet_buffer) < 4) {
  2649.             $temp $this->get_channel_packet(self::CHANNELtrue);
  2650.             if (is_bool($temp)) {
  2651.                 $this->packet_type false;
  2652.                 $this->packet_buffer '';
  2653.                 return false;
  2654.             }
  2655.             $this->packet_buffer.= $temp;
  2656.         }
  2657.         if (strlen($this->packet_buffer) < 4) {
  2658.             return false;
  2659.         }
  2660.         extract(unpack('Nlength'Strings::shift($this->packet_buffer4)));
  2661.         /** @var integer $length */
  2662.         $tempLength $length;
  2663.         $tempLength-= strlen($this->packet_buffer);
  2664.         // 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h
  2665.         if ($tempLength 256 1024) {
  2666.             throw new \RuntimeException('Invalid Size');
  2667.         }
  2668.         // SFTP packet type and data payload
  2669.         while ($tempLength 0) {
  2670.             $temp $this->get_channel_packet(self::CHANNELtrue);
  2671.             if (is_bool($temp)) {
  2672.                 $this->packet_type false;
  2673.                 $this->packet_buffer '';
  2674.                 return false;
  2675.             }
  2676.             $this->packet_buffer.= $temp;
  2677.             $tempLength-= strlen($temp);
  2678.         }
  2679.         $stop microtime(true);
  2680.         $this->packet_type ord(Strings::shift($this->packet_buffer));
  2681.         if ($this->use_request_id) {
  2682.             extract(unpack('Npacket_id'Strings::shift($this->packet_buffer4))); // remove the request id
  2683.             $length-= 5// account for the request id and the packet type
  2684.         } else {
  2685.             $length-= 1// account for the packet type
  2686.         }
  2687.         $packet Strings::shift($this->packet_buffer$length);
  2688.         if (defined('NET_SFTP_LOGGING')) {
  2689.             $packet_type '<- ' $this->packet_types[$this->packet_type] .
  2690.                            ' (' round($stop $start4) . 's)';
  2691.             if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
  2692.                 switch (PHP_SAPI) {
  2693.                     case 'cli':
  2694.                         $start $stop "\r\n";
  2695.                         break;
  2696.                     default:
  2697.                         $start '<pre>';
  2698.                         $stop '</pre>';
  2699.                 }
  2700.                 echo $start $this->format_log([$packet], [$packet_type]) . $stop;
  2701.                 @flush();
  2702.                 @ob_flush();
  2703.             } else {
  2704.                 $this->packet_type_log[] = $packet_type;
  2705.                 if (NET_SFTP_LOGGING == self::LOG_COMPLEX) {
  2706.                     $this->packet_log[] = $packet;
  2707.                 }
  2708.             }
  2709.         }
  2710.         if (isset($request_id) && $this->use_request_id && $packet_id != $request_id) {
  2711.             $this->requestBuffer[$packet_id] = array(
  2712.                 'packet_type' => $this->packet_type,
  2713.                 'packet' => $packet
  2714.             );
  2715.             return $this->get_sftp_packet($request_id);
  2716.         }
  2717.         return $packet;
  2718.     }
  2719.     /**
  2720.      * Returns a log of the packets that have been sent and received.
  2721.      *
  2722.      * Returns a string if NET_SFTP_LOGGING == self::LOG_COMPLEX, an array if NET_SFTP_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING')
  2723.      *
  2724.      * @access public
  2725.      * @return array|string
  2726.      */
  2727.     public function getSFTPLog()
  2728.     {
  2729.         if (!defined('NET_SFTP_LOGGING')) {
  2730.             return false;
  2731.         }
  2732.         switch (NET_SFTP_LOGGING) {
  2733.             case self::LOG_COMPLEX:
  2734.                 return $this->format_log($this->packet_log$this->packet_type_log);
  2735.                 break;
  2736.             //case self::LOG_SIMPLE:
  2737.             default:
  2738.                 return $this->packet_type_log;
  2739.         }
  2740.     }
  2741.     /**
  2742.      * Returns all errors
  2743.      *
  2744.      * @return array
  2745.      * @access public
  2746.      */
  2747.     public function getSFTPErrors()
  2748.     {
  2749.         return $this->sftp_errors;
  2750.     }
  2751.     /**
  2752.      * Returns the last error
  2753.      *
  2754.      * @return string
  2755.      * @access public
  2756.      */
  2757.     public function getLastSFTPError()
  2758.     {
  2759.         return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : '';
  2760.     }
  2761.     /**
  2762.      * Get supported SFTP versions
  2763.      *
  2764.      * @return array
  2765.      * @access public
  2766.      */
  2767.     public function getSupportedVersions()
  2768.     {
  2769.         $temp = ['version' => $this->version];
  2770.         if (isset($this->extensions['versions'])) {
  2771.             $temp['extensions'] = $this->extensions['versions'];
  2772.         }
  2773.         return $temp;
  2774.     }
  2775.     /**
  2776.      * Disconnect
  2777.      *
  2778.      * @param int $reason
  2779.      * @return bool
  2780.      * @access protected
  2781.      */
  2782.     protected function disconnect_helper($reason)
  2783.     {
  2784.         $this->pwd false;
  2785.         parent::disconnect_helper($reason);
  2786.     }
  2787.     /**
  2788.      * Enable Date Preservation
  2789.      *
  2790.      * @access public
  2791.      */
  2792.     function enableDatePreservation()
  2793.     {
  2794.         $this->preserveTime true;
  2795.     }
  2796.     /**
  2797.      * Disable Date Preservation
  2798.      *
  2799.      * @access public
  2800.      */
  2801.     function disableDatePreservation()
  2802.     {
  2803.         $this->preserveTime false;
  2804.     }
  2805. }