[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Client/ -> FtpClient.php (source)

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


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