[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/ -> Stream.php (source)

   1  <?php
   2  
   3  /**
   4   * SFTP Stream Wrapper
   5   *
   6   * Creates an sftp:// protocol handler that can be used with, for example, fopen(), dir(), etc.
   7   *
   8   * PHP version 5
   9   *
  10   * @category  Net
  11   * @package   SFTP
  12   * @author    Jim Wigginton <[email protected]>
  13   * @copyright 2013 Jim Wigginton
  14   * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  15   * @link      http://phpseclib.sourceforge.net
  16   */
  17  
  18  namespace phpseclib3\Net\SFTP;
  19  
  20  use phpseclib3\Crypt\Common\PrivateKey;
  21  use phpseclib3\Net\SFTP;
  22  use phpseclib3\Net\SSH2;
  23  
  24  /**
  25   * SFTP Stream Wrapper
  26   *
  27   * @package SFTP
  28   * @author  Jim Wigginton <[email protected]>
  29   * @access  public
  30   */
  31  class Stream
  32  {
  33      /**
  34       * SFTP instances
  35       *
  36       * Rather than re-create the connection we re-use instances if possible
  37       *
  38       * @var array
  39       */
  40      public static $instances;
  41  
  42      /**
  43       * SFTP instance
  44       *
  45       * @var object
  46       * @access private
  47       */
  48      private $sftp;
  49  
  50      /**
  51       * Path
  52       *
  53       * @var string
  54       * @access private
  55       */
  56      private $path;
  57  
  58      /**
  59       * Mode
  60       *
  61       * @var string
  62       * @access private
  63       */
  64      private $mode;
  65  
  66      /**
  67       * Position
  68       *
  69       * @var int
  70       * @access private
  71       */
  72      private $pos;
  73  
  74      /**
  75       * Size
  76       *
  77       * @var int
  78       * @access private
  79       */
  80      private $size;
  81  
  82      /**
  83       * Directory entries
  84       *
  85       * @var array
  86       * @access private
  87       */
  88      private $entries;
  89  
  90      /**
  91       * EOF flag
  92       *
  93       * @var bool
  94       * @access private
  95       */
  96      private $eof;
  97  
  98      /**
  99       * Context resource
 100       *
 101       * Technically this needs to be publicly accessible so PHP can set it directly
 102       *
 103       * @var resource
 104       * @access public
 105       */
 106      public $context;
 107  
 108      /**
 109       * Notification callback function
 110       *
 111       * @var callable
 112       * @access public
 113       */
 114      private $notification;
 115  
 116      /**
 117       * Registers this class as a URL wrapper.
 118       *
 119       * @param string $protocol The wrapper name to be registered.
 120       * @return bool True on success, false otherwise.
 121       * @access public
 122       */
 123      public static function register($protocol = 'sftp')
 124      {
 125          if (in_array($protocol, stream_get_wrappers(), true)) {
 126              return false;
 127          }
 128          return stream_wrapper_register($protocol, get_called_class());
 129      }
 130  
 131      /**
 132       * The Constructor
 133       *
 134       * @access public
 135       */
 136      public function __construct()
 137      {
 138          if (defined('NET_SFTP_STREAM_LOGGING')) {
 139              echo "__construct()\r\n";
 140          }
 141      }
 142  
 143      /**
 144       * Path Parser
 145       *
 146       * Extract a path from a URI and actually connect to an SSH server if appropriate
 147       *
 148       * If "notification" is set as a context parameter the message code for successful login is
 149       * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE.
 150       *
 151       * @param string $path
 152       * @return string
 153       * @access private
 154       */
 155      protected function parse_path($path)
 156      {
 157          $orig = $path;
 158          extract(parse_url($path) + ['port' => 22]);
 159          if (isset($query)) {
 160              $path .= '?' . $query;
 161          } elseif (preg_match('/(\?|\?#)$/', $orig)) {
 162              $path .= '?';
 163          }
 164          if (isset($fragment)) {
 165              $path .= '#' . $fragment;
 166          } elseif ($orig[strlen($orig) - 1] == '#') {
 167              $path .= '#';
 168          }
 169  
 170          if (!isset($host)) {
 171              return false;
 172          }
 173  
 174          if (isset($this->context)) {
 175              $context = stream_context_get_params($this->context);
 176              if (isset($context['notification'])) {
 177                  $this->notification = $context['notification'];
 178              }
 179          }
 180  
 181          if (preg_match('/^{[a-z0-9]+}$/i', $host)) {
 182              $host = SSH2::getConnectionByResourceId($host);
 183              if ($host === false) {
 184                  return false;
 185              }
 186              $this->sftp = $host;
 187          } else {
 188              if (isset($this->context)) {
 189                  $context = stream_context_get_options($this->context);
 190              }
 191              if (isset($context[$scheme]['session'])) {
 192                  $sftp = $context[$scheme]['session'];
 193              }
 194              if (isset($context[$scheme]['sftp'])) {
 195                  $sftp = $context[$scheme]['sftp'];
 196              }
 197              if (isset($sftp) && $sftp instanceof SFTP) {
 198                  $this->sftp = $sftp;
 199                  return $path;
 200              }
 201              if (isset($context[$scheme]['username'])) {
 202                  $user = $context[$scheme]['username'];
 203              }
 204              if (isset($context[$scheme]['password'])) {
 205                  $pass = $context[$scheme]['password'];
 206              }
 207              if (isset($context[$scheme]['privkey']) && $context[$scheme]['privkey'] instanceof PrivateKey) {
 208                  $pass = $context[$scheme]['privkey'];
 209              }
 210  
 211              if (!isset($user) || !isset($pass)) {
 212                  return false;
 213              }
 214  
 215              // casting $pass to a string is necessary in the event that it's a \phpseclib3\Crypt\RSA object
 216              if (isset(self::$instances[$host][$port][$user][(string) $pass])) {
 217                  $this->sftp = self::$instances[$host][$port][$user][(string) $pass];
 218              } else {
 219                  $this->sftp = new SFTP($host, $port);
 220                  $this->sftp->disableStatCache();
 221                  if (isset($this->notification) && is_callable($this->notification)) {
 222                      /* if !is_callable($this->notification) we could do this:
 223  
 224                         user_error('fopen(): failed to call user notifier', E_USER_WARNING);
 225  
 226                         the ftp wrapper gives errors like that when the notifier isn't callable.
 227                         i've opted not to do that, however, since the ftp wrapper gives the line
 228                         on which the fopen occurred as the line number - not the line that the
 229                         user_error is on.
 230                      */
 231                      call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
 232                      call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
 233                      if (!$this->sftp->login($user, $pass)) {
 234                          call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0);
 235                          return false;
 236                      }
 237                      call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0);
 238                  } else {
 239                      if (!$this->sftp->login($user, $pass)) {
 240                          return false;
 241                      }
 242                  }
 243                  self::$instances[$host][$port][$user][(string) $pass] = $this->sftp;
 244              }
 245          }
 246  
 247          return $path;
 248      }
 249  
 250      /**
 251       * Opens file or URL
 252       *
 253       * @param string $path
 254       * @param string $mode
 255       * @param int $options
 256       * @param string $opened_path
 257       * @return bool
 258       * @access public
 259       */
 260      private function _stream_open($path, $mode, $options, &$opened_path)
 261      {
 262          $path = $this->parse_path($path);
 263  
 264          if ($path === false) {
 265              return false;
 266          }
 267          $this->path = $path;
 268  
 269          $this->size = $this->sftp->filesize($path);
 270          $this->mode = preg_replace('#[bt]$#', '', $mode);
 271          $this->eof = false;
 272  
 273          if ($this->size === false) {
 274              if ($this->mode[0] == 'r') {
 275                  return false;
 276              } else {
 277                  $this->sftp->touch($path);
 278                  $this->size = 0;
 279              }
 280          } else {
 281              switch ($this->mode[0]) {
 282                  case 'x':
 283                      return false;
 284                  case 'w':
 285                      $this->sftp->truncate($path, 0);
 286                      $this->size = 0;
 287              }
 288          }
 289  
 290          $this->pos = $this->mode[0] != 'a' ? 0 : $this->size;
 291  
 292          return true;
 293      }
 294  
 295      /**
 296       * Read from stream
 297       *
 298       * @param int $count
 299       * @return mixed
 300       * @access public
 301       */
 302      private function _stream_read($count)
 303      {
 304          switch ($this->mode) {
 305              case 'w':
 306              case 'a':
 307              case 'x':
 308              case 'c':
 309                  return false;
 310          }
 311  
 312          // commented out because some files - eg. /dev/urandom - will say their size is 0 when in fact it's kinda infinite
 313          //if ($this->pos >= $this->size) {
 314          //    $this->eof = true;
 315          //    return false;
 316          //}
 317  
 318          $result = $this->sftp->get($this->path, false, $this->pos, $count);
 319          if (isset($this->notification) && is_callable($this->notification)) {
 320              if ($result === false) {
 321                  call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
 322                  return 0;
 323              }
 324              // seems that PHP calls stream_read in 8k chunks
 325              call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size);
 326          }
 327  
 328          if (empty($result)) { // ie. false or empty string
 329              $this->eof = true;
 330              return false;
 331          }
 332          $this->pos += strlen($result);
 333  
 334          return $result;
 335      }
 336  
 337      /**
 338       * Write to stream
 339       *
 340       * @param string $data
 341       * @return int|false
 342       * @access public
 343       */
 344      private function _stream_write($data)
 345      {
 346          switch ($this->mode) {
 347              case 'r':
 348                  return false;
 349          }
 350  
 351          $result = $this->sftp->put($this->path, $data, SFTP::SOURCE_STRING, $this->pos);
 352          if (isset($this->notification) && is_callable($this->notification)) {
 353              if (!$result) {
 354                  call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
 355                  return 0;
 356              }
 357              // seems that PHP splits up strings into 8k blocks before calling stream_write
 358              call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data));
 359          }
 360  
 361          if ($result === false) {
 362              return false;
 363          }
 364          $this->pos += strlen($data);
 365          if ($this->pos > $this->size) {
 366              $this->size = $this->pos;
 367          }
 368          $this->eof = false;
 369          return strlen($data);
 370      }
 371  
 372      /**
 373       * Retrieve the current position of a stream
 374       *
 375       * @return int
 376       * @access public
 377       */
 378      private function _stream_tell()
 379      {
 380          return $this->pos;
 381      }
 382  
 383      /**
 384       * Tests for end-of-file on a file pointer
 385       *
 386       * In my testing there are four classes functions that normally effect the pointer:
 387       * fseek, fputs  / fwrite, fgets / fread and ftruncate.
 388       *
 389       * Only fgets / fread, however, results in feof() returning true. do fputs($fp, 'aaa') on a blank file and feof()
 390       * will return false. do fread($fp, 1) and feof() will then return true. do fseek($fp, 10) on ablank file and feof()
 391       * will return false. do fread($fp, 1) and feof() will then return true.
 392       *
 393       * @return bool
 394       * @access public
 395       */
 396      private function _stream_eof()
 397      {
 398          return $this->eof;
 399      }
 400  
 401      /**
 402       * Seeks to specific location in a stream
 403       *
 404       * @param int $offset
 405       * @param int $whence
 406       * @return bool
 407       * @access public
 408       */
 409      private function _stream_seek($offset, $whence)
 410      {
 411          switch ($whence) {
 412              case SEEK_SET:
 413                  if ($offset < 0) {
 414                      return false;
 415                  }
 416                  break;
 417              case SEEK_CUR:
 418                  $offset += $this->pos;
 419                  break;
 420              case SEEK_END:
 421                  $offset += $this->size;
 422          }
 423  
 424          $this->pos = $offset;
 425          $this->eof = false;
 426          return true;
 427      }
 428  
 429      /**
 430       * Change stream options
 431       *
 432       * @param string $path
 433       * @param int $option
 434       * @param mixed $var
 435       * @return bool
 436       * @access public
 437       */
 438      private function _stream_metadata($path, $option, $var)
 439      {
 440          $path = $this->parse_path($path);
 441          if ($path === false) {
 442              return false;
 443          }
 444  
 445          // stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the constants haven't been defined
 446          // see http://www.php.net/streamwrapper.stream-metadata and https://bugs.php.net/64246
 447          //     and https://github.com/php/php-src/blob/master/main/php_streams.h#L592
 448          switch ($option) {
 449              case 1: // PHP_STREAM_META_TOUCH
 450                  $time = isset($var[0]) ? $var[0] : null;
 451                  $atime = isset($var[1]) ? $var[1] : null;
 452                  return $this->sftp->touch($path, $time, $atime);
 453              case 2: // PHP_STREAM_OWNER_NAME
 454              case 3: // PHP_STREAM_GROUP_NAME
 455                  return false;
 456              case 4: // PHP_STREAM_META_OWNER
 457                  return $this->sftp->chown($path, $var);
 458              case 5: // PHP_STREAM_META_GROUP
 459                  return $this->sftp->chgrp($path, $var);
 460              case 6: // PHP_STREAM_META_ACCESS
 461                  return $this->sftp->chmod($path, $var) !== false;
 462          }
 463      }
 464  
 465      /**
 466       * Retrieve the underlaying resource
 467       *
 468       * @param int $cast_as
 469       * @return resource
 470       * @access public
 471       */
 472      private function _stream_cast($cast_as)
 473      {
 474          return $this->sftp->fsock;
 475      }
 476  
 477      /**
 478       * Advisory file locking
 479       *
 480       * @param int $operation
 481       * @return bool
 482       * @access public
 483       */
 484      private function _stream_lock($operation)
 485      {
 486          return false;
 487      }
 488  
 489      /**
 490       * Renames a file or directory
 491       *
 492       * Attempts to rename oldname to newname, moving it between directories if necessary.
 493       * If newname exists, it will be overwritten.  This is a departure from what \phpseclib3\Net\SFTP
 494       * does.
 495       *
 496       * @param string $path_from
 497       * @param string $path_to
 498       * @return bool
 499       * @access public
 500       */
 501      private function _rename($path_from, $path_to)
 502      {
 503          $path1 = parse_url($path_from);
 504          $path2 = parse_url($path_to);
 505          unset($path1['path'], $path2['path']);
 506          if ($path1 != $path2) {
 507              return false;
 508          }
 509  
 510          $path_from = $this->parse_path($path_from);
 511          $path_to = parse_url($path_to);
 512          if ($path_from === false) {
 513              return false;
 514          }
 515  
 516          $path_to = $path_to['path']; // the $component part of parse_url() was added in PHP 5.1.2
 517          // "It is an error if there already exists a file with the name specified by newpath."
 518          //  -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5
 519          if (!$this->sftp->rename($path_from, $path_to)) {
 520              if ($this->sftp->stat($path_to)) {
 521                  return $this->sftp->delete($path_to, true) && $this->sftp->rename($path_from, $path_to);
 522              }
 523              return false;
 524          }
 525  
 526          return true;
 527      }
 528  
 529      /**
 530       * Open directory handle
 531       *
 532       * The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and
 533       * removed in 5.4 I'm just going to ignore it.
 534       *
 535       * Also, nlist() is the best that this function is realistically going to be able to do. When an SFTP client
 536       * sends a SSH_FXP_READDIR packet you don't generally get info on just one file but on multiple files. Quoting
 537       * the SFTP specs:
 538       *
 539       *    The SSH_FXP_NAME response has the following format:
 540       *
 541       *        uint32     id
 542       *        uint32     count
 543       *        repeats count times:
 544       *                string     filename
 545       *                string     longname
 546       *                ATTRS      attrs
 547       *
 548       * @param string $path
 549       * @param int $options
 550       * @return bool
 551       * @access public
 552       */
 553      private function _dir_opendir($path, $options)
 554      {
 555          $path = $this->parse_path($path);
 556          if ($path === false) {
 557              return false;
 558          }
 559          $this->pos = 0;
 560          $this->entries = $this->sftp->nlist($path);
 561          return $this->entries !== false;
 562      }
 563  
 564      /**
 565       * Read entry from directory handle
 566       *
 567       * @return mixed
 568       * @access public
 569       */
 570      private function _dir_readdir()
 571      {
 572          if (isset($this->entries[$this->pos])) {
 573              return $this->entries[$this->pos++];
 574          }
 575          return false;
 576      }
 577  
 578      /**
 579       * Rewind directory handle
 580       *
 581       * @return bool
 582       * @access public
 583       */
 584      private function _dir_rewinddir()
 585      {
 586          $this->pos = 0;
 587          return true;
 588      }
 589  
 590      /**
 591       * Close directory handle
 592       *
 593       * @return bool
 594       * @access public
 595       */
 596      private function _dir_closedir()
 597      {
 598          return true;
 599      }
 600  
 601      /**
 602       * Create a directory
 603       *
 604       * Only valid $options is STREAM_MKDIR_RECURSIVE
 605       *
 606       * @param string $path
 607       * @param int $mode
 608       * @param int $options
 609       * @return bool
 610       * @access public
 611       */
 612      private function _mkdir($path, $mode, $options)
 613      {
 614          $path = $this->parse_path($path);
 615          if ($path === false) {
 616              return false;
 617          }
 618  
 619          return $this->sftp->mkdir($path, $mode, $options & STREAM_MKDIR_RECURSIVE);
 620      }
 621  
 622      /**
 623       * Removes a directory
 624       *
 625       * Only valid $options is STREAM_MKDIR_RECURSIVE per <http://php.net/streamwrapper.rmdir>, however,
 626       * <http://php.net/rmdir>  does not have a $recursive parameter as mkdir() does so I don't know how
 627       * STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it out with rmdir() I get 8 as
 628       * $options. What does 8 correspond to?
 629       *
 630       * @param string $path
 631       * @param int $options
 632       * @return bool
 633       * @access public
 634       */
 635      private function _rmdir($path, $options)
 636      {
 637          $path = $this->parse_path($path);
 638          if ($path === false) {
 639              return false;
 640          }
 641  
 642          return $this->sftp->rmdir($path);
 643      }
 644  
 645      /**
 646       * Flushes the output
 647       *
 648       * See <http://php.net/fflush>. Always returns true because \phpseclib3\Net\SFTP doesn't cache stuff before writing
 649       *
 650       * @return bool
 651       * @access public
 652       */
 653      private function _stream_flush()
 654      {
 655          return true;
 656      }
 657  
 658      /**
 659       * Retrieve information about a file resource
 660       *
 661       * @return mixed
 662       * @access public
 663       */
 664      private function _stream_stat()
 665      {
 666          $results = $this->sftp->stat($this->path);
 667          if ($results === false) {
 668              return false;
 669          }
 670          return $results;
 671      }
 672  
 673      /**
 674       * Delete a file
 675       *
 676       * @param string $path
 677       * @return bool
 678       * @access public
 679       */
 680      private function _unlink($path)
 681      {
 682          $path = $this->parse_path($path);
 683          if ($path === false) {
 684              return false;
 685          }
 686  
 687          return $this->sftp->delete($path, false);
 688      }
 689  
 690      /**
 691       * Retrieve information about a file
 692       *
 693       * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of \phpseclib3\Net\SFTP\Stream is quiet by default
 694       * might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll
 695       * cross that bridge when and if it's reached
 696       *
 697       * @param string $path
 698       * @param int $flags
 699       * @return mixed
 700       * @access public
 701       */
 702      private function _url_stat($path, $flags)
 703      {
 704          $path = $this->parse_path($path);
 705          if ($path === false) {
 706              return false;
 707          }
 708  
 709          $results = $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path);
 710          if ($results === false) {
 711              return false;
 712          }
 713  
 714          return $results;
 715      }
 716  
 717      /**
 718       * Truncate stream
 719       *
 720       * @param int $new_size
 721       * @return bool
 722       * @access public
 723       */
 724      private function _stream_truncate($new_size)
 725      {
 726          if (!$this->sftp->truncate($this->path, $new_size)) {
 727              return false;
 728          }
 729  
 730          $this->eof = false;
 731          $this->size = $new_size;
 732  
 733          return true;
 734      }
 735  
 736      /**
 737       * Change stream options
 738       *
 739       * STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't.
 740       * The other two aren't supported because of limitations in \phpseclib3\Net\SFTP.
 741       *
 742       * @param int $option
 743       * @param int $arg1
 744       * @param int $arg2
 745       * @return bool
 746       * @access public
 747       */
 748      private function _stream_set_option($option, $arg1, $arg2)
 749      {
 750          return false;
 751      }
 752  
 753      /**
 754       * Close an resource
 755       *
 756       * @access public
 757       */
 758      private function _stream_close()
 759      {
 760      }
 761  
 762      /**
 763       * __call Magic Method
 764       *
 765       * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you.
 766       * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function
 767       * lets you figure that out.
 768       *
 769       * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not
 770       * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method.
 771       *
 772       * @param string $name
 773       * @param array $arguments
 774       * @return mixed
 775       * @access public
 776       */
 777      public function __call($name, $arguments)
 778      {
 779          if (defined('NET_SFTP_STREAM_LOGGING')) {
 780              echo $name . '(';
 781              $last = count($arguments) - 1;
 782              foreach ($arguments as $i => $argument) {
 783                  var_export($argument);
 784                  if ($i != $last) {
 785                      echo ',';
 786                  }
 787              }
 788              echo ")\r\n";
 789          }
 790          $name = '_' . $name;
 791          if (!method_exists($this, $name)) {
 792              return false;
 793          }
 794          return $this->$name(...$arguments);
 795      }
 796  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer