1 <?php
2 3 4 5 6 7 8
9
10 defined('JPATH_PLATFORM') or die;
11
12 13 14 15 16 17 18 19 20 21 22
23
24 if (!defined('CRLF'))
25 {
26 define('CRLF', "\r\n");
27 }
28
29 if (!defined('FTP_AUTOASCII'))
30 {
31 define('FTP_AUTOASCII', -1);
32 }
33
34 if (!defined('FTP_BINARY'))
35 {
36 define('FTP_BINARY', 1);
37 }
38
39 if (!defined('FTP_ASCII'))
40 {
41 define('FTP_ASCII', 0);
42 }
43
44 if (!defined('FTP_NATIVE'))
45 {
46 define('FTP_NATIVE', (function_exists('ftp_connect')) ? 1 : 0);
47 }
48
49 50 51 52 53
54 class JClientFtp
55 {
56 57 58 59
60 protected $_conn = null;
61
62 63 64 65
66 protected $_dataconn = null;
67
68 69 70 71
72 protected $_pasv = null;
73
74 75 76 77
78 protected $_response = null;
79
80 81 82 83
84 protected $_timeout = 15;
85
86 87 88 89
90 protected $_type = null;
91
92 93 94 95
96 protected $_autoAscii = array(
97 'asp',
98 'bat',
99 'c',
100 'cpp',
101 'csv',
102 'h',
103 'htm',
104 'html',
105 'shtml',
106 'ini',
107 'inc',
108 'log',
109 'php',
110 'php3',
111 'pl',
112 'perl',
113 'sh',
114 'sql',
115 'txt',
116 'xhtml',
117 'xml',
118 );
119
120 121 122 123 124 125
126 protected $_lineEndings = array('UNIX' => "\n", 'WIN' => "\r\n");
127
128 129 130 131
132 protected static $instances = array();
133
134 135 136 137 138 139 140
141 public function __construct(array $options = array())
142 {
143
144 if (!isset($options['type']))
145 {
146 $options['type'] = FTP_BINARY;
147 }
148
149 $this->setOptions($options);
150
151 if (FTP_NATIVE)
152 {
153
154 jimport('joomla.utilities.buffer');
155
156
157 JLoader::load('JBuffer');
158 }
159 }
160
161 162 163 164 165 166 167
168 public function __destruct()
169 {
170 if (is_resource($this->_conn))
171 {
172 $this->quit();
173 }
174 }
175
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
195 public static function getInstance($host = '127.0.0.1', $port = '21', array $options = array(), $user = null, $pass = null)
196 {
197 $signature = $user . ':' . $pass . '@' . $host . ':' . $port;
198
199
200 if (!isset(static::$instances[$signature]) || !is_object(static::$instances[$signature]))
201 {
202 static::$instances[$signature] = new static($options);
203 }
204 else
205 {
206 static::$instances[$signature]->setOptions($options);
207 }
208
209
210 if (!static::$instances[$signature]->isConnected())
211 {
212 $return = static::$instances[$signature]->connect($host, $port);
213
214 if ($return && $user !== null && $pass !== null)
215 {
216 static::$instances[$signature]->login($user, $pass);
217 }
218 }
219
220 return static::$instances[$signature];
221 }
222
223 224 225 226 227 228 229 230 231
232 public function setOptions(array $options)
233 {
234 if (isset($options['type']))
235 {
236 $this->_type = $options['type'];
237 }
238
239 if (isset($options['timeout']))
240 {
241 $this->_timeout = $options['timeout'];
242 }
243
244 return true;
245 }
246
247 248 249 250 251 252 253 254 255 256
257 public function connect($host = '127.0.0.1', $port = 21)
258 {
259 $errno = null;
260 $err = null;
261
262
263 if (is_resource($this->_conn))
264 {
265 return true;
266 }
267
268
269 if (FTP_NATIVE)
270 {
271 $this->_conn = @ftp_connect($host, $port, $this->_timeout);
272
273 if ($this->_conn === false)
274 {
275 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_NO_CONNECT', $host, $port), JLog::WARNING, 'jerror');
276
277 return false;
278 }
279
280 ftp_set_option($this->_conn, FTP_TIMEOUT_SEC, $this->_timeout);
281
282 return true;
283 }
284
285
286 $this->_conn = @ fsockopen($host, $port, $errno, $err, $this->_timeout);
287
288 if (!$this->_conn)
289 {
290 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_NO_CONNECT_SOCKET', $host, $port, $errno, $err), JLog::WARNING, 'jerror');
291
292 return false;
293 }
294
295
296 socket_set_timeout($this->_conn, $this->_timeout, 0);
297
298
299 if (!$this->_verifyResponse(220))
300 {
301 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_BAD_RESPONSE', $this->_response), JLog::WARNING, 'jerror');
302
303 return false;
304 }
305
306 return true;
307 }
308
309 310 311 312 313 314 315
316 public function isConnected()
317 {
318 return is_resource($this->_conn);
319 }
320
321 322 323 324 325 326 327 328 329 330
331 public function login($user = 'anonymous', $pass = 'jftp@joomla.org')
332 {
333
334 if (FTP_NATIVE)
335 {
336 if (@ftp_login($this->_conn, $user, $pass) === false)
337 {
338 JLog::add('JFtp::login: Unable to login', JLog::WARNING, 'jerror');
339
340 return false;
341 }
342
343 return true;
344 }
345
346
347 if (!$this->_putCmd('USER ' . $user, array(331, 503)))
348 {
349 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_BAD_USERNAME', $this->_response, $user), JLog::WARNING, 'jerror');
350
351 return false;
352 }
353
354
355 if ($this->_responseCode == 503)
356 {
357 return true;
358 }
359
360
361 if (!$this->_putCmd('PASS ' . $pass, 230))
362 {
363 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_BAD_PASSWORD', $this->_response, str_repeat('*', strlen($pass))), JLog::WARNING, 'jerror');
364
365 return false;
366 }
367
368 return true;
369 }
370
371 372 373 374 375 376 377
378 public function quit()
379 {
380
381 if (FTP_NATIVE)
382 {
383 @ftp_close($this->_conn);
384
385 return true;
386 }
387
388
389 @fwrite($this->_conn, "QUIT\r\n");
390 @fclose($this->_conn);
391
392 return true;
393 }
394
395 396 397 398 399 400 401
402 public function pwd()
403 {
404
405 if (FTP_NATIVE)
406 {
407 if (($ret = @ftp_pwd($this->_conn)) === false)
408 {
409 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_PWD_BAD_RESPONSE_NATIVE'), JLog::WARNING, 'jerror');
410
411 return false;
412 }
413
414 return $ret;
415 }
416
417 $match = array(null);
418
419
420 if (!$this->_putCmd('PWD', 257))
421 {
422 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_PWD_BAD_RESPONSE', $this->_response), JLog::WARNING, 'jerror');
423
424 return false;
425 }
426
427
428 preg_match('/"[^"\r\n]*"/', $this->_response, $match);
429
430
431 return preg_replace("/\"/", '', $match[0]);
432 }
433
434 435 436 437 438 439 440
441 public function syst()
442 {
443
444 if (FTP_NATIVE)
445 {
446 if (($ret = @ftp_systype($this->_conn)) === false)
447 {
448 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_SYST_BAD_RESPONSE_NATIVE'), JLog::WARNING, 'jerror');
449
450 return false;
451 }
452 }
453 else
454 {
455
456 if (!$this->_putCmd('SYST', 215))
457 {
458 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_SYST_BAD_RESPONSE', $this->_response), JLog::WARNING, 'jerror');
459
460 return false;
461 }
462
463 $ret = $this->_response;
464 }
465
466
467 if (strpos(strtoupper($ret), 'MAC') !== false)
468 {
469 $ret = 'MAC';
470 }
471 elseif (strpos(strtoupper($ret), 'WIN') !== false)
472 {
473 $ret = 'WIN';
474 }
475 else
476 {
477 $ret = 'UNIX';
478 }
479
480
481 return $ret;
482 }
483
484 485 486 487 488 489 490 491 492
493 public function chdir($path)
494 {
495
496 if (FTP_NATIVE)
497 {
498 if (@ftp_chdir($this->_conn, $path) === false)
499 {
500 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_CHDIR_BAD_RESPONSE_NATIVE'), JLog::WARNING, 'jerror');
501
502 return false;
503 }
504
505 return true;
506 }
507
508
509 if (!$this->_putCmd('CWD ' . $path, 250))
510 {
511 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_CHDIR_BAD_RESPONSE', $this->_response, $path), JLog::WARNING, 'jerror');
512
513 return false;
514 }
515
516 return true;
517 }
518
519 520 521 522 523 524 525 526 527
528 public function reinit()
529 {
530
531 if (FTP_NATIVE)
532 {
533 if (@ftp_site($this->_conn, 'REIN') === false)
534 {
535 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_REINIT_BAD_RESPONSE_NATIVE'), JLog::WARNING, 'jerror');
536
537 return false;
538 }
539
540 return true;
541 }
542
543
544 if (!$this->_putCmd('REIN', 220))
545 {
546 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_REINIT_BAD_RESPONSE', $this->_response), JLog::WARNING, 'jerror');
547
548 return false;
549 }
550
551 return true;
552 }
553
554 555 556 557 558 559 560 561 562 563
564 public function rename($from, $to)
565 {
566
567 if (FTP_NATIVE)
568 {
569 if (@ftp_rename($this->_conn, $from, $to) === false)
570 {
571 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_RENAME_BAD_RESPONSE_NATIVE'), JLog::WARNING, 'jerror');
572
573 return false;
574 }
575
576 return true;
577 }
578
579
580 if (!$this->_putCmd('RNFR ' . $from, 350))
581 {
582 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_RENAME_BAD_RESPONSE_FROM', $this->_response, $from), JLog::WARNING, 'jerror');
583
584 return false;
585 }
586
587
588 if (!$this->_putCmd('RNTO ' . $to, 250))
589 {
590 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_RENAME_BAD_RESPONSE_TO', $this->_response, $to), JLog::WARNING, 'jerror');
591
592 return false;
593 }
594
595 return true;
596 }
597
598 599 600 601 602 603 604 605 606 607
608 public function chmod($path, $mode)
609 {
610
611 if ($path == '')
612 {
613 $path = '.';
614 }
615
616
617 if (is_int($mode))
618 {
619 $mode = decoct($mode);
620 }
621
622
623 if (FTP_NATIVE)
624 {
625 if (@ftp_site($this->_conn, 'CHMOD ' . $mode . ' ' . $path) === false)
626 {
627 if (!IS_WIN)
628 {
629 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_CHMOD_BAD_RESPONSE_NATIVE'), JLog::WARNING, 'jerror');
630 }
631
632 return false;
633 }
634
635 return true;
636 }
637
638
639 if (!$this->_putCmd('SITE CHMOD ' . $mode . ' ' . $path, array(200, 250)))
640 {
641 if (!IS_WIN)
642 {
643 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_CHMOD_BAD_RESPONSE', $this->_response, $path, $mode), JLog::WARNING, 'jerror');
644 }
645
646 return false;
647 }
648
649 return true;
650 }
651
652 653 654 655 656 657 658 659 660
661 public function delete($path)
662 {
663
664 if (FTP_NATIVE)
665 {
666 if (@ftp_delete($this->_conn, $path) === false)
667 {
668 if (@ftp_rmdir($this->_conn, $path) === false)
669 {
670 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_DELETE_BAD_RESPONSE_NATIVE'), JLog::WARNING, 'jerror');
671
672 return false;
673 }
674 }
675
676 return true;
677 }
678
679
680 if (!$this->_putCmd('DELE ' . $path, 250))
681 {
682 if (!$this->_putCmd('RMD ' . $path, 250))
683 {
684 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_DELETE_BAD_RESPONSE', $this->_response, $path), JLog::WARNING, 'jerror');
685
686 return false;
687 }
688 }
689
690 return true;
691 }
692
693 694 695 696 697 698 699 700 701
702 public function mkdir($path)
703 {
704
705 if (FTP_NATIVE)
706 {
707 if (@ftp_mkdir($this->_conn, $path) === false)
708 {
709 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_MKDIR_BAD_RESPONSE_NATIVE'), JLog::WARNING, 'jerror');
710
711 return false;
712 }
713
714 return true;
715 }
716
717
718 if (!$this->_putCmd('MKD ' . $path, 257))
719 {
720 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_MKDIR_BAD_RESPONSE', $this->_response, $path), JLog::WARNING, 'jerror');
721
722 return false;
723 }
724
725 return true;
726 }
727
728 729 730 731 732 733 734 735 736
737 public function restart($point)
738 {
739
740 if (FTP_NATIVE)
741 {
742 if (@ftp_site($this->_conn, 'REST ' . $point) === false)
743 {
744 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_RESTART_BAD_RESPONSE_NATIVE'), JLog::WARNING, 'jerror');
745
746 return false;
747 }
748
749 return true;
750 }
751
752
753 if (!$this->_putCmd('REST ' . $point, 350))
754 {
755 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_RESTART_BAD_RESPONSE', $this->_response, $point), JLog::WARNING, 'jerror');
756
757 return false;
758 }
759
760 return true;
761 }
762
763 764 765 766 767 768 769 770 771
772 public function create($path)
773 {
774
775 if (FTP_NATIVE)
776 {
777
778 if (@ftp_pasv($this->_conn, true) === false)
779 {
780 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_CREATE_BAD_RESPONSE_PASSIVE'), JLog::WARNING, 'jerror');
781
782 return false;
783 }
784
785 $buffer = fopen('buffer://tmp', 'r');
786
787 if (@ftp_fput($this->_conn, $path, $buffer, FTP_ASCII) === false)
788 {
789 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_CREATE_BAD_RESPONSE_BUFFER'), JLog::WARNING, 'jerror');
790 fclose($buffer);
791
792 return false;
793 }
794
795 fclose($buffer);
796
797 return true;
798 }
799
800
801 if (!$this->_passive())
802 {
803 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_CREATE_BAD_RESPONSE_PASSIVE'), JLog::WARNING, 'jerror');
804
805 return false;
806 }
807
808 if (!$this->_putCmd('STOR ' . $path, array(150, 125)))
809 {
810 @ fclose($this->_dataconn);
811 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_CREATE_BAD_RESPONSE', $this->_response, $path), JLog::WARNING, 'jerror');
812
813 return false;
814 }
815
816
817 fclose($this->_dataconn);
818
819 if (!$this->_verifyResponse(226))
820 {
821 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_CREATE_BAD_RESPONSE_TRANSFER', $this->_response, $path), JLog::WARNING, 'jerror');
822
823 return false;
824 }
825
826 return true;
827 }
828
829 830 831 832 833 834 835 836 837 838
839 public function read($remote, &$buffer)
840 {
841
842 $mode = $this->_findMode($remote);
843
844
845 if (FTP_NATIVE)
846 {
847
848 if (@ftp_pasv($this->_conn, true) === false)
849 {
850 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_READ_BAD_RESPONSE_PASSIVE'), JLog::WARNING, 'jerror');
851
852 return false;
853 }
854
855 $tmp = fopen('buffer://tmp', 'br+');
856
857 if (@ftp_fget($this->_conn, $tmp, $remote, $mode) === false)
858 {
859 fclose($tmp);
860 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_READ_BAD_RESPONSE_BUFFER'), JLog::WARNING, 'jerror');
861
862 return false;
863 }
864
865 rewind($tmp);
866 $buffer = '';
867
868 while (!feof($tmp))
869 {
870 $buffer .= fread($tmp, 8192);
871 }
872
873 fclose($tmp);
874
875 return true;
876 }
877
878 $this->_mode($mode);
879
880
881 if (!$this->_passive())
882 {
883 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_READ_BAD_RESPONSE_PASSIVE'), JLog::WARNING, 'jerror');
884
885 return false;
886 }
887
888 if (!$this->_putCmd('RETR ' . $remote, array(150, 125)))
889 {
890 @ fclose($this->_dataconn);
891 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_READ_BAD_RESPONSE', $this->_response, $remote), JLog::WARNING, 'jerror');
892
893 return false;
894 }
895
896
897 $buffer = '';
898
899 while (!feof($this->_dataconn))
900 {
901 $buffer .= fread($this->_dataconn, 4096);
902 }
903
904
905 fclose($this->_dataconn);
906
907
908 if ($mode == FTP_ASCII)
909 {
910 $os = 'UNIX';
911
912 if (IS_WIN)
913 {
914 $os = 'WIN';
915 }
916
917 $buffer = preg_replace('/' . CRLF . '/', $this->_lineEndings[$os], $buffer);
918 }
919
920 if (!$this->_verifyResponse(226))
921 {
922 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_READ_BAD_RESPONSE_TRANSFER', $this->_response, $remote), JLog::WARNING, 'jerror');
923
924 return false;
925 }
926
927 return true;
928 }
929
930 931 932 933 934 935 936 937 938 939
940 public function get($local, $remote)
941 {
942
943 $mode = $this->_findMode($remote);
944
945
946 if (FTP_NATIVE)
947 {
948
949 if (@ftp_pasv($this->_conn, true) === false)
950 {
951 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_GET_PASSIVE'), JLog::WARNING, 'jerror');
952
953 return false;
954 }
955
956 if (@ftp_get($this->_conn, $local, $remote, $mode) === false)
957 {
958 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_GET_BAD_RESPONSE'), JLog::WARNING, 'jerror');
959
960 return false;
961 }
962
963 return true;
964 }
965
966 $this->_mode($mode);
967
968
969 $fp = fopen($local, 'wb');
970
971 if (!$fp)
972 {
973 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_GET_WRITING_LOCAL', $local), JLog::WARNING, 'jerror');
974
975 return false;
976 }
977
978
979 if (!$this->_passive())
980 {
981 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_GET_PASSIVE'), JLog::WARNING, 'jerror');
982
983 return false;
984 }
985
986 if (!$this->_putCmd('RETR ' . $remote, array(150, 125)))
987 {
988 @ fclose($this->_dataconn);
989 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_GET_BAD_RESPONSE_RETR', $this->_response, $remote), JLog::WARNING, 'jerror');
990
991 return false;
992 }
993
994
995 while (!feof($this->_dataconn))
996 {
997 $buffer = fread($this->_dataconn, 4096);
998 fwrite($fp, $buffer, 4096);
999 }
1000
1001
1002 fclose($this->_dataconn);
1003 fclose($fp);
1004
1005 if (!$this->_verifyResponse(226))
1006 {
1007 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_GET_BAD_RESPONSE_TRANSFER', $this->_response, $remote), JLog::WARNING, 'jerror');
1008
1009 return false;
1010 }
1011
1012 return true;
1013 }
1014
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
1025 public function store($local, $remote = null)
1026 {
1027
1028
1029 if ($remote == null)
1030 {
1031 $remote = basename($local);
1032 }
1033
1034
1035 $mode = $this->_findMode($remote);
1036
1037
1038 if (FTP_NATIVE)
1039 {
1040
1041 if (@ftp_pasv($this->_conn, true) === false)
1042 {
1043 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_STORE_PASSIVE'), JLog::WARNING, 'jerror');
1044
1045 return false;
1046 }
1047
1048 if (@ftp_put($this->_conn, $remote, $local, $mode) === false)
1049 {
1050 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_STORE_BAD_RESPONSE'), JLog::WARNING, 'jerror');
1051
1052 return false;
1053 }
1054
1055 return true;
1056 }
1057
1058 $this->_mode($mode);
1059
1060
1061 if (@ file_exists($local))
1062 {
1063 $fp = fopen($local, 'rb');
1064
1065 if (!$fp)
1066 {
1067 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_STORE_READING_LOCAL', $local), JLog::WARNING, 'jerror');
1068
1069 return false;
1070 }
1071 }
1072 else
1073 {
1074 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_STORE_FIND_LOCAL', $local), JLog::WARNING, 'jerror');
1075
1076 return false;
1077 }
1078
1079
1080 if (!$this->_passive())
1081 {
1082 @ fclose($fp);
1083 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_STORE_PASSIVE'), JLog::WARNING, 'jerror');
1084
1085 return false;
1086 }
1087
1088
1089 if (!$this->_putCmd('STOR ' . $remote, array(150, 125)))
1090 {
1091 @ fclose($fp);
1092 @ fclose($this->_dataconn);
1093 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_STORE_BAD_RESPONSE_STOR', $this->_response, $remote), JLog::WARNING, 'jerror');
1094
1095 return false;
1096 }
1097
1098
1099 while (!feof($fp))
1100 {
1101 $line = fread($fp, 4096);
1102
1103 do
1104 {
1105 if (($result = @ fwrite($this->_dataconn, $line)) === false)
1106 {
1107 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_STORE_DATA_PORT'), JLog::WARNING, 'jerror');
1108
1109 return false;
1110 }
1111
1112 $line = substr($line, $result);
1113 }
1114 while ($line != '');
1115 }
1116
1117 fclose($fp);
1118 fclose($this->_dataconn);
1119
1120 if (!$this->_verifyResponse(226))
1121 {
1122 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_STORE_BAD_RESPONSE_TRANSFER', $this->_response, $remote), JLog::WARNING, 'jerror');
1123
1124 return false;
1125 }
1126
1127 return true;
1128 }
1129
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139
1140 public function write($remote, $buffer)
1141 {
1142
1143 $mode = $this->_findMode($remote);
1144
1145
1146 if (FTP_NATIVE)
1147 {
1148
1149 if (@ftp_pasv($this->_conn, true) === false)
1150 {
1151 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_WRITE_PASSIVE'), JLog::WARNING, 'jerror');
1152
1153 return false;
1154 }
1155
1156 $tmp = fopen('buffer://tmp', 'br+');
1157 fwrite($tmp, $buffer);
1158 rewind($tmp);
1159
1160 if (@ftp_fput($this->_conn, $remote, $tmp, $mode) === false)
1161 {
1162 fclose($tmp);
1163 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_WRITE_BAD_RESPONSE'), JLog::WARNING, 'jerror');
1164
1165 return false;
1166 }
1167
1168 fclose($tmp);
1169
1170 return true;
1171 }
1172
1173
1174 $this->_mode($mode);
1175
1176
1177 if (!$this->_passive())
1178 {
1179 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_WRITE_PASSIVE'), JLog::WARNING, 'jerror');
1180
1181 return false;
1182 }
1183
1184
1185 if (!$this->_putCmd('STOR ' . $remote, array(150, 125)))
1186 {
1187 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_WRITE_BAD_RESPONSE_STOR', $this->_response, $remote), JLog::WARNING, 'jerror');
1188 @ fclose($this->_dataconn);
1189
1190 return false;
1191 }
1192
1193
1194 do
1195 {
1196 if (($result = @ fwrite($this->_dataconn, $buffer)) === false)
1197 {
1198 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_WRITE_DATA_PORT'), JLog::WARNING, 'jerror');
1199
1200 return false;
1201 }
1202
1203 $buffer = substr($buffer, $result);
1204 }
1205 while ($buffer != '');
1206
1207
1208 fclose($this->_dataconn);
1209
1210
1211 if (!$this->_verifyResponse(226))
1212 {
1213 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_WRITE_BAD_RESPONSE_TRANSFER', $this->_response, $remote), JLog::WARNING, 'jerror');
1214
1215 return false;
1216 }
1217
1218 return true;
1219 }
1220
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230
1231 public function append($remote, $buffer)
1232 {
1233
1234 $mode = $this->_findMode($remote);
1235
1236
1237 if (FTP_NATIVE)
1238 {
1239
1240 if (@ftp_pasv($this->_conn, true) === false)
1241 {
1242 throw new RuntimeException(JText::_('JLIB_CLIENT_ERROR_JFTP_APPEND_PASSIVE'), 36);
1243 }
1244
1245 $tmp = fopen('buffer://tmp', 'bw+');
1246 fwrite($tmp, $buffer);
1247 rewind($tmp);
1248
1249 $size = $this->size($remote);
1250
1251 if ($size === false)
1252 {
1253 }
1254
1255 if (@ftp_fput($this->_conn, $remote, $tmp, $mode, $size) === false)
1256 {
1257 fclose($tmp);
1258
1259 throw new RuntimeException(JText::_('JLIB_CLIENT_ERROR_JFTP_APPEND_BAD_RESPONSE'), 35);
1260 }
1261
1262 fclose($tmp);
1263
1264 return true;
1265 }
1266
1267
1268 $this->_mode($mode);
1269
1270
1271 if (!$this->_passive())
1272 {
1273 throw new RuntimeException(JText::_('JLIB_CLIENT_ERROR_JFTP_APPEND_PASSIVE'), 36);
1274 }
1275
1276
1277 if (!$this->_putCmd('APPE ' . $remote, array(150, 125)))
1278 {
1279 @fclose($this->_dataconn);
1280
1281 throw new RuntimeException(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_APPEND_BAD_RESPONSE_APPE', $this->_response, $remote), 35);
1282 }
1283
1284
1285 do
1286 {
1287 if (($result = @ fwrite($this->_dataconn, $buffer)) === false)
1288 {
1289 throw new RuntimeException(JText::_('JLIB_CLIENT_ERROR_JFTP_APPEND_DATA_PORT'), 37);
1290 }
1291
1292 $buffer = substr($buffer, $result);
1293 }
1294 while ($buffer != '');
1295
1296
1297 fclose($this->_dataconn);
1298
1299
1300 if (!$this->_verifyResponse(226))
1301 {
1302 throw new RuntimeException(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_APPEND_BAD_RESPONSE_TRANSFER', $this->_response, $remote), 37);
1303 }
1304
1305 return true;
1306 }
1307
1308 1309 1310 1311 1312 1313 1314 1315 1316
1317 public function size($remote)
1318 {
1319 if (FTP_NATIVE)
1320 {
1321 $size = ftp_size($this->_conn, $remote);
1322
1323
1324 if ($size === -1)
1325 {
1326 $response = ftp_raw($this->_conn, 'SIZE ' . $remote);
1327 $responseCode = substr($response[0], 0, 3);
1328 $responseMessage = substr($response[0], 4);
1329
1330 if ($responseCode != '213')
1331 {
1332 throw new RuntimeException(JText::_('JLIB_CLIENT_ERROR_JFTP_SIZE_BAD_RESPONSE'), 35);
1333 }
1334
1335 $size = (int) $responseMessage;
1336 }
1337
1338 return $size;
1339 }
1340
1341
1342 if (!$this->_passive())
1343 {
1344 throw new RuntimeException(JText::_('JLIB_CLIENT_ERROR_JFTP_SIZE_PASSIVE'), 36);
1345 }
1346
1347
1348 if (!$this->_putCmd('SIZE ' . $remote, array(213)))
1349 {
1350 @fclose($this->_dataconn);
1351
1352 throw new RuntimeException(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_SIZE_BAD_RESPONSE', $this->_response, $remote), 35);
1353 }
1354
1355 return (int) substr($this->_responseMsg, 4);
1356 }
1357
1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369
1370 public function listNames($path = null)
1371 {
1372 $data = null;
1373
1374
1375 if (FTP_NATIVE)
1376 {
1377
1378 if (@ftp_pasv($this->_conn, true) === false)
1379 {
1380 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_LISTNAMES_PASSIVE'), JLog::WARNING, 'jerror');
1381
1382 return false;
1383 }
1384
1385 if (($list = @ftp_nlist($this->_conn, $path)) === false)
1386 {
1387
1388 if ($this->listDetails($path, 'files') === array())
1389 {
1390 return array();
1391 }
1392
1393 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_LISTNAMES_BAD_RESPONSE'), JLog::WARNING, 'jerror');
1394
1395 return false;
1396 }
1397
1398 $list = preg_replace('#^' . preg_quote($path, '#') . '[/\\\\]?#', '', $list);
1399
1400 if ($keys = array_merge(array_keys($list, '.'), array_keys($list, '..')))
1401 {
1402 foreach ($keys as $key)
1403 {
1404 unset($list[$key]);
1405 }
1406 }
1407
1408 return $list;
1409 }
1410
1411
1412 if ($path != null)
1413 {
1414 $path = ' ' . $path;
1415 }
1416
1417
1418 if (!$this->_passive())
1419 {
1420 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_LISTNAMES_PASSIVE'), JLog::WARNING, 'jerror');
1421
1422 return false;
1423 }
1424
1425 if (!$this->_putCmd('NLST' . $path, array(150, 125)))
1426 {
1427 @ fclose($this->_dataconn);
1428
1429
1430 if ($this->listDetails($path, 'files') === array())
1431 {
1432 return array();
1433 }
1434
1435 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_LISTNAMES_BAD_RESPONSE_NLST', $this->_response, $path), JLog::WARNING, 'jerror');
1436
1437 return false;
1438 }
1439
1440
1441 while (!feof($this->_dataconn))
1442 {
1443 $data .= fread($this->_dataconn, 4096);
1444 }
1445
1446 fclose($this->_dataconn);
1447
1448
1449 if (!$this->_verifyResponse(226))
1450 {
1451 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_LISTNAMES_BAD_RESPONSE_TRANSFER', $this->_response, $path), JLog::WARNING, 'jerror');
1452
1453 return false;
1454 }
1455
1456 $data = preg_split('/[' . CRLF . ']+/', $data, -1, PREG_SPLIT_NO_EMPTY);
1457 $data = preg_replace('#^' . preg_quote(substr($path, 1), '#') . '[/\\\\]?#', '', $data);
1458
1459 if ($keys = array_merge(array_keys($data, '.'), array_keys($data, '..')))
1460 {
1461 foreach ($keys as $key)
1462 {
1463 unset($data[$key]);
1464 }
1465 }
1466
1467 return $data;
1468 }
1469
1470 1471 1472 1473 1474 1475 1476 1477 1478 1479
1480 public function listDetails($path = null, $type = 'all')
1481 {
1482 $dir_list = array();
1483 $data = null;
1484 $regs = null;
1485
1486
1487
1488 $recurse = false;
1489
1490
1491 if (FTP_NATIVE)
1492 {
1493
1494 if (@ftp_pasv($this->_conn, true) === false)
1495 {
1496 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_LISTDETAILS_PASSIVE'), JLog::WARNING, 'jerror');
1497
1498 return false;
1499 }
1500
1501 if (($contents = @ftp_rawlist($this->_conn, $path)) === false)
1502 {
1503 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_LISTDETAILS_BAD_RESPONSE'), JLog::WARNING, 'jerror');
1504
1505 return false;
1506 }
1507 }
1508 else
1509 {
1510
1511
1512
1513 if (!$this->_passive())
1514 {
1515 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_LISTDETAILS_PASSIVE'), JLog::WARNING, 'jerror');
1516
1517 return false;
1518 }
1519
1520
1521 if ($path != null)
1522 {
1523 $path = ' ' . $path;
1524 }
1525
1526
1527 if (!$this->_putCmd(($recurse == true) ? 'LIST -R' : 'LIST' . $path, array(150, 125)))
1528 {
1529 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_LISTDETAILS_BAD_RESPONSE_LIST', $this->_response, $path), JLog::WARNING, 'jerror');
1530 @ fclose($this->_dataconn);
1531
1532 return false;
1533 }
1534
1535
1536 while (!feof($this->_dataconn))
1537 {
1538 $data .= fread($this->_dataconn, 4096);
1539 }
1540
1541 fclose($this->_dataconn);
1542
1543
1544 if (!$this->_verifyResponse(226))
1545 {
1546 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_LISTDETAILS_BAD_RESPONSE_TRANSFER', $this->_response, $path), JLog::WARNING, 'jerror');
1547
1548 return false;
1549 }
1550
1551 $contents = explode(CRLF, $data);
1552 }
1553
1554
1555 if ($type == 'raw')
1556 {
1557 return $data;
1558 }
1559
1560
1561 if (empty($contents[0]))
1562 {
1563 return $dir_list;
1564 }
1565
1566
1567 if (strtolower(substr($contents[0], 0, 6)) == 'total ')
1568 {
1569 array_shift($contents);
1570
1571 if (!isset($contents[0]) || empty($contents[0]))
1572 {
1573 return $dir_list;
1574 }
1575 }
1576
1577
1578 $regexps = array(
1579 'UNIX' => '#([-dl][rwxstST-]+).* ([0-9]*) ([a-zA-Z0-9]+).* ([a-zA-Z0-9]+).* ([0-9]*)'
1580 . ' ([a-zA-Z]+[0-9: ]*[0-9])[ ]+(([0-9]{1,2}:[0-9]{2})|[0-9]{4}) (.+)#',
1581 'MAC' => '#([-dl][rwxstST-]+).* ?([0-9 ]*)?([a-zA-Z0-9]+).* ([a-zA-Z0-9]+).* ([0-9]*)'
1582 . ' ([a-zA-Z]+[0-9: ]*[0-9])[ ]+(([0-9]{2}:[0-9]{2})|[0-9]{4}) (.+)#',
1583 'WIN' => '#([0-9]{2})-([0-9]{2})-([0-9]{2}) +([0-9]{2}):([0-9]{2})(AM|PM) +([0-9]+|<DIR>) +(.+)#',
1584 );
1585
1586
1587 $osType = null;
1588
1589 foreach ($regexps as $k => $v)
1590 {
1591 if (@preg_match($v, $contents[0]))
1592 {
1593 $osType = $k;
1594 $regexp = $v;
1595 break;
1596 }
1597 }
1598
1599 if (!$osType)
1600 {
1601 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_LISTDETAILS_UNRECOGNISED'), JLog::WARNING, 'jerror');
1602
1603 return false;
1604 }
1605
1606
1607 if ($osType == 'UNIX' || $osType == 'MAC')
1608 {
1609 foreach ($contents as $file)
1610 {
1611 $tmp_array = null;
1612
1613 if (@preg_match($regexp, $file, $regs))
1614 {
1615 $fType = (int) strpos('-dl', $regs[1]{0});
1616
1617
1618 $tmp_array['type'] = $fType;
1619 $tmp_array['rights'] = $regs[1];
1620
1621
1622 $tmp_array['user'] = $regs[3];
1623 $tmp_array['group'] = $regs[4];
1624 $tmp_array['size'] = $regs[5];
1625 $tmp_array['date'] = @date('m-d', strtotime($regs[6]));
1626 $tmp_array['time'] = $regs[7];
1627 $tmp_array['name'] = $regs[9];
1628 }
1629
1630
1631 if ($type == 'files' && $tmp_array['type'] == 1)
1632 {
1633 continue;
1634 }
1635
1636
1637 if ($type == 'folders' && $tmp_array['type'] == 0)
1638 {
1639 continue;
1640 }
1641
1642 if (is_array($tmp_array) && $tmp_array['name'] != '.' && $tmp_array['name'] != '..')
1643 {
1644 $dir_list[] = $tmp_array;
1645 }
1646 }
1647 }
1648 else
1649 {
1650 foreach ($contents as $file)
1651 {
1652 $tmp_array = null;
1653
1654 if (@preg_match($regexp, $file, $regs))
1655 {
1656 $fType = (int) ($regs[7] == '<DIR>');
1657 $timestamp = strtotime("$regs[3]-$regs[1]-$regs[2] $regs[4]:$regs[5]$regs[6]");
1658
1659
1660 $tmp_array['type'] = $fType;
1661 $tmp_array['rights'] = '';
1662
1663
1664 $tmp_array['user'] = '';
1665 $tmp_array['group'] = '';
1666 $tmp_array['size'] = (int) $regs[7];
1667 $tmp_array['date'] = date('m-d', $timestamp);
1668 $tmp_array['time'] = date('H:i', $timestamp);
1669 $tmp_array['name'] = $regs[8];
1670 }
1671
1672 if ($type == 'files' && $tmp_array['type'] == 1)
1673 {
1674 continue;
1675 }
1676
1677 if ($type == 'folders' && $tmp_array['type'] == 0)
1678 {
1679 continue;
1680 }
1681
1682 if (is_array($tmp_array) && $tmp_array['name'] != '.' && $tmp_array['name'] != '..')
1683 {
1684 $dir_list[] = $tmp_array;
1685 }
1686 }
1687 }
1688
1689 return $dir_list;
1690 }
1691
1692 1693 1694 1695 1696 1697 1698 1699 1700 1701
1702 protected function _putCmd($cmd, $expectedResponse)
1703 {
1704
1705 if (!is_resource($this->_conn))
1706 {
1707 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_PUTCMD_UNCONNECTED'), JLog::WARNING, 'jerror');
1708
1709 return false;
1710 }
1711
1712
1713 if (!fwrite($this->_conn, $cmd . "\r\n"))
1714 {
1715 JLog::add(JText::sprintf('DDD', JText::sprintf('JLIB_CLIENT_ERROR_JFTP_PUTCMD_SEND', $cmd)), JLog::WARNING, 'jerror');
1716 }
1717
1718 return $this->_verifyResponse($expectedResponse);
1719 }
1720
1721 1722 1723 1724 1725 1726 1727 1728 1729
1730 protected function _verifyResponse($expected)
1731 {
1732 $parts = null;
1733
1734
1735 $endTime = time() + $this->_timeout;
1736 $this->_response = '';
1737
1738 do
1739 {
1740 $this->_response .= fgets($this->_conn, 4096);
1741 }
1742 while (!preg_match('/^([0-9]{3})(-(.*' . CRLF . ')+\1)? [^' . CRLF . ']+' . CRLF . "$/", $this->_response, $parts) && time() < $endTime);
1743
1744
1745 if (!isset($parts[1]))
1746 {
1747 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_VERIFYRESPONSE', $this->_response), JLog::WARNING, 'jerror');
1748
1749 return false;
1750 }
1751
1752
1753 $this->_responseCode = $parts[1];
1754 $this->_responseMsg = $parts[0];
1755
1756
1757 if (is_array($expected))
1758 {
1759 if (in_array($this->_responseCode, $expected))
1760 {
1761 $retval = true;
1762 }
1763 else
1764 {
1765 $retval = false;
1766 }
1767 }
1768 else
1769 {
1770 if ($this->_responseCode == $expected)
1771 {
1772 $retval = true;
1773 }
1774 else
1775 {
1776 $retval = false;
1777 }
1778 }
1779
1780 return $retval;
1781 }
1782
1783 1784 1785 1786 1787 1788 1789
1790 protected function _passive()
1791 {
1792 $match = array();
1793 $parts = array();
1794 $errno = null;
1795 $err = null;
1796
1797
1798 if (!is_resource($this->_conn))
1799 {
1800 JLog::add(JText::_('JLIB_CLIENT_ERROR_JFTP_PASSIVE_CONNECT_PORT'), JLog::WARNING, 'jerror');
1801
1802 return false;
1803 }
1804
1805
1806 @ fwrite($this->_conn, "PASV\r\n");
1807
1808
1809 $endTime = time() + $this->_timeout;
1810 $this->_response = '';
1811
1812 do
1813 {
1814 $this->_response .= fgets($this->_conn, 4096);
1815 }
1816 while (!preg_match('/^([0-9]{3})(-(.*' . CRLF . ')+\1)? [^' . CRLF . ']+' . CRLF . "$/", $this->_response, $parts) && time() < $endTime);
1817
1818
1819 if (!isset($parts[1]))
1820 {
1821 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_PASSIVE_RESPONSE', $this->_response), JLog::WARNING, 'jerror');
1822
1823 return false;
1824 }
1825
1826
1827 $this->_responseCode = $parts[1];
1828 $this->_responseMsg = $parts[0];
1829
1830
1831 if ($this->_responseCode != '227')
1832 {
1833 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_PASSIVE_IP_OBTAIN', $this->_responseMsg), JLog::WARNING, 'jerror');
1834
1835 return false;
1836 }
1837
1838
1839 if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $this->_responseMsg, $match) == 0)
1840 {
1841 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_PASSIVE_IP_VALID', $this->_responseMsg), JLog::WARNING, 'jerror');
1842
1843 return false;
1844 }
1845
1846
1847 $this->_pasv = array('ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], 'port' => $match[5] * 256 + $match[6]);
1848
1849
1850 $this->_dataconn = @fsockopen($this->_pasv['ip'], $this->_pasv['port'], $errno, $err, $this->_timeout);
1851
1852 if (!$this->_dataconn)
1853 {
1854 JLog::add(
1855 JText::sprintf('JLIB_CLIENT_ERROR_JFTP_PASSIVE_CONNECT', $this->_pasv['ip'], $this->_pasv['port'], $errno, $err),
1856 JLog::WARNING,
1857 'jerror'
1858 );
1859
1860 return false;
1861 }
1862
1863
1864 socket_set_timeout($this->_conn, $this->_timeout, 0);
1865
1866 return true;
1867 }
1868
1869 1870 1871 1872 1873 1874 1875 1876 1877
1878 protected function _findMode($fileName)
1879 {
1880 if ($this->_type == FTP_AUTOASCII)
1881 {
1882 $dot = strrpos($fileName, '.') + 1;
1883 $ext = substr($fileName, $dot);
1884
1885 if (in_array($ext, $this->_autoAscii))
1886 {
1887 $mode = FTP_ASCII;
1888 }
1889 else
1890 {
1891 $mode = FTP_BINARY;
1892 }
1893 }
1894 elseif ($this->_type == FTP_ASCII)
1895 {
1896 $mode = FTP_ASCII;
1897 }
1898 else
1899 {
1900 $mode = FTP_BINARY;
1901 }
1902
1903 return $mode;
1904 }
1905
1906 1907 1908 1909 1910 1911 1912 1913 1914 1915
1916 protected function _mode($mode)
1917 {
1918 if ($mode == FTP_BINARY)
1919 {
1920 if (!$this->_putCmd('TYPE I', 200))
1921 {
1922 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_MODE_BINARY', $this->_response), JLog::WARNING, 'jerror');
1923
1924 return false;
1925 }
1926 }
1927 else
1928 {
1929 if (!$this->_putCmd('TYPE A', 200))
1930 {
1931 JLog::add(JText::sprintf('JLIB_CLIENT_ERROR_JFTP_MODE_ASCII', $this->_response), JLog::WARNING, 'jerror');
1932
1933 return false;
1934 }
1935 }
1936
1937 return true;
1938 }
1939 }
1940
1941 1942 1943 1944 1945 1946
1947 class JFTP extends JClientFtp
1948 {
1949 1950 1951 1952 1953 1954 1955
1956 public function __construct(array $options = array())
1957 {
1958 JLog::add('JFTP is deprecated. Use JClientFtp instead.', JLog::WARNING, 'deprecated');
1959 parent::__construct($options);
1960 }
1961 }
1962