[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/joomla/filesystem/src/Clients/ -> FtpClient.php (source)

   1  <?php
   2  /**
   3   * Part of the Joomla Framework Filesystem Package
   4   *
   5   * @copyright  Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
   6   * @license    GNU General Public License version 2 or later; see LICENSE
   7   */
   8  
   9  namespace Joomla\Filesystem\Clients;
  10  
  11  use Joomla\Filesystem\Exception\FilesystemException;
  12  
  13  /*
  14   * Error Codes:
  15   * - 30 : Unable to connect to host
  16   * - 31 : Not connected
  17   * - 32 : Unable to send command to server
  18   * - 33 : Bad username
  19   * - 34 : Bad password
  20   * - 35 : Bad response
  21   * - 36 : Passive mode failed
  22   * - 37 : Data transfer error
  23   * - 38 : Local filesystem error
  24   */
  25  
  26  if (!\defined('CRLF'))
  27  {
  28      \define('CRLF', "\r\n");
  29  }
  30  
  31  if (!\defined('FTP_AUTOASCII'))
  32  {
  33      \define('FTP_AUTOASCII', -1);
  34  }
  35  
  36  if (!\defined('FTP_BINARY'))
  37  {
  38      \define('FTP_BINARY', 1);
  39  }
  40  
  41  if (!\defined('FTP_ASCII'))
  42  {
  43      \define('FTP_ASCII', 0);
  44  }
  45  
  46  if (!\defined('FTP_NATIVE'))
  47  {
  48      \define('FTP_NATIVE', (\function_exists('ftp_connect')) ? 1 : 0);
  49  }
  50  
  51  /**
  52   * FTP client class
  53   *
  54   * @since  1.0
  55   */
  56  class FtpClient
  57  {
  58      /**
  59       * Socket resource
  60       *
  61       * @var    resource
  62       * @since  1.0
  63       */
  64      private $conn;
  65  
  66      /**
  67       * Data port connection resource
  68       *
  69       * @var    resource
  70       * @since  1.0
  71       */
  72      private $dataconn;
  73  
  74      /**
  75       * Passive connection information
  76       *
  77       * @var    array
  78       * @since  1.0
  79       */
  80      private $pasv;
  81  
  82      /**
  83       * Response Message
  84       *
  85       * @var    string
  86       * @since  1.0
  87       */
  88      private $response;
  89  
  90      /**
  91       * Response Code
  92       *
  93       * @var    integer
  94       * @since  1.0
  95       */
  96      private $responseCode;
  97  
  98      /**
  99       * Response Message
 100       *
 101       * @var    string
 102       * @since  1.0
 103       */
 104      private $responseMsg;
 105  
 106      /**
 107       * Timeout limit
 108       *
 109       * @var    integer
 110       * @since  1.0
 111       */
 112      private $timeout = 15;
 113  
 114      /**
 115       * Transfer Type
 116       *
 117       * @var    integer
 118       * @since  1.0
 119       */
 120      private $type;
 121  
 122      /**
 123       * Array to hold ascii format file extensions
 124       *
 125       * @var    array
 126       * @since  1.0
 127       */
 128      private $autoAscii = [
 129          'asp',
 130          'bat',
 131          'c',
 132          'cpp',
 133          'csv',
 134          'h',
 135          'htm',
 136          'html',
 137          'shtml',
 138          'ini',
 139          'inc',
 140          'log',
 141          'php',
 142          'php3',
 143          'pl',
 144          'perl',
 145          'sh',
 146          'sql',
 147          'txt',
 148          'xhtml',
 149          'xml',
 150      ];
 151  
 152      /**
 153       * Array to hold native line ending characters
 154       *
 155       * @var    array
 156       * @since  1.0
 157       */
 158      private $lineEndings = ['UNIX' => "\n", 'WIN' => "\r\n"];
 159  
 160      /**
 161       * FtpClient instances container.
 162       *
 163       * @var    FtpClient[]
 164       * @since  1.0
 165       */
 166      protected static $instances = [];
 167  
 168      /**
 169       * FtpClient object constructor
 170       *
 171       * @param   array  $options  Associative array of options to set
 172       *
 173       * @since   1.0
 174       */
 175  	public function __construct(array $options = [])
 176      {
 177          // If default transfer type is not set, set it to autoascii detect
 178          if (!isset($options['type']))
 179          {
 180              $options['type'] = \FTP_BINARY;
 181          }
 182  
 183          $this->setOptions($options);
 184  
 185          if (FTP_NATIVE)
 186          {
 187              // Autoloading fails for Buffer as the class is used as a stream handler
 188              class_exists('Joomla\\Filesystem\\Buffer');
 189          }
 190      }
 191  
 192      /**
 193       * FtpClient object destructor
 194       *
 195       * Closes an existing connection, if we have one
 196       *
 197       * @since   1.0
 198       */
 199  	public function __destruct()
 200      {
 201          if (\is_resource($this->conn))
 202          {
 203              $this->quit();
 204          }
 205      }
 206  
 207      /**
 208       * Returns the global FTP connector object, only creating it
 209       * if it doesn't already exist.
 210       *
 211       * You may optionally specify a username and password in the parameters. If you do so,
 212       * you may not login() again with different credentials using the same object.
 213       * If you do not use this option, you must quit() the current connection when you
 214       * are done, to free it for use by others.
 215       *
 216       * @param   string  $host     Host to connect to
 217       * @param   string  $port     Port to connect to
 218       * @param   array   $options  Array with any of these options: type=>[FTP_AUTOASCII|FTP_ASCII|FTP_BINARY], timeout=>(int)
 219       * @param   string  $user     Username to use for a connection
 220       * @param   string  $pass     Password to use for a connection
 221       *
 222       * @return  FtpClient  The FTP Client object.
 223       *
 224       * @since   1.0
 225       */
 226  	public static function getInstance($host = '127.0.0.1', $port = '21', array $options = [], $user = null, $pass = null)
 227      {
 228          $signature = $user . ':' . $pass . '@' . $host . ':' . $port;
 229  
 230          // Create a new instance, or set the options of an existing one
 231          if (!isset(self::$instances[$signature]) || !\is_object(self::$instances[$signature]))
 232          {
 233              self::$instances[$signature] = new static($options);
 234          }
 235          else
 236          {
 237              self::$instances[$signature]->setOptions($options);
 238          }
 239  
 240          // Connect to the server, and login, if requested
 241          if (!self::$instances[$signature]->isConnected())
 242          {
 243              $return = self::$instances[$signature]->connect($host, $port);
 244  
 245              if ($return && $user !== null && $pass !== null)
 246              {
 247                  self::$instances[$signature]->login($user, $pass);
 248              }
 249          }
 250  
 251          return self::$instances[$signature];
 252      }
 253  
 254      /**
 255       * Set client options
 256       *
 257       * @param   array  $options  Associative array of options to set
 258       *
 259       * @return  boolean  True if successful
 260       *
 261       * @since   1.0
 262       */
 263  	public function setOptions(array $options)
 264      {
 265          if (isset($options['type']))
 266          {
 267              $this->type = $options['type'];
 268          }
 269  
 270          if (isset($options['timeout']))
 271          {
 272              $this->timeout = $options['timeout'];
 273          }
 274  
 275          return true;
 276      }
 277  
 278      /**
 279       * Method to connect to a FTP server
 280       *
 281       * @param   string   $host  Host to connect to [Default: 127.0.0.1]
 282       * @param   integer  $port  Port to connect on [Default: port 21]
 283       *
 284       * @return  boolean  True if successful
 285       *
 286       * @since   1.0
 287       * @throws  FilesystemException
 288       */
 289  	public function connect($host = '127.0.0.1', $port = 21)
 290      {
 291          $errno = null;
 292          $err   = null;
 293  
 294          // If already connected, return
 295          if (\is_resource($this->conn))
 296          {
 297              return true;
 298          }
 299  
 300          // If native FTP support is enabled let's use it...
 301          if (FTP_NATIVE)
 302          {
 303              $this->conn = @ftp_connect($host, $port, $this->timeout);
 304  
 305              if ($this->conn === false)
 306              {
 307                  throw new FilesystemException(sprintf('%1$s: Could not connect to host " %2$s " on port " %3$s "', __METHOD__, $host, $port));
 308              }
 309  
 310              // Set the timeout for this connection
 311              ftp_set_option($this->conn, \FTP_TIMEOUT_SEC, $this->timeout);
 312  
 313              return true;
 314          }
 315  
 316          // Connect to the FTP server.
 317          $this->conn = @ fsockopen($host, $port, $errno, $err, $this->timeout);
 318  
 319          if (!$this->conn)
 320          {
 321              throw new FilesystemException(
 322                  sprintf(
 323                      '%1$s: Could not connect to host " %2$s " on port " %3$s ". Socket error number: %4$s and error message: %5$s',
 324                      __METHOD__,
 325                      $host,
 326                      $port,
 327                      $errno,
 328                      $err
 329                  )
 330              );
 331          }
 332  
 333          // Set the timeout for this connection
 334          socket_set_timeout($this->conn, $this->timeout, 0);
 335  
 336          // Check for welcome response code
 337          if (!$this->_verifyResponse(220))
 338          {
 339              throw new FilesystemException(sprintf('%1$s: Bad response. Server response: %2$s [Expected: 220]', __METHOD__, $this->response));
 340          }
 341  
 342          return true;
 343      }
 344  
 345      /**
 346       * Method to determine if the object is connected to an FTP server
 347       *
 348       * @return  boolean  True if connected
 349       *
 350       * @since   1.0
 351       */
 352  	public function isConnected()
 353      {
 354          return \is_resource($this->conn);
 355      }
 356  
 357      /**
 358       * Method to login to a server once connected
 359       *
 360       * @param   string  $user  Username to login to the server
 361       * @param   string  $pass  Password to login to the server
 362       *
 363       * @return  boolean  True if successful
 364       *
 365       * @since   1.0
 366       * @throws  FilesystemException
 367       */
 368  	public function login($user = 'anonymous', $pass = '[email protected]')
 369      {
 370          // If native FTP support is enabled let's use it...
 371          if (FTP_NATIVE)
 372          {
 373              if (@ftp_login($this->conn, $user, $pass) === false)
 374              {
 375                  throw new FilesystemException(__METHOD__ . ': Unable to login');
 376              }
 377  
 378              return true;
 379          }
 380  
 381          // Send the username
 382          if (!$this->_putCmd('USER ' . $user, [331, 503]))
 383          {
 384              throw new FilesystemException(
 385                  sprintf('%1$s: Bad Username. Server response: %2$s [Expected: 331]. Username sent: %3$s', __METHOD__, $this->response, $user)
 386              );
 387          }
 388  
 389          // If we are already logged in, continue :)
 390          if ($this->responseCode == 503)
 391          {
 392              return true;
 393          }
 394  
 395          // Send the password
 396          if (!$this->_putCmd('PASS ' . $pass, 230))
 397          {
 398              throw new FilesystemException(sprintf('%1$s: Bad Password. Server response: %2$s [Expected: 230].', __METHOD__, $this->response));
 399          }
 400  
 401          return true;
 402      }
 403  
 404      /**
 405       * Method to quit and close the connection
 406       *
 407       * @return  boolean  True if successful
 408       *
 409       * @since   1.0
 410       */
 411  	public function quit()
 412      {
 413          // If native FTP support is enabled lets use it...
 414          if (FTP_NATIVE)
 415          {
 416              @ftp_close($this->conn);
 417  
 418              return true;
 419          }
 420  
 421          // Logout and close connection
 422          @fwrite($this->conn, "QUIT\r\n");
 423          @fclose($this->conn);
 424  
 425          return true;
 426      }
 427  
 428      /**
 429       * Method to retrieve the current working directory on the FTP server
 430       *
 431       * @return  string   Current working directory
 432       *
 433       * @since   1.0
 434       * @throws  FilesystemException
 435       */
 436  	public function pwd()
 437      {
 438          // If native FTP support is enabled let's use it...
 439          if (FTP_NATIVE)
 440          {
 441              if (($ret = @ftp_pwd($this->conn)) === false)
 442              {
 443                  throw new FilesystemException(__METHOD__ . 'Bad response.');
 444              }
 445  
 446              return $ret;
 447          }
 448  
 449          $match = [null];
 450  
 451          // Send print working directory command and verify success
 452          if (!$this->_putCmd('PWD', 257))
 453          {
 454              throw new FilesystemException(sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 257]', __METHOD__, $this->response));
 455          }
 456  
 457          // Match just the path
 458          preg_match('/"[^"\r\n]*"/', $this->response, $match);
 459  
 460          // Return the cleaned path
 461          return preg_replace('/"/', '', $match[0]);
 462      }
 463  
 464      /**
 465       * Method to system string from the FTP server
 466       *
 467       * @return  string   System identifier string
 468       *
 469       * @since   1.0
 470       * @throws  FilesystemException
 471       */
 472  	public function syst()
 473      {
 474          // If native FTP support is enabled lets use it...
 475          if (FTP_NATIVE)
 476          {
 477              if (($ret = @ftp_systype($this->conn)) === false)
 478              {
 479                  throw new FilesystemException(__METHOD__ . 'Bad response.');
 480              }
 481          }
 482          else
 483          {
 484              // Send print working directory command and verify success
 485              if (!$this->_putCmd('SYST', 215))
 486              {
 487                  throw new FilesystemException(sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 215]', __METHOD__, $this->response));
 488              }
 489  
 490              $ret = $this->response;
 491          }
 492  
 493          // Match the system string to an OS
 494          if (strpos(strtoupper($ret), 'MAC') !== false)
 495          {
 496              $ret = 'MAC';
 497          }
 498          elseif (strpos(strtoupper($ret), 'WIN') !== false)
 499          {
 500              $ret = 'WIN';
 501          }
 502          else
 503          {
 504              $ret = 'UNIX';
 505          }
 506  
 507          // Return the os type
 508          return $ret;
 509      }
 510  
 511      /**
 512       * Method to change the current working directory on the FTP server
 513       *
 514       * @param   string  $path  Path to change into on the server
 515       *
 516       * @return  boolean True if successful
 517       *
 518       * @since   1.0
 519       * @throws  FilesystemException
 520       */
 521  	public function chdir($path)
 522      {
 523          // If native FTP support is enabled lets use it...
 524          if (FTP_NATIVE)
 525          {
 526              if (@ftp_chdir($this->conn, $path) === false)
 527              {
 528                  throw new FilesystemException(__METHOD__ . 'Bad response.');
 529              }
 530  
 531              return true;
 532          }
 533  
 534          // Send change directory command and verify success
 535          if (!$this->_putCmd('CWD ' . $path, 250))
 536          {
 537              throw new FilesystemException(
 538                  sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 250].  Sent path: %3$s', __METHOD__, $this->response, $path)
 539              );
 540          }
 541  
 542          return true;
 543      }
 544  
 545      /**
 546       * Method to reinitialise the server, ie. need to login again
 547       *
 548       * NOTE: This command not available on all servers
 549       *
 550       * @return  boolean  True if successful
 551       *
 552       * @since   1.0
 553       * @throws  FilesystemException
 554       */
 555  	public function reinit()
 556      {
 557          // If native FTP support is enabled let's use it...
 558          if (FTP_NATIVE)
 559          {
 560              if (@ftp_site($this->conn, 'REIN') === false)
 561              {
 562                  throw new FilesystemException(__METHOD__ . 'Bad response.');
 563              }
 564  
 565              return true;
 566          }
 567  
 568          // Send reinitialise command to the server
 569          if (!$this->_putCmd('REIN', 220))
 570          {
 571              throw new FilesystemException(sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 220]', __METHOD__, $this->response));
 572          }
 573  
 574          return true;
 575      }
 576  
 577      /**
 578       * Method to rename a file/folder on the FTP server
 579       *
 580       * @param   string  $from  Path to change file/folder from
 581       * @param   string  $to    Path to change file/folder to
 582       *
 583       * @return  boolean  True if successful
 584       *
 585       * @since   1.0
 586       * @throws  FilesystemException
 587       */
 588  	public function rename($from, $to)
 589      {
 590          // If native FTP support is enabled let's use it...
 591          if (FTP_NATIVE)
 592          {
 593              if (@ftp_rename($this->conn, $from, $to) === false)
 594              {
 595                  throw new FilesystemException(__METHOD__ . 'Bad response.');
 596              }
 597  
 598              return true;
 599          }
 600  
 601          // Send rename from command to the server
 602          if (!$this->_putCmd('RNFR ' . $from, 350))
 603          {
 604              throw new FilesystemException(
 605                  sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 350].  From path sent: %3$s', __METHOD__, $this->response, $from)
 606              );
 607          }
 608  
 609          // Send rename to command to the server
 610          if (!$this->_putCmd('RNTO ' . $to, 250))
 611          {
 612              throw new FilesystemException(
 613                  sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 250].  To path sent: %3$s', __METHOD__, $this->response, $to)
 614              );
 615          }
 616  
 617          return true;
 618      }
 619  
 620      /**
 621       * Method to change mode for a path on the FTP server
 622       *
 623       * @param   string  $path  Path to change mode on
 624       * @param   mixed   $mode  Octal value to change mode to, e.g. '0777', 0777 or 511 (string or integer)
 625       *
 626       * @return  boolean  True if successful
 627       *
 628       * @since   1.0
 629       * @throws  FilesystemException
 630       */
 631  	public function chmod($path, $mode)
 632      {
 633          // If no filename is given, we assume the current directory is the target
 634          if ($path == '')
 635          {
 636              $path = '.';
 637          }
 638  
 639          // Convert the mode to a string
 640          if (\is_int($mode))
 641          {
 642              $mode = decoct($mode);
 643          }
 644  
 645          // If native FTP support is enabled let's use it...
 646          if (FTP_NATIVE)
 647          {
 648              if (@ftp_site($this->conn, 'CHMOD ' . $mode . ' ' . $path) === false)
 649              {
 650                  if (!\defined('PHP_WINDOWS_VERSION_MAJOR'))
 651                  {
 652                      throw new FilesystemException(__METHOD__ . 'Bad response.');
 653                  }
 654  
 655                  return false;
 656              }
 657  
 658              return true;
 659          }
 660  
 661          // Send change mode command and verify success [must convert mode from octal]
 662          if (!$this->_putCmd('SITE CHMOD ' . $mode . ' ' . $path, [200, 250]))
 663          {
 664              if (!\defined('PHP_WINDOWS_VERSION_MAJOR'))
 665              {
 666                  throw new FilesystemException(
 667                      sprintf(
 668                          '%1$s: Bad response.  Server response: %2$s [Expected: 250].  Path sent: %3$s.  Mode sent: %4$s',
 669                          __METHOD__,
 670                          $this->response,
 671                          $path,
 672                          $mode
 673                      )
 674                  );
 675              }
 676  
 677              return false;
 678          }
 679  
 680          return true;
 681      }
 682  
 683      /**
 684       * Method to delete a path [file/folder] on the FTP server
 685       *
 686       * @param   string  $path  Path to delete
 687       *
 688       * @return  boolean  True if successful
 689       *
 690       * @since   1.0
 691       * @throws  FilesystemException
 692       */
 693  	public function delete($path)
 694      {
 695          // If native FTP support is enabled let's use it...
 696          if (FTP_NATIVE)
 697          {
 698              if (@ftp_delete($this->conn, $path) === false)
 699              {
 700                  if (@ftp_rmdir($this->conn, $path) === false)
 701                  {
 702                      throw new FilesystemException(__METHOD__ . 'Bad response.');
 703                  }
 704              }
 705  
 706              return true;
 707          }
 708  
 709          // Send delete file command and if that doesn't work, try to remove a directory
 710          if (!$this->_putCmd('DELE ' . $path, 250))
 711          {
 712              if (!$this->_putCmd('RMD ' . $path, 250))
 713              {
 714                  throw new FilesystemException(
 715                      sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 250].  Path sent: %3$s', __METHOD__, $this->response, $path)
 716                  );
 717              }
 718          }
 719  
 720          return true;
 721      }
 722  
 723      /**
 724       * Method to create a directory on the FTP server
 725       *
 726       * @param   string  $path  Directory to create
 727       *
 728       * @return  boolean  True if successful
 729       *
 730       * @since   1.0
 731       * @throws  FilesystemException
 732       */
 733  	public function mkdir($path)
 734      {
 735          // If native FTP support is enabled let's use it...
 736          if (FTP_NATIVE)
 737          {
 738              if (@ftp_mkdir($this->conn, $path) === false)
 739              {
 740                  throw new FilesystemException(__METHOD__ . 'Bad response.');
 741              }
 742  
 743              return true;
 744          }
 745  
 746          // Send change directory command and verify success
 747          if (!$this->_putCmd('MKD ' . $path, 257))
 748          {
 749              throw new FilesystemException(
 750                  sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 257].  Path sent: %3$s', __METHOD__, $this->response, $path)
 751              );
 752          }
 753  
 754          return true;
 755      }
 756  
 757      /**
 758       * Method to restart data transfer at a given byte
 759       *
 760       * @param   integer  $point  Byte to restart transfer at
 761       *
 762       * @return  boolean  True if successful
 763       *
 764       * @since   1.0
 765       * @throws  FilesystemException
 766       */
 767  	public function restart($point)
 768      {
 769          // If native FTP support is enabled let's use it...
 770          if (FTP_NATIVE)
 771          {
 772              if (@ftp_site($this->conn, 'REST ' . $point) === false)
 773              {
 774                  throw new FilesystemException(__METHOD__ . 'Bad response.');
 775              }
 776  
 777              return true;
 778          }
 779  
 780          // Send restart command and verify success
 781          if (!$this->_putCmd('REST ' . $point, 350))
 782          {
 783              throw new FilesystemException(
 784                  sprintf(
 785                      '%1$s: Bad response.  Server response: %2$s [Expected: 350].  Restart point sent: %3$s', __METHOD__, $this->response, $point
 786                  )
 787              );
 788          }
 789  
 790          return true;
 791      }
 792  
 793      /**
 794       * Method to create an empty file on the FTP server
 795       *
 796       * @param   string  $path  Path local file to store on the FTP server
 797       *
 798       * @return  boolean  True if successful
 799       *
 800       * @since   1.0
 801       * @throws  FilesystemException
 802       */
 803  	public function create($path)
 804      {
 805          // If native FTP support is enabled let's use it...
 806          if (FTP_NATIVE)
 807          {
 808              // Turn passive mode on
 809              if (@ftp_pasv($this->conn, true) === false)
 810              {
 811                  throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
 812              }
 813  
 814              $buffer = fopen('buffer://tmp', 'r');
 815  
 816              if (@ftp_fput($this->conn, $path, $buffer, \FTP_ASCII) === false)
 817              {
 818                  fclose($buffer);
 819  
 820                  throw new FilesystemException(__METHOD__ . 'Bad response.');
 821              }
 822  
 823              fclose($buffer);
 824  
 825              return true;
 826          }
 827  
 828          // Start passive mode
 829          if (!$this->_passive())
 830          {
 831              throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
 832          }
 833  
 834          if (!$this->_putCmd('STOR ' . $path, [150, 125]))
 835          {
 836              @ fclose($this->dataconn);
 837  
 838              throw new FilesystemException(
 839                  sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 150 or 125].  Path sent: %3$s', __METHOD__, $this->response, $path)
 840              );
 841          }
 842  
 843          // To create a zero byte upload close the data port connection
 844          fclose($this->dataconn);
 845  
 846          if (!$this->_verifyResponse(226))
 847          {
 848              throw new FilesystemException(
 849                  sprintf('%1$s: Transfer failed.  Server response: %2$s [Expected: 226].  Path sent: %3$s', __METHOD__, $this->response, $path)
 850              );
 851          }
 852  
 853          return true;
 854      }
 855  
 856      /**
 857       * Method to read a file from the FTP server's contents into a buffer
 858       *
 859       * @param   string  $remote  Path to remote file to read on the FTP server
 860       * @param   string  $buffer  Buffer variable to read file contents into
 861       *
 862       * @return  boolean  True if successful
 863       *
 864       * @since   1.0
 865       * @throws  FilesystemException
 866       */
 867  	public function read($remote, &$buffer)
 868      {
 869          // Determine file type
 870          $mode = $this->_findMode($remote);
 871  
 872          // If native FTP support is enabled let's use it...
 873          if (FTP_NATIVE)
 874          {
 875              // Turn passive mode on
 876              if (@ftp_pasv($this->conn, true) === false)
 877              {
 878                  throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
 879              }
 880  
 881              $tmp = fopen('buffer://tmp', 'br+');
 882  
 883              if (@ftp_fget($this->conn, $tmp, $remote, $mode) === false)
 884              {
 885                  fclose($tmp);
 886  
 887                  throw new FilesystemException(__METHOD__ . 'Bad response.');
 888              }
 889  
 890              // Read tmp buffer contents
 891              rewind($tmp);
 892              $buffer = '';
 893  
 894              while (!feof($tmp))
 895              {
 896                  $buffer .= fread($tmp, 8192);
 897              }
 898  
 899              fclose($tmp);
 900  
 901              return true;
 902          }
 903  
 904          $this->_mode($mode);
 905  
 906          // Start passive mode
 907          if (!$this->_passive())
 908          {
 909              throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
 910          }
 911  
 912          if (!$this->_putCmd('RETR ' . $remote, [150, 125]))
 913          {
 914              @ fclose($this->dataconn);
 915  
 916              throw new FilesystemException(
 917                  sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 150 or 125].  Path sent: %3$s', __METHOD__, $this->response, $remote)
 918              );
 919          }
 920  
 921          // Read data from data port connection and add to the buffer
 922          $buffer = '';
 923  
 924          while (!feof($this->dataconn))
 925          {
 926              $buffer .= fread($this->dataconn, 4096);
 927          }
 928  
 929          // Close the data port connection
 930          fclose($this->dataconn);
 931  
 932          // Let's try to cleanup some line endings if it is ascii
 933          if ($mode == \FTP_ASCII)
 934          {
 935              $os = 'UNIX';
 936  
 937              if (\defined('PHP_WINDOWS_VERSION_MAJOR'))
 938              {
 939                  $os = 'WIN';
 940              }
 941  
 942              $buffer = preg_replace('/' . CRLF . '/', $this->lineEndings[$os], $buffer);
 943          }
 944  
 945          if (!$this->_verifyResponse(226))
 946          {
 947              throw new FilesystemException(
 948                  sprintf(
 949                      '%1$s: Transfer failed.  Server response: %2$s [Expected: 226].  Restart point sent: %3$s',
 950                      __METHOD__, $this->response, $remote
 951                  )
 952              );
 953          }
 954  
 955          return true;
 956      }
 957  
 958      /**
 959       * Method to get a file from the FTP server and save it to a local file
 960       *
 961       * @param   string  $local   Local path to save remote file to
 962       * @param   string  $remote  Path to remote file to get on the FTP server
 963       *
 964       * @return  boolean  True if successful
 965       *
 966       * @since   1.0
 967       * @throws  FilesystemException
 968       */
 969  	public function get($local, $remote)
 970      {
 971          // Determine file type
 972          $mode = $this->_findMode($remote);
 973  
 974          // If native FTP support is enabled let's use it...
 975          if (FTP_NATIVE)
 976          {
 977              // Turn passive mode on
 978              if (@ftp_pasv($this->conn, true) === false)
 979              {
 980                  throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
 981              }
 982  
 983              if (@ftp_get($this->conn, $local, $remote, $mode) === false)
 984              {
 985                  throw new FilesystemException(__METHOD__ . 'Bad response.');
 986              }
 987  
 988              return true;
 989          }
 990  
 991          $this->_mode($mode);
 992  
 993          // Check to see if the local file can be opened for writing
 994          $fp = fopen($local, 'wb');
 995  
 996          if (!$fp)
 997          {
 998              throw new FilesystemException(sprintf('%1$s: Unable to open local file for writing.  Local path: %2$s', __METHOD__, $local));
 999          }
1000  
1001          // Start passive mode
1002          if (!$this->_passive())
1003          {
1004              throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
1005          }
1006  
1007          if (!$this->_putCmd('RETR ' . $remote, [150, 125]))
1008          {
1009              @ fclose($this->dataconn);
1010  
1011              throw new FilesystemException(
1012                  sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 150 or 125].  Path sent: %3$s', __METHOD__, $this->response, $remote)
1013              );
1014          }
1015  
1016          // Read data from data port connection and add to the buffer
1017          while (!feof($this->dataconn))
1018          {
1019              $buffer = fread($this->dataconn, 4096);
1020              fwrite($fp, $buffer, 4096);
1021          }
1022  
1023          // Close the data port connection and file pointer
1024          fclose($this->dataconn);
1025          fclose($fp);
1026  
1027          if (!$this->_verifyResponse(226))
1028          {
1029              throw new FilesystemException(
1030                  sprintf('%1$s: Transfer failed.  Server response: %2$s [Expected: 226].  Path sent: %3$s', __METHOD__, $this->response, $remote)
1031              );
1032          }
1033  
1034          return true;
1035      }
1036  
1037      /**
1038       * Method to store a file to the FTP server
1039       *
1040       * @param   string  $local   Path to local file to store on the FTP server
1041       * @param   string  $remote  FTP path to file to create
1042       *
1043       * @return  boolean  True if successful
1044       *
1045       * @since   1.0
1046       * @throws  FilesystemException
1047       */
1048  	public function store($local, $remote = null)
1049      {
1050          // If remote file is not given, use the filename of the local file in the current
1051          // working directory.
1052          if ($remote == null)
1053          {
1054              $remote = basename($local);
1055          }
1056  
1057          // Determine file type
1058          $mode = $this->_findMode($remote);
1059  
1060          // If native FTP support is enabled let's use it...
1061          if (FTP_NATIVE)
1062          {
1063              // Turn passive mode on
1064              if (@ftp_pasv($this->conn, true) === false)
1065              {
1066                  throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
1067              }
1068  
1069              if (@ftp_put($this->conn, $remote, $local, $mode) === false)
1070              {
1071                  throw new FilesystemException(__METHOD__ . 'Bad response.');
1072              }
1073  
1074              return true;
1075          }
1076  
1077          $this->_mode($mode);
1078  
1079          // Check to see if the local file exists and if so open it for reading
1080          if (@ file_exists($local))
1081          {
1082              $fp = fopen($local, 'rb');
1083  
1084              if (!$fp)
1085              {
1086                  throw new FilesystemException(sprintf('%1$s: Unable to open local file for reading. Local path: %2$s', __METHOD__, $local));
1087              }
1088          }
1089          else
1090          {
1091              throw new FilesystemException(sprintf('%1$s: Unable to find local file. Local path: %2$s', __METHOD__, $local));
1092          }
1093  
1094          // Start passive mode
1095          if (!$this->_passive())
1096          {
1097              @ fclose($fp);
1098  
1099              throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
1100          }
1101  
1102          // Send store command to the FTP server
1103          if (!$this->_putCmd('STOR ' . $remote, [150, 125]))
1104          {
1105              @ fclose($fp);
1106              @ fclose($this->dataconn);
1107  
1108              throw new FilesystemException(
1109                  sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 150 or 125].  Path sent: %3$s', __METHOD__, $this->response, $remote)
1110              );
1111          }
1112  
1113          // Do actual file transfer, read local file and write to data port connection
1114          while (!feof($fp))
1115          {
1116              $line = fread($fp, 4096);
1117  
1118              do
1119              {
1120                  if (($result = @ fwrite($this->dataconn, $line)) === false)
1121                  {
1122                      throw new FilesystemException(__METHOD__ . ': Unable to write to data port socket');
1123                  }
1124  
1125                  $line = substr($line, $result);
1126              }
1127              while ($line != '');
1128          }
1129  
1130          fclose($fp);
1131          fclose($this->dataconn);
1132  
1133          if (!$this->_verifyResponse(226))
1134          {
1135              throw new FilesystemException(
1136                  sprintf('%1$s: Transfer failed.  Server response: %2$s [Expected: 226].  Path sent: %3$s', __METHOD__, $this->response, $remote)
1137              );
1138          }
1139  
1140          return true;
1141      }
1142  
1143      /**
1144       * Method to write a string to the FTP server
1145       *
1146       * @param   string  $remote  FTP path to file to write to
1147       * @param   string  $buffer  Contents to write to the FTP server
1148       *
1149       * @return  boolean  True if successful
1150       *
1151       * @since   1.0
1152       * @throws  FilesystemException
1153       */
1154  	public function write($remote, $buffer)
1155      {
1156          // Determine file type
1157          $mode = $this->_findMode($remote);
1158  
1159          // If native FTP support is enabled let's use it...
1160          if (FTP_NATIVE)
1161          {
1162              // Turn passive mode on
1163              if (@ftp_pasv($this->conn, true) === false)
1164              {
1165                  throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
1166              }
1167  
1168              $tmp = fopen('buffer://tmp', 'br+');
1169              fwrite($tmp, $buffer);
1170              rewind($tmp);
1171  
1172              if (@ftp_fput($this->conn, $remote, $tmp, $mode) === false)
1173              {
1174                  fclose($tmp);
1175  
1176                  throw new FilesystemException(__METHOD__ . 'Bad response.');
1177              }
1178  
1179              fclose($tmp);
1180  
1181              return true;
1182          }
1183  
1184          // First we need to set the transfer mode
1185          $this->_mode($mode);
1186  
1187          // Start passive mode
1188          if (!$this->_passive())
1189          {
1190              throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
1191          }
1192  
1193          // Send store command to the FTP server
1194          if (!$this->_putCmd('STOR ' . $remote, [150, 125]))
1195          {
1196              @ fclose($this->dataconn);
1197  
1198              throw new FilesystemException(
1199                  sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 150 or 125].  Path sent: %3$s', __METHOD__, $this->response, $remote)
1200              );
1201          }
1202  
1203          // Write buffer to the data connection port
1204          do
1205          {
1206              if (($result = @ fwrite($this->dataconn, $buffer)) === false)
1207              {
1208                  throw new FilesystemException(__METHOD__ . ': Unable to write to data port socket.');
1209              }
1210  
1211              $buffer = substr($buffer, $result);
1212          }
1213          while ($buffer != '');
1214  
1215          // Close the data connection port [Data transfer complete]
1216          fclose($this->dataconn);
1217  
1218          // Verify that the server recieved the transfer
1219          if (!$this->_verifyResponse(226))
1220          {
1221              throw new FilesystemException(
1222                  sprintf('%1$s: Transfer failed.  Server response: %2$s [Expected: 226].  Path sent: %3$s', __METHOD__, $this->response, $remote)
1223              );
1224          }
1225  
1226          return true;
1227      }
1228  
1229      /**
1230       * Method to list the filenames of the contents of a directory on the FTP server
1231       *
1232       * Note: Some servers also return folder names. However, to be sure to list folders on all
1233       * servers, you should use listDetails() instead if you also need to deal with folders
1234       *
1235       * @param   string  $path  Path local file to store on the FTP server
1236       *
1237       * @return  string  Directory listing
1238       *
1239       * @since   1.0
1240       * @throws  FilesystemException
1241       */
1242  	public function listNames($path = null)
1243      {
1244          $data = null;
1245  
1246          // If native FTP support is enabled let's use it...
1247          if (FTP_NATIVE)
1248          {
1249              // Turn passive mode on
1250              if (@ftp_pasv($this->conn, true) === false)
1251              {
1252                  throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
1253              }
1254  
1255              if (($list = @ftp_nlist($this->conn, $path)) === false)
1256              {
1257                  // Workaround for empty directories on some servers
1258                  if ($this->listDetails($path, 'files') === [])
1259                  {
1260                      return [];
1261                  }
1262  
1263                  throw new FilesystemException(__METHOD__ . 'Bad response.');
1264              }
1265  
1266              $list = preg_replace('#^' . preg_quote($path, '#') . '[/\\\\]?#', '', $list);
1267  
1268              if ($keys = array_merge(array_keys($list, '.'), array_keys($list, '..')))
1269              {
1270                  foreach ($keys as $key)
1271                  {
1272                      unset($list[$key]);
1273                  }
1274              }
1275  
1276              return $list;
1277          }
1278  
1279          // If a path exists, prepend a space
1280          if ($path != null)
1281          {
1282              $path = ' ' . $path;
1283          }
1284  
1285          // Start passive mode
1286          if (!$this->_passive())
1287          {
1288              throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
1289          }
1290  
1291          if (!$this->_putCmd('NLST' . $path, [150, 125]))
1292          {
1293              @ fclose($this->dataconn);
1294  
1295              // Workaround for empty directories on some servers
1296              if ($this->listDetails($path, 'files') === [])
1297              {
1298                  return [];
1299              }
1300  
1301              throw new FilesystemException(
1302                  sprintf('%1$s: Bad response.  Server response: %2$s [Expected: 150 or 125].  Path sent: %3$s', __METHOD__, $this->response, $path)
1303              );
1304          }
1305  
1306          // Read in the file listing.
1307          while (!feof($this->dataconn))
1308          {
1309              $data .= fread($this->dataconn, 4096);
1310          }
1311  
1312          fclose($this->dataconn);
1313  
1314          // Everything go okay?
1315          if (!$this->_verifyResponse(226))
1316          {
1317              throw new FilesystemException(
1318                  sprintf('%1$s: Transfer failed.  Server response: %2$s [Expected: 226].  Path sent: %3$s', __METHOD__, $this->response, $path)
1319              );
1320          }
1321  
1322          $data = preg_split('/[' . CRLF . ']+/', $data, -1, \PREG_SPLIT_NO_EMPTY);
1323          $data = preg_replace('#^' . preg_quote(substr($path, 1), '#') . '[/\\\\]?#', '', $data);
1324  
1325          if ($keys = array_merge(array_keys($data, '.'), array_keys($data, '..')))
1326          {
1327              foreach ($keys as $key)
1328              {
1329                  unset($data[$key]);
1330              }
1331          }
1332  
1333          return $data;
1334      }
1335  
1336      /**
1337       * Method to list the contents of a directory on the FTP server
1338       *
1339       * @param   string  $path  Path to the local file to be stored on the FTP server
1340       * @param   string  $type  Return type [raw|all|folders|files]
1341       *
1342       * @return  string[]  If $type is raw: string Directory listing, otherwise array of string with file-names
1343       *
1344       * @since   1.0
1345       * @throws  FilesystemException
1346       */
1347  	public function listDetails($path = null, $type = 'all')
1348      {
1349          $dirList = [];
1350          $data    = null;
1351          $regs    = null;
1352  
1353          // TODO: Deal with recurse -- nightmare
1354          // For now we will just set it to false
1355          $recurse = false;
1356  
1357          // If native FTP support is enabled let's use it...
1358          if (FTP_NATIVE)
1359          {
1360              // Turn passive mode on
1361              if (@ftp_pasv($this->conn, true) === false)
1362              {
1363                  throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
1364              }
1365  
1366              if (($contents = @ftp_rawlist($this->conn, $path)) === false)
1367              {
1368                  throw new FilesystemException(__METHOD__ . 'Bad response.');
1369              }
1370          }
1371          else
1372          {
1373              // Non Native mode
1374  
1375              // Start passive mode
1376              if (!$this->_passive())
1377              {
1378                  throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.');
1379              }
1380  
1381              // If a path exists, prepend a space
1382              if ($path != null)
1383              {
1384                  $path = ' ' . $path;
1385              }
1386  
1387              // Request the file listing
1388              if (!$this->_putCmd(($recurse == true) ? 'LIST -R' : 'LIST' . $path, [150, 125]))
1389              {
1390                  @ fclose($this->dataconn);
1391  
1392                  throw new FilesystemException(
1393                      sprintf(
1394                          '%1$s: Bad response.  Server response: %2$s [Expected: 150 or 125].  Path sent: %3$s',
1395                          __METHOD__, $this->response, $path
1396                      )
1397                  );
1398              }
1399  
1400              // Read in the file listing.
1401              while (!feof($this->dataconn))
1402              {
1403                  $data .= fread($this->dataconn, 4096);
1404              }
1405  
1406              fclose($this->dataconn);
1407  
1408              // Everything go okay?
1409              if (!$this->_verifyResponse(226))
1410              {
1411                  throw new FilesystemException(
1412                      sprintf('%1$s: Transfer failed.  Server response: %2$s [Expected: 226].  Path sent: %3$s', __METHOD__, $this->response, $path)
1413                  );
1414              }
1415  
1416              $contents = explode(CRLF, $data);
1417          }
1418  
1419          // If only raw output is requested we are done
1420          if ($type == 'raw')
1421          {
1422              return $data;
1423          }
1424  
1425          // If we received the listing of an empty directory, we are done as well
1426          if (empty($contents[0]))
1427          {
1428              return $dirList;
1429          }
1430  
1431          // If the server returned the number of results in the first response, let's dump it
1432          if (strtolower(substr($contents[0], 0, 6)) == 'total ')
1433          {
1434              array_shift($contents);
1435  
1436              if (!isset($contents[0]) || empty($contents[0]))
1437              {
1438                  return $dirList;
1439              }
1440          }
1441  
1442          // Regular expressions for the directory listing parsing.
1443          $regexps = [
1444              'UNIX' => '#([-dl][rwxstST-]+).* ([0-9]*) ([a-zA-Z0-9]+).* ([a-zA-Z0-9]+).* ([0-9]*)'
1445                  . ' ([a-zA-Z]+[0-9: ]*[0-9])[ ]+(([0-9]{1,2}:[0-9]{2})|[0-9]{4}) (.+)#',
1446              'MAC' => '#([-dl][rwxstST-]+).* ?([0-9 ]*)?([a-zA-Z0-9]+).* ([a-zA-Z0-9]+).* ([0-9]*)'
1447                  . ' ([a-zA-Z]+[0-9: ]*[0-9])[ ]+(([0-9]{2}:[0-9]{2})|[0-9]{4}) (.+)#',
1448              'WIN' => '#([0-9]{2})-([0-9]{2})-([0-9]{2}) +([0-9]{2}):([0-9]{2})(AM|PM) +([0-9]+|<DIR>) +(.+)#',
1449          ];
1450  
1451          // Find out the format of the directory listing by matching one of the regexps
1452          $osType = null;
1453  
1454          foreach ($regexps as $k => $v)
1455          {
1456              if (@preg_match($v, $contents[0]))
1457              {
1458                  $osType = $k;
1459                  $regexp = $v;
1460  
1461                  break;
1462              }
1463          }
1464  
1465          if (!$osType)
1466          {
1467              throw new FilesystemException(__METHOD__ . ': Unrecognised directory listing format.');
1468          }
1469  
1470          /*
1471           * Here is where it is going to get dirty....
1472           */
1473          if ($osType == 'UNIX' || $osType == 'MAC')
1474          {
1475              foreach ($contents as $file)
1476              {
1477                  $tmpArray = null;
1478  
1479                  if (@preg_match($regexp, $file, $regs))
1480                  {
1481                      $fType = (int) strpos('-dl', $regs[1][0]);
1482  
1483                      // $tmpArray['line'] = $regs[0];
1484                      $tmpArray['type']   = $fType;
1485                      $tmpArray['rights'] = $regs[1];
1486  
1487                      // $tmpArray['number'] = $regs[2];
1488                      $tmpArray['user']  = $regs[3];
1489                      $tmpArray['group'] = $regs[4];
1490                      $tmpArray['size']  = $regs[5];
1491                      $tmpArray['date']  = @date('m-d', strtotime($regs[6]));
1492                      $tmpArray['time']  = $regs[7];
1493                      $tmpArray['name']  = $regs[9];
1494                  }
1495  
1496                  // If we just want files, do not add a folder
1497                  if ($type == 'files' && $tmpArray['type'] == 1)
1498                  {
1499                      continue;
1500                  }
1501  
1502                  // If we just want folders, do not add a file
1503                  if ($type == 'folders' && $tmpArray['type'] == 0)
1504                  {
1505                      continue;
1506                  }
1507  
1508                  if (\is_array($tmpArray) && $tmpArray['name'] != '.' && $tmpArray['name'] != '..')
1509                  {
1510                      $dirList[] = $tmpArray;
1511                  }
1512              }
1513          }
1514          else
1515          {
1516              foreach ($contents as $file)
1517              {
1518                  $tmpArray = null;
1519  
1520                  if (@preg_match($regexp, $file, $regs))
1521                  {
1522                      $fType     = (int) ($regs[7] == '<DIR>');
1523                      $timestamp = strtotime("$regs[3]-$regs[1]-$regs[2] $regs[4]:$regs[5]$regs[6]");
1524  
1525                      // $tmpArray['line'] = $regs[0];
1526                      $tmpArray['type']   = $fType;
1527                      $tmpArray['rights'] = '';
1528  
1529                      // $tmpArray['number'] = 0;
1530                      $tmpArray['user']  = '';
1531                      $tmpArray['group'] = '';
1532                      $tmpArray['size']  = (int) $regs[7];
1533                      $tmpArray['date']  = date('m-d', $timestamp);
1534                      $tmpArray['time']  = date('H:i', $timestamp);
1535                      $tmpArray['name']  = $regs[8];
1536                  }
1537  
1538                  // If we just want files, do not add a folder
1539                  if ($type == 'files' && $tmpArray['type'] == 1)
1540                  {
1541                      continue;
1542                  }
1543  
1544                  // If we just want folders, do not add a file
1545                  if ($type == 'folders' && $tmpArray['type'] == 0)
1546                  {
1547                      continue;
1548                  }
1549  
1550                  if (\is_array($tmpArray) && $tmpArray['name'] != '.' && $tmpArray['name'] != '..')
1551                  {
1552                      $dirList[] = $tmpArray;
1553                  }
1554              }
1555          }
1556  
1557          return $dirList;
1558      }
1559  
1560      /**
1561       * Send command to the FTP server and validate an expected response code
1562       *
1563       * @param   string  $cmd               Command to send to the FTP server
1564       * @param   mixed   $expectedResponse  Integer response code or array of integer response codes
1565       *
1566       * @return  boolean  True if command executed successfully
1567       *
1568       * @since   1.0
1569       * @throws  FilesystemException
1570       */
1571  	protected function _putCmd($cmd, $expectedResponse)
1572      {
1573          // Make sure we have a connection to the server
1574          if (!\is_resource($this->conn))
1575          {
1576              throw new FilesystemException(__METHOD__ . ': Not connected to the control port.');
1577          }
1578  
1579          // Send the command to the server
1580          if (!fwrite($this->conn, $cmd . "\r\n"))
1581          {
1582              throw new FilesystemException(sprintf('%1$s: Unable to send command: %2$s', __METHOD__, $cmd));
1583          }
1584  
1585          return $this->_verifyResponse($expectedResponse);
1586      }
1587  
1588      /**
1589       * Verify the response code from the server and log response if flag is set
1590       *
1591       * @param   mixed  $expected  Integer response code or array of integer response codes
1592       *
1593       * @return  boolean  True if response code from the server is expected
1594       *
1595       * @since   1.0
1596       * @throws  FilesystemException
1597       */
1598  	protected function _verifyResponse($expected)
1599      {
1600          $parts = null;
1601  
1602          // Wait for a response from the server, but timeout after the set time limit
1603          $endTime        = time() + $this->timeout;
1604          $this->response = '';
1605  
1606          do
1607          {
1608              $this->response .= fgets($this->conn, 4096);
1609          }
1610          while (!preg_match('/^([0-9]{3})(-(.*' . CRLF . ')+\\1)? [^' . CRLF . ']+' . CRLF . '$/', $this->response, $parts) && time() < $endTime);
1611  
1612          // Catch a timeout or bad response
1613          if (!isset($parts[1]))
1614          {
1615              throw new FilesystemException(
1616                  sprintf(
1617                      '%1$s: Timeout or unrecognised response while waiting for a response from the server. Server response: %2$s',
1618                      __METHOD__, $this->response
1619                  )
1620              );
1621          }
1622  
1623          // Separate the code from the message
1624          $this->responseCode = $parts[1];
1625          $this->responseMsg  = $parts[0];
1626  
1627          // Did the server respond with the code we wanted?
1628          if (\is_array($expected))
1629          {
1630              if (\in_array($this->responseCode, $expected))
1631              {
1632                  $retval = true;
1633              }
1634              else
1635              {
1636                  $retval = false;
1637              }
1638          }
1639          else
1640          {
1641              if ($this->responseCode == $expected)
1642              {
1643                  $retval = true;
1644              }
1645              else
1646              {
1647                  $retval = false;
1648              }
1649          }
1650  
1651          return $retval;
1652      }
1653  
1654      /**
1655       * Set server to passive mode and open a data port connection
1656       *
1657       * @return  boolean  True if successful
1658       *
1659       * @since   1.0
1660       * @throws  FilesystemException
1661       */
1662  	protected function _passive()
1663      {
1664          $match = [];
1665          $parts = [];
1666          $errno = null;
1667          $err   = null;
1668  
1669          // Make sure we have a connection to the server
1670          if (!\is_resource($this->conn))
1671          {
1672              throw new FilesystemException(__METHOD__ . ': Not connected to the control port.');
1673          }
1674  
1675          // Request a passive connection - this means, we'll talk to you, you don't talk to us.
1676          @ fwrite($this->conn, "PASV\r\n");
1677  
1678          // Wait for a response from the server, but timeout after the set time limit
1679          $endTime        = time() + $this->timeout;
1680          $this->response = '';
1681  
1682          do
1683          {
1684              $this->response .= fgets($this->conn, 4096);
1685          }
1686          while (!preg_match('/^([0-9]{3})(-(.*' . CRLF . ')+\\1)? [^' . CRLF . ']+' . CRLF . '$/', $this->response, $parts) && time() < $endTime);
1687  
1688          // Catch a timeout or bad response
1689          if (!isset($parts[1]))
1690          {
1691              throw new FilesystemException(
1692                  sprintf(
1693                      '%1$s: Timeout or unrecognised response while waiting for a response from the server. Server response: %2$s',
1694                      __METHOD__, $this->response
1695                  )
1696              );
1697          }
1698  
1699          // Separate the code from the message
1700          $this->responseCode = $parts[1];
1701          $this->responseMsg  = $parts[0];
1702  
1703          // If it's not 227, we weren't given an IP and port, which means it failed.
1704          if ($this->responseCode != 227)
1705          {
1706              throw new FilesystemException(
1707                  sprintf('%1$s: Unable to obtain IP and port for data transfer. Server response: %2$s', __METHOD__, $this->responseMsg)
1708              );
1709          }
1710  
1711          // Snatch the IP and port information, or die horribly trying...
1712          if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $this->responseMsg, $match) == 0)
1713          {
1714              throw new FilesystemException(
1715                  sprintf('%1$s: IP and port for data transfer not valid. Server response: %2$s', __METHOD__, $this->responseMsg)
1716              );
1717          }
1718  
1719          // This is pretty simple - store it for later use ;).
1720          $this->pasv = ['ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], 'port' => $match[5] * 256 + $match[6]];
1721  
1722          // Connect, assuming we've got a connection.
1723          $this->dataconn = @fsockopen($this->pasv['ip'], $this->pasv['port'], $errno, $err, $this->timeout);
1724  
1725          if (!$this->dataconn)
1726          {
1727              throw new FilesystemException(
1728                  sprintf(
1729                      '%1$s: Could not connect to host %2$s on port %3$s. Socket error number: %4$s and error message: %5$s',
1730                      __METHOD__,
1731                      $this->pasv['ip'],
1732                      $this->pasv['port'],
1733                      $errno,
1734                      $err
1735                  )
1736              );
1737          }
1738  
1739          // Set the timeout for this connection
1740          socket_set_timeout($this->conn, $this->timeout, 0);
1741  
1742          return true;
1743      }
1744  
1745      /**
1746       * Method to find out the correct transfer mode for a specific file
1747       *
1748       * @param   string  $fileName  Name of the file
1749       *
1750       * @return  integer Transfer-mode for this filetype [FTP_ASCII|FTP_BINARY]
1751       *
1752       * @since   1.0
1753       */
1754  	protected function _findMode($fileName)
1755      {
1756          if ($this->type == FTP_AUTOASCII)
1757          {
1758              $dot = strrpos($fileName, '.') + 1;
1759              $ext = substr($fileName, $dot);
1760  
1761              if (\in_array($ext, $this->autoAscii))
1762              {
1763                  $mode = \FTP_ASCII;
1764              }
1765              else
1766              {
1767                  $mode = \FTP_BINARY;
1768              }
1769          }
1770          elseif ($this->type == \FTP_ASCII)
1771          {
1772              $mode = \FTP_ASCII;
1773          }
1774          else
1775          {
1776              $mode = \FTP_BINARY;
1777          }
1778  
1779          return $mode;
1780      }
1781  
1782      /**
1783       * Set transfer mode
1784       *
1785       * @param   integer  $mode  Integer representation of data transfer mode [1:Binary|0:Ascii]
1786       *                          Defined constants can also be used [FTP_BINARY|FTP_ASCII]
1787       *
1788       * @return  boolean  True if successful
1789       *
1790       * @since   1.0
1791       * @throws  FilesystemException
1792       */
1793  	protected function _mode($mode)
1794      {
1795          if ($mode == \FTP_BINARY)
1796          {
1797              if (!$this->_putCmd('TYPE I', 200))
1798              {
1799                  throw new FilesystemException(
1800                      sprintf('%1$s: Bad response. Server response: %2$s [Expected: 200]. Mode sent: Binary', __METHOD__, $this->response)
1801                  );
1802              }
1803          }
1804          else
1805          {
1806              if (!$this->_putCmd('TYPE A', 200))
1807              {
1808                  throw new FilesystemException(
1809                      sprintf('%1$s: Bad response. Server response: %2$s [Expected: 200]. Mode sent: ASCII', __METHOD__, $this->response)
1810                  );
1811              }
1812          }
1813  
1814          return true;
1815      }
1816  }


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