Source for file ftp.php

Documentation is available at ftp.php

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

Documentation generated on Tue, 19 Nov 2013 15:03:56 +0100 by phpDocumentor 1.4.3