Joomla Platform  13.1
Documentation des API du framework Joomla Platform
 Tout Classes Espaces de nommage Fichiers Fonctions Variables Pages
ftp.php
Aller à la documentation de ce fichier.
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  */
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 }