ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
SFTP.php
Go to the documentation of this file.
1<?php
2
38namespace phpseclib\Net;
39
41
49class SFTP extends SSH2
50{
60 const CHANNEL = 0x100;
61
73 // this value isn't really used anymore but i'm keeping it reserved for historical reasons
74 const SOURCE_STRING = 2;
79 const SOURCE_CALLBACK = 16;
83 const RESUME = 4;
87 const RESUME_START = 8;
97 var $packet_types = array();
98
106 var $status_codes = array();
107
118 var $request_id = false;
119
130 var $packet_type = -1;
131
140
148 var $extensions = array();
149
158
167 var $pwd = false;
168
176 var $packet_type_log = array();
177
185 var $packet_log = array();
186
195 var $sftp_errors = array();
196
209 var $stat_cache = array();
210
220
229 var $use_stat_cache = true;
230
239 var $sortOptions = array();
240
252 function __construct($host, $port = 22, $timeout = 10)
253 {
254 parent::__construct($host, $port, $timeout);
255
256 $this->max_sftp_packet = 1 << 15;
257
258 $this->packet_types = array(
259 1 => 'NET_SFTP_INIT',
260 2 => 'NET_SFTP_VERSION',
261 /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+:
262 SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
263 pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
264 3 => 'NET_SFTP_OPEN',
265 4 => 'NET_SFTP_CLOSE',
266 5 => 'NET_SFTP_READ',
267 6 => 'NET_SFTP_WRITE',
268 7 => 'NET_SFTP_LSTAT',
269 9 => 'NET_SFTP_SETSTAT',
270 11 => 'NET_SFTP_OPENDIR',
271 12 => 'NET_SFTP_READDIR',
272 13 => 'NET_SFTP_REMOVE',
273 14 => 'NET_SFTP_MKDIR',
274 15 => 'NET_SFTP_RMDIR',
275 16 => 'NET_SFTP_REALPATH',
276 17 => 'NET_SFTP_STAT',
277 /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+:
278 SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
279 pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
280 18 => 'NET_SFTP_RENAME',
281 19 => 'NET_SFTP_READLINK',
282 20 => 'NET_SFTP_SYMLINK',
283
284 101=> 'NET_SFTP_STATUS',
285 102=> 'NET_SFTP_HANDLE',
286 /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+:
287 SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
288 pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
289 103=> 'NET_SFTP_DATA',
290 104=> 'NET_SFTP_NAME',
291 105=> 'NET_SFTP_ATTRS',
292
293 200=> 'NET_SFTP_EXTENDED'
294 );
295 $this->status_codes = array(
296 0 => 'NET_SFTP_STATUS_OK',
297 1 => 'NET_SFTP_STATUS_EOF',
298 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
299 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
300 4 => 'NET_SFTP_STATUS_FAILURE',
301 5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
302 6 => 'NET_SFTP_STATUS_NO_CONNECTION',
303 7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
304 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED',
305 9 => 'NET_SFTP_STATUS_INVALID_HANDLE',
306 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH',
307 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS',
308 12 => 'NET_SFTP_STATUS_WRITE_PROTECT',
309 13 => 'NET_SFTP_STATUS_NO_MEDIA',
310 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM',
311 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED',
312 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL',
313 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT',
314 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY',
315 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY',
316 20 => 'NET_SFTP_STATUS_INVALID_FILENAME',
317 21 => 'NET_SFTP_STATUS_LINK_LOOP',
318 22 => 'NET_SFTP_STATUS_CANNOT_DELETE',
319 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER',
320 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY',
321 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT',
322 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED',
323 27 => 'NET_SFTP_STATUS_DELETE_PENDING',
324 28 => 'NET_SFTP_STATUS_FILE_CORRUPT',
325 29 => 'NET_SFTP_STATUS_OWNER_INVALID',
326 30 => 'NET_SFTP_STATUS_GROUP_INVALID',
327 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK'
328 );
329 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
330 // the order, in this case, matters quite a lot - see \phpseclib\Net\SFTP::_parseAttributes() to understand why
331 $this->attributes = array(
332 0x00000001 => 'NET_SFTP_ATTR_SIZE',
333 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
334 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
335 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
336 // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
337 // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in
338 // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000.
339 // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
340 -1 << 31 => 'NET_SFTP_ATTR_EXTENDED'
341 );
342 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
343 // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name
344 // the array for that $this->open5_flags and similarily alter the constant names.
345 $this->open_flags = array(
346 0x00000001 => 'NET_SFTP_OPEN_READ',
347 0x00000002 => 'NET_SFTP_OPEN_WRITE',
348 0x00000004 => 'NET_SFTP_OPEN_APPEND',
349 0x00000008 => 'NET_SFTP_OPEN_CREATE',
350 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
351 0x00000020 => 'NET_SFTP_OPEN_EXCL'
352 );
353 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
354 // see \phpseclib\Net\SFTP::_parseLongname() for an explanation
355 $this->file_types = array(
356 1 => 'NET_SFTP_TYPE_REGULAR',
357 2 => 'NET_SFTP_TYPE_DIRECTORY',
358 3 => 'NET_SFTP_TYPE_SYMLINK',
359 4 => 'NET_SFTP_TYPE_SPECIAL',
360 5 => 'NET_SFTP_TYPE_UNKNOWN',
361 // the followin types were first defined for use in SFTPv5+
362 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
363 6 => 'NET_SFTP_TYPE_SOCKET',
364 7 => 'NET_SFTP_TYPE_CHAR_DEVICE',
365 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE',
366 9 => 'NET_SFTP_TYPE_FIFO'
367 );
368 $this->_define_array(
369 $this->packet_types,
370 $this->status_codes,
371 $this->attributes,
372 $this->open_flags,
373 $this->file_types
374 );
375
376 if (!defined('NET_SFTP_QUEUE_SIZE')) {
377 define('NET_SFTP_QUEUE_SIZE', 50);
378 }
379 }
380
389 function login($username)
390 {
391 $args = func_get_args();
392 if (!call_user_func_array(array(&$this, '_login'), $args)) {
393 return false;
394 }
395
396 $this->window_size_server_to_client[self::CHANNEL] = $this->window_size;
397
398 $packet = pack(
399 'CNa*N3',
400 NET_SSH2_MSG_CHANNEL_OPEN,
401 strlen('session'),
402 'session',
403 self::CHANNEL,
404 $this->window_size,
405 0x4000
406 );
407
408 if (!$this->_send_binary_packet($packet)) {
409 return false;
410 }
411
412 $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN;
413
414 $response = $this->_get_channel_packet(self::CHANNEL);
415 if ($response === false) {
416 return false;
417 }
418
419 $packet = pack(
420 'CNNa*CNa*',
421 NET_SSH2_MSG_CHANNEL_REQUEST,
422 $this->server_channels[self::CHANNEL],
423 strlen('subsystem'),
424 'subsystem',
425 1,
426 strlen('sftp'),
427 'sftp'
428 );
429 if (!$this->_send_binary_packet($packet)) {
430 return false;
431 }
432
433 $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
434
435 $response = $this->_get_channel_packet(self::CHANNEL);
436 if ($response === false) {
437 // from PuTTY's psftp.exe
438 $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" .
439 "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" .
440 "exec sftp-server";
441 // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does
442 // is redundant
443 $packet = pack(
444 'CNNa*CNa*',
445 NET_SSH2_MSG_CHANNEL_REQUEST,
446 $this->server_channels[self::CHANNEL],
447 strlen('exec'),
448 'exec',
449 1,
450 strlen($command),
451 $command
452 );
453 if (!$this->_send_binary_packet($packet)) {
454 return false;
455 }
456
457 $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
458
459 $response = $this->_get_channel_packet(self::CHANNEL);
460 if ($response === false) {
461 return false;
462 }
463 }
464
465 $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
466
467 if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) {
468 return false;
469 }
470
471 $response = $this->_get_sftp_packet();
472 if ($this->packet_type != NET_SFTP_VERSION) {
473 user_error('Expected SSH_FXP_VERSION');
474 return false;
475 }
476
477 extract(unpack('Nversion', $this->_string_shift($response, 4)));
478 $this->version = $version;
479 while (!empty($response)) {
480 extract(unpack('Nlength', $this->_string_shift($response, 4)));
481 $key = $this->_string_shift($response, $length);
482 extract(unpack('Nlength', $this->_string_shift($response, 4)));
483 $value = $this->_string_shift($response, $length);
484 $this->extensions[$key] = $value;
485 }
486
487 /*
488 SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
489 however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
490 not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
491 one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
492 'newline@vandyke.com' would.
493 */
494 /*
495 if (isset($this->extensions['newline@vandyke.com'])) {
496 $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
497 unset($this->extensions['newline@vandyke.com']);
498 }
499 */
500
501 $this->request_id = 1;
502
503 /*
504 A Note on SFTPv4/5/6 support:
505 <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.1> states the following:
506
507 "If the client wishes to interoperate with servers that support noncontiguous version
508 numbers it SHOULD send '3'"
509
510 Given that the server only sends its version number after the client has already done so, the above
511 seems to be suggesting that v3 should be the default version. This makes sense given that v3 is the
512 most popular.
513
514 <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.5> states the following;
515
516 "If the server did not send the "versions" extension, or the version-from-list was not included, the
517 server MAY send a status response describing the failure, but MUST then close the channel without
518 processing any further requests."
519
520 So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and
521 a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4? If it only implements
522 v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed
523 in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what \phpseclib\Net\SFTP would do is close the
524 channel and reopen it with a new and updated SSH_FXP_INIT packet.
525 */
526 switch ($this->version) {
527 case 2:
528 case 3:
529 break;
530 default:
531 return false;
532 }
533
534 $this->pwd = $this->_realpath('.');
535
536 $this->_update_stat_cache($this->pwd, array());
537
538 return true;
539 }
540
547 {
548 $this->use_stat_cache = false;
549 }
550
557 {
558 $this->use_stat_cache = true;
559 }
560
566 function clearStatCache()
567 {
568 $this->stat_cache = array();
569 }
570
577 function pwd()
578 {
579 return $this->pwd;
580 }
581
589 function _logError($response, $status = -1)
590 {
591 if ($status == -1) {
592 extract(unpack('Nstatus', $this->_string_shift($response, 4)));
593 }
594
595 $error = $this->status_codes[$status];
596
597 if ($this->version > 2) {
598 extract(unpack('Nlength', $this->_string_shift($response, 4)));
599 $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length);
600 } else {
601 $this->sftp_errors[] = $error;
602 }
603 }
604
616 function _realpath($path)
617 {
618 if ($this->pwd === false) {
619 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
620 if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) {
621 return false;
622 }
623
624 $response = $this->_get_sftp_packet();
625 switch ($this->packet_type) {
626 case NET_SFTP_NAME:
627 // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following
628 // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks
629 // at is the first part and that part is defined the same in SFTP versions 3 through 6.
630 $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway
631 extract(unpack('Nlength', $this->_string_shift($response, 4)));
632 return $this->_string_shift($response, $length);
633 case NET_SFTP_STATUS:
634 $this->_logError($response);
635 return false;
636 default:
637 user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
638 return false;
639 }
640 }
641
642 if ($path[0] != '/') {
643 $path = $this->pwd . '/' . $path;
644 }
645
646 $path = explode('/', $path);
647 $new = array();
648 foreach ($path as $dir) {
649 if (!strlen($dir)) {
650 continue;
651 }
652 switch ($dir) {
653 case '..':
654 array_pop($new);
655 case '.':
656 break;
657 default:
658 $new[] = $dir;
659 }
660 }
661
662 return '/' . implode('/', $new);
663 }
664
672 function chdir($dir)
673 {
674 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
675 return false;
676 }
677
678 // assume current dir if $dir is empty
679 if ($dir === '') {
680 $dir = './';
681 // suffix a slash if needed
682 } elseif ($dir[strlen($dir) - 1] != '/') {
683 $dir.= '/';
684 }
685
686 $dir = $this->_realpath($dir);
687
688 // confirm that $dir is, in fact, a valid directory
689 if ($this->use_stat_cache && is_array($this->_query_stat_cache($dir))) {
690 $this->pwd = $dir;
691 return true;
692 }
693
694 // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us
695 // the currently logged in user has the appropriate permissions or not. maybe you could see if
696 // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy
697 // way to get those with SFTP
698
699 if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
700 return false;
701 }
702
703 // see \phpseclib\Net\SFTP::nlist() for a more thorough explanation of the following
704 $response = $this->_get_sftp_packet();
705 switch ($this->packet_type) {
706 case NET_SFTP_HANDLE:
707 $handle = substr($response, 4);
708 break;
709 case NET_SFTP_STATUS:
710 $this->_logError($response);
711 return false;
712 default:
713 user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
714 return false;
715 }
716
717 if (!$this->_close_handle($handle)) {
718 return false;
719 }
720
721 $this->_update_stat_cache($dir, array());
722
723 $this->pwd = $dir;
724 return true;
725 }
726
735 function nlist($dir = '.', $recursive = false)
736 {
737 return $this->_nlist_helper($dir, $recursive, '');
738 }
739
749 function _nlist_helper($dir, $recursive, $relativeDir)
750 {
751 $files = $this->_list($dir, false);
752
753 if (!$recursive) {
754 return $files;
755 }
756
757 $result = array();
758 foreach ($files as $value) {
759 if ($value == '.' || $value == '..') {
760 if ($relativeDir == '') {
761 $result[] = $value;
762 }
763 continue;
764 }
765 if (is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $value)))) {
766 $temp = $this->_nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/');
767 $result = array_merge($result, $temp);
768 } else {
769 $result[] = $relativeDir . $value;
770 }
771 }
772
773 return $result;
774 }
775
784 function rawlist($dir = '.', $recursive = false)
785 {
786 $files = $this->_list($dir, true);
787 if (!$recursive || $files === false) {
788 return $files;
789 }
790
791 static $depth = 0;
792
793 foreach ($files as $key => $value) {
794 if ($depth != 0 && $key == '..') {
795 unset($files[$key]);
796 continue;
797 }
798 if ($key != '.' && $key != '..' && is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)))) {
799 $depth++;
800 $files[$key] = $this->rawlist($dir . '/' . $key, true);
801 $depth--;
802 } else {
803 $files[$key] = (object) $value;
804 }
805 }
806
807 return $files;
808 }
809
818 function _list($dir, $raw = true)
819 {
820 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
821 return false;
822 }
823
824 $dir = $this->_realpath($dir . '/');
825 if ($dir === false) {
826 return false;
827 }
828
829 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
830 if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
831 return false;
832 }
833
834 $response = $this->_get_sftp_packet();
835 switch ($this->packet_type) {
836 case NET_SFTP_HANDLE:
837 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
838 // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
839 // represent the length of the string and leave it at that
840 $handle = substr($response, 4);
841 break;
842 case NET_SFTP_STATUS:
843 // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
844 $this->_logError($response);
845 return false;
846 default:
847 user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
848 return false;
849 }
850
851 $this->_update_stat_cache($dir, array());
852
853 $contents = array();
854 while (true) {
855 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
856 // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many
857 // SSH_MSG_CHANNEL_DATA messages is not known to me.
858 if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) {
859 return false;
860 }
861
862 $response = $this->_get_sftp_packet();
863 switch ($this->packet_type) {
864 case NET_SFTP_NAME:
865 extract(unpack('Ncount', $this->_string_shift($response, 4)));
866 for ($i = 0; $i < $count; $i++) {
867 extract(unpack('Nlength', $this->_string_shift($response, 4)));
868 $shortname = $this->_string_shift($response, $length);
869 extract(unpack('Nlength', $this->_string_shift($response, 4)));
870 $longname = $this->_string_shift($response, $length);
872 if (!isset($attributes['type'])) {
873 $fileType = $this->_parseLongname($longname);
874 if ($fileType) {
875 $attributes['type'] = $fileType;
876 }
877 }
878 $contents[$shortname] = $attributes + array('filename' => $shortname);
879
880 if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) {
881 $this->_update_stat_cache($dir . '/' . $shortname, array());
882 } else {
883 if ($shortname == '..') {
884 $temp = $this->_realpath($dir . '/..') . '/.';
885 } else {
886 $temp = $dir . '/' . $shortname;
887 }
888 $this->_update_stat_cache($temp, (object) array('lstat' => $attributes));
889 }
890 // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
891 // final SSH_FXP_STATUS packet should tell us that, already.
892 }
893 break;
894 case NET_SFTP_STATUS:
895 extract(unpack('Nstatus', $this->_string_shift($response, 4)));
896 if ($status != NET_SFTP_STATUS_EOF) {
897 $this->_logError($response, $status);
898 return false;
899 }
900 break 2;
901 default:
902 user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
903 return false;
904 }
905 }
906
907 if (!$this->_close_handle($handle)) {
908 return false;
909 }
910
911 if (count($this->sortOptions)) {
912 uasort($contents, array(&$this, '_comparator'));
913 }
914
915 return $raw ? $contents : array_keys($contents);
916 }
917
928 function _comparator($a, $b)
929 {
930 switch (true) {
931 case $a['filename'] === '.' || $b['filename'] === '.':
932 if ($a['filename'] === $b['filename']) {
933 return 0;
934 }
935 return $a['filename'] === '.' ? -1 : 1;
936 case $a['filename'] === '..' || $b['filename'] === '..':
937 if ($a['filename'] === $b['filename']) {
938 return 0;
939 }
940 return $a['filename'] === '..' ? -1 : 1;
941 case isset($a['type']) && $a['type'] === NET_SFTP_TYPE_DIRECTORY:
942 if (!isset($b['type'])) {
943 return 1;
944 }
945 if ($b['type'] !== $a['type']) {
946 return -1;
947 }
948 break;
949 case isset($b['type']) && $b['type'] === NET_SFTP_TYPE_DIRECTORY:
950 return 1;
951 }
952 foreach ($this->sortOptions as $sort => $order) {
953 if (!isset($a[$sort]) || !isset($b[$sort])) {
954 if (isset($a[$sort])) {
955 return -1;
956 }
957 if (isset($b[$sort])) {
958 return 1;
959 }
960 return 0;
961 }
962 switch ($sort) {
963 case 'filename':
964 $result = strcasecmp($a['filename'], $b['filename']);
965 if ($result) {
966 return $order === SORT_DESC ? -$result : $result;
967 }
968 break;
969 case 'permissions':
970 case 'mode':
971 $a[$sort]&= 07777;
972 $b[$sort]&= 07777;
973 default:
974 if ($a[$sort] === $b[$sort]) {
975 break;
976 }
977 return $order === SORT_ASC ? $a[$sort] - $b[$sort] : $b[$sort] - $a[$sort];
978 }
979 }
980 }
981
1002 function setListOrder()
1003 {
1004 $this->sortOptions = array();
1005 $args = func_get_args();
1006 if (empty($args)) {
1007 return;
1008 }
1009 $len = count($args) & 0x7FFFFFFE;
1010 for ($i = 0; $i < $len; $i+=2) {
1011 $this->sortOptions[$args[$i]] = $args[$i + 1];
1012 }
1013 if (!count($this->sortOptions)) {
1014 $this->sortOptions = array('bogus' => true);
1015 }
1016 }
1017
1027 function size($filename)
1028 {
1029 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1030 return false;
1031 }
1032
1033 $result = $this->stat($filename);
1034 if ($result === false) {
1035 return false;
1036 }
1037 return isset($result['size']) ? $result['size'] : -1;
1038 }
1039
1047 function _update_stat_cache($path, $value)
1048 {
1049 if ($this->use_stat_cache === false) {
1050 return;
1051 }
1052
1053 // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($path, '/'))
1054 $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));
1055
1056 $temp = &$this->stat_cache;
1057 $max = count($dirs) - 1;
1058 foreach ($dirs as $i => $dir) {
1059 // if $temp is an object that means one of two things.
1060 // 1. a file was deleted and changed to a directory behind phpseclib's back
1061 // 2. it's a symlink. when lstat is done it's unclear what it's a symlink to
1062 if (is_object($temp)) {
1063 $temp = array();
1064 }
1065 if (!isset($temp[$dir])) {
1066 $temp[$dir] = array();
1067 }
1068 if ($i === $max) {
1069 if (is_object($temp[$dir])) {
1070 if (!isset($value->stat) && isset($temp[$dir]->stat)) {
1071 $value->stat = $temp[$dir]->stat;
1072 }
1073 if (!isset($value->lstat) && isset($temp[$dir]->lstat)) {
1074 $value->lstat = $temp[$dir]->lstat;
1075 }
1076 }
1077 $temp[$dir] = $value;
1078 break;
1079 }
1080 $temp = &$temp[$dir];
1081 }
1082 }
1083
1092 {
1093 $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));
1094
1095 $temp = &$this->stat_cache;
1096 $max = count($dirs) - 1;
1097 foreach ($dirs as $i => $dir) {
1098 if ($i === $max) {
1099 unset($temp[$dir]);
1100 return true;
1101 }
1102 if (!isset($temp[$dir])) {
1103 return false;
1104 }
1105 $temp = &$temp[$dir];
1106 }
1107 }
1108
1119 {
1120 $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));
1121
1122 $temp = &$this->stat_cache;
1123 foreach ($dirs as $dir) {
1124 if (!isset($temp[$dir])) {
1125 return null;
1126 }
1127 $temp = &$temp[$dir];
1128 }
1129 return $temp;
1130 }
1131
1141 function stat($filename)
1142 {
1143 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1144 return false;
1145 }
1146
1147 $filename = $this->_realpath($filename);
1148 if ($filename === false) {
1149 return false;
1150 }
1151
1152 if ($this->use_stat_cache) {
1154 if (is_array($result) && isset($result['.']) && isset($result['.']->stat)) {
1155 return $result['.']->stat;
1156 }
1157 if (is_object($result) && isset($result->stat)) {
1158 return $result->stat;
1159 }
1160 }
1161
1162 $stat = $this->_stat($filename, NET_SFTP_STAT);
1163 if ($stat === false) {
1165 return false;
1166 }
1167 if (isset($stat['type'])) {
1168 if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
1169 $filename.= '/.';
1170 }
1171 $this->_update_stat_cache($filename, (object) array('stat' => $stat));
1172 return $stat;
1173 }
1174
1175 $pwd = $this->pwd;
1176 $stat['type'] = $this->chdir($filename) ?
1177 NET_SFTP_TYPE_DIRECTORY :
1178 NET_SFTP_TYPE_REGULAR;
1179 $this->pwd = $pwd;
1180
1181 if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
1182 $filename.= '/.';
1183 }
1184 $this->_update_stat_cache($filename, (object) array('stat' => $stat));
1185
1186 return $stat;
1187 }
1188
1199 {
1200 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1201 return false;
1202 }
1203
1204 $filename = $this->_realpath($filename);
1205 if ($filename === false) {
1206 return false;
1207 }
1208
1209 if ($this->use_stat_cache) {
1211 if (is_array($result) && isset($result['.']) && isset($result['.']->lstat)) {
1212 return $result['.']->lstat;
1213 }
1214 if (is_object($result) && isset($result->lstat)) {
1215 return $result->lstat;
1216 }
1217 }
1218
1219 $lstat = $this->_stat($filename, NET_SFTP_LSTAT);
1220 if ($lstat === false) {
1222 return false;
1223 }
1224 if (isset($lstat['type'])) {
1225 if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
1226 $filename.= '/.';
1227 }
1228 $this->_update_stat_cache($filename, (object) array('lstat' => $lstat));
1229 return $lstat;
1230 }
1231
1232 $stat = $this->_stat($filename, NET_SFTP_STAT);
1233
1234 if ($lstat != $stat) {
1235 $lstat = array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK));
1236 $this->_update_stat_cache($filename, (object) array('lstat' => $lstat));
1237 return $stat;
1238 }
1239
1240 $pwd = $this->pwd;
1241 $lstat['type'] = $this->chdir($filename) ?
1242 NET_SFTP_TYPE_DIRECTORY :
1243 NET_SFTP_TYPE_REGULAR;
1244 $this->pwd = $pwd;
1245
1246 if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
1247 $filename.= '/.';
1248 }
1249 $this->_update_stat_cache($filename, (object) array('lstat' => $lstat));
1250
1251 return $lstat;
1252 }
1253
1266 {
1267 // SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
1268 $packet = pack('Na*', strlen($filename), $filename);
1269 if (!$this->_send_sftp_packet($type, $packet)) {
1270 return false;
1271 }
1272
1273 $response = $this->_get_sftp_packet();
1274 switch ($this->packet_type) {
1275 case NET_SFTP_ATTRS:
1276 return $this->_parseAttributes($response);
1277 case NET_SFTP_STATUS:
1278 $this->_logError($response);
1279 return false;
1280 }
1281
1282 user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
1283 return false;
1284 }
1285
1294 function truncate($filename, $new_size)
1295 {
1296 $attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size / 4294967296, $new_size); // 4294967296 == 0x100000000 == 1<<32
1297
1298 return $this->_setstat($filename, $attr, false);
1299 }
1300
1312 function touch($filename, $time = null, $atime = null)
1313 {
1314 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1315 return false;
1316 }
1317
1318 $filename = $this->_realpath($filename);
1319 if ($filename === false) {
1320 return false;
1321 }
1322
1323 if (!isset($time)) {
1324 $time = time();
1325 }
1326 if (!isset($atime)) {
1327 $atime = $time;
1328 }
1329
1330 $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL;
1331 $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time, $atime);
1332 $packet = pack('Na*Na*', strlen($filename), $filename, $flags, $attr);
1333 if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
1334 return false;
1335 }
1336
1337 $response = $this->_get_sftp_packet();
1338 switch ($this->packet_type) {
1339 case NET_SFTP_HANDLE:
1340 return $this->_close_handle(substr($response, 4));
1341 case NET_SFTP_STATUS:
1342 $this->_logError($response);
1343 break;
1344 default:
1345 user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
1346 return false;
1347 }
1348
1349 return $this->_setstat($filename, $attr, false);
1350 }
1351
1363 function chown($filename, $uid, $recursive = false)
1364 {
1365 // quoting from <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
1366 // "if the owner or group is specified as -1, then that ID is not changed"
1367 $attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1);
1368
1369 return $this->_setstat($filename, $attr, $recursive);
1370 }
1371
1383 function chgrp($filename, $gid, $recursive = false)
1384 {
1385 $attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid);
1386
1387 return $this->_setstat($filename, $attr, $recursive);
1388 }
1389
1402 function chmod($mode, $filename, $recursive = false)
1403 {
1404 if (is_string($mode) && is_int($filename)) {
1405 $temp = $mode;
1406 $mode = $filename;
1407 $filename = $temp;
1408 }
1409
1410 $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
1411 if (!$this->_setstat($filename, $attr, $recursive)) {
1412 return false;
1413 }
1414 if ($recursive) {
1415 return true;
1416 }
1417
1418 $filename = $this->_realPath($filename);
1419 // rather than return what the permissions *should* be, we'll return what they actually are. this will also
1420 // tell us if the file actually exists.
1421 // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
1422 $packet = pack('Na*', strlen($filename), $filename);
1423 if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) {
1424 return false;
1425 }
1426
1427 $response = $this->_get_sftp_packet();
1428 switch ($this->packet_type) {
1429 case NET_SFTP_ATTRS:
1430 $attrs = $this->_parseAttributes($response);
1431 return $attrs['permissions'];
1432 case NET_SFTP_STATUS:
1433 $this->_logError($response);
1434 return false;
1435 }
1436
1437 user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
1438 return false;
1439 }
1440
1450 function _setstat($filename, $attr, $recursive)
1451 {
1452 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1453 return false;
1454 }
1455
1456 $filename = $this->_realpath($filename);
1457 if ($filename === false) {
1458 return false;
1459 }
1460
1462
1463 if ($recursive) {
1464 $i = 0;
1465 $result = $this->_setstat_recursive($filename, $attr, $i);
1466 $this->_read_put_responses($i);
1467 return $result;
1468 }
1469
1470 // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to
1471 // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
1472 if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) {
1473 return false;
1474 }
1475
1476 /*
1477 "Because some systems must use separate system calls to set various attributes, it is possible that a failure
1478 response will be returned, but yet some of the attributes may be have been successfully modified. If possible,
1479 servers SHOULD avoid this situation; however, clients MUST be aware that this is possible."
1480
1481 -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
1482 */
1483 $response = $this->_get_sftp_packet();
1484 if ($this->packet_type != NET_SFTP_STATUS) {
1485 user_error('Expected SSH_FXP_STATUS');
1486 return false;
1487 }
1488
1489 extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1490 if ($status != NET_SFTP_STATUS_OK) {
1491 $this->_logError($response, $status);
1492 return false;
1493 }
1494
1495 return true;
1496 }
1497
1509 function _setstat_recursive($path, $attr, &$i)
1510 {
1511 if (!$this->_read_put_responses($i)) {
1512 return false;
1513 }
1514 $i = 0;
1515 $entries = $this->_list($path, true);
1516
1517 if ($entries === false) {
1518 return $this->_setstat($path, $attr, false);
1519 }
1520
1521 // normally $entries would have at least . and .. but it might not if the directories
1522 // permissions didn't allow reading
1523 if (empty($entries)) {
1524 return false;
1525 }
1526
1527 unset($entries['.'], $entries['..']);
1528 foreach ($entries as $filename => $props) {
1529 if (!isset($props['type'])) {
1530 return false;
1531 }
1532
1533 $temp = $path . '/' . $filename;
1534 if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
1535 if (!$this->_setstat_recursive($temp, $attr, $i)) {
1536 return false;
1537 }
1538 } else {
1539 if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) {
1540 return false;
1541 }
1542
1543 $i++;
1544
1545 if ($i >= NET_SFTP_QUEUE_SIZE) {
1546 if (!$this->_read_put_responses($i)) {
1547 return false;
1548 }
1549 $i = 0;
1550 }
1551 }
1552 }
1553
1554 if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) {
1555 return false;
1556 }
1557
1558 $i++;
1559
1560 if ($i >= NET_SFTP_QUEUE_SIZE) {
1561 if (!$this->_read_put_responses($i)) {
1562 return false;
1563 }
1564 $i = 0;
1565 }
1566
1567 return true;
1568 }
1569
1577 function readlink($link)
1578 {
1579 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1580 return false;
1581 }
1582
1583 $link = $this->_realpath($link);
1584
1585 if (!$this->_send_sftp_packet(NET_SFTP_READLINK, pack('Na*', strlen($link), $link))) {
1586 return false;
1587 }
1588
1589 $response = $this->_get_sftp_packet();
1590 switch ($this->packet_type) {
1591 case NET_SFTP_NAME:
1592 break;
1593 case NET_SFTP_STATUS:
1594 $this->_logError($response);
1595 return false;
1596 default:
1597 user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
1598 return false;
1599 }
1600
1601 extract(unpack('Ncount', $this->_string_shift($response, 4)));
1602 // the file isn't a symlink
1603 if (!$count) {
1604 return false;
1605 }
1606
1607 extract(unpack('Nlength', $this->_string_shift($response, 4)));
1608 return $this->_string_shift($response, $length);
1609 }
1610
1621 function symlink($target, $link)
1622 {
1623 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1624 return false;
1625 }
1626
1627 $target = $this->_realpath($target);
1628 $link = $this->_realpath($link);
1629
1630 $packet = pack('Na*Na*', strlen($target), $target, strlen($link), $link);
1631 if (!$this->_send_sftp_packet(NET_SFTP_SYMLINK, $packet)) {
1632 return false;
1633 }
1634
1635 $response = $this->_get_sftp_packet();
1636 if ($this->packet_type != NET_SFTP_STATUS) {
1637 user_error('Expected SSH_FXP_STATUS');
1638 return false;
1639 }
1640
1641 extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1642 if ($status != NET_SFTP_STATUS_OK) {
1643 $this->_logError($response, $status);
1644 return false;
1645 }
1646
1647 return true;
1648 }
1649
1657 function mkdir($dir, $mode = -1, $recursive = false)
1658 {
1659 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1660 return false;
1661 }
1662
1663 $dir = $this->_realpath($dir);
1664 // by not providing any permissions, hopefully the server will use the logged in users umask - their
1665 // default permissions.
1666 $attr = $mode == -1 ? "\0\0\0\0" : pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
1667
1668 if ($recursive) {
1669 $dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir));
1670 if (empty($dirs[0])) {
1671 array_shift($dirs);
1672 $dirs[0] = '/' . $dirs[0];
1673 }
1674 for ($i = 0; $i < count($dirs); $i++) {
1675 $temp = array_slice($dirs, 0, $i + 1);
1676 $temp = implode('/', $temp);
1677 $result = $this->_mkdir_helper($temp, $attr);
1678 }
1679 return $result;
1680 }
1681
1682 return $this->_mkdir_helper($dir, $attr);
1683 }
1684
1692 function _mkdir_helper($dir, $attr)
1693 {
1694 if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, $attr))) {
1695 return false;
1696 }
1697
1698 $response = $this->_get_sftp_packet();
1699 if ($this->packet_type != NET_SFTP_STATUS) {
1700 user_error('Expected SSH_FXP_STATUS');
1701 return false;
1702 }
1703
1704 extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1705 if ($status != NET_SFTP_STATUS_OK) {
1706 $this->_logError($response, $status);
1707 return false;
1708 }
1709
1710 return true;
1711 }
1712
1720 function rmdir($dir)
1721 {
1722 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1723 return false;
1724 }
1725
1726 $dir = $this->_realpath($dir);
1727 if ($dir === false) {
1728 return false;
1729 }
1730
1731 if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) {
1732 return false;
1733 }
1734
1735 $response = $this->_get_sftp_packet();
1736 if ($this->packet_type != NET_SFTP_STATUS) {
1737 user_error('Expected SSH_FXP_STATUS');
1738 return false;
1739 }
1740
1741 extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1742 if ($status != NET_SFTP_STATUS_OK) {
1743 // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
1744 $this->_logError($response, $status);
1745 return false;
1746 }
1747
1748 $this->_remove_from_stat_cache($dir);
1749 // the following will do a soft delete, which would be useful if you deleted a file
1750 // and then tried to do a stat on the deleted file. the above, in contrast, does
1751 // a hard delete
1752 //$this->_update_stat_cache($dir, false);
1753
1754 return true;
1755 }
1756
1802 function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null)
1803 {
1804 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1805 return false;
1806 }
1807
1808 $remote_file = $this->_realpath($remote_file);
1809 if ($remote_file === false) {
1810 return false;
1811 }
1812
1813 $this->_remove_from_stat_cache($remote_file);
1814
1815 $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
1816 // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
1817 // in practice, it doesn't seem to do that.
1818 //$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
1819
1820 if ($start >= 0) {
1821 $offset = $start;
1822 } elseif ($mode & self::RESUME) {
1823 // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called
1824 $size = $this->size($remote_file);
1825 $offset = $size !== false ? $size : 0;
1826 } else {
1827 $offset = 0;
1828 $flags|= NET_SFTP_OPEN_TRUNCATE;
1829 }
1830
1831 $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0);
1832 if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
1833 return false;
1834 }
1835
1836 $response = $this->_get_sftp_packet();
1837 switch ($this->packet_type) {
1838 case NET_SFTP_HANDLE:
1839 $handle = substr($response, 4);
1840 break;
1841 case NET_SFTP_STATUS:
1842 $this->_logError($response);
1843 return false;
1844 default:
1845 user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
1846 return false;
1847 }
1848
1849 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
1850 $dataCallback = false;
1851 switch (true) {
1852 case $mode & self::SOURCE_CALLBACK:
1853 if (!is_callable($data)) {
1854 user_error("\$data should be is_callable() if you specify SOURCE_CALLBACK flag");
1855 }
1856 $dataCallback = $data;
1857 // do nothing
1858 break;
1859 case is_resource($data):
1860 $mode = $mode & ~self::SOURCE_LOCAL_FILE;
1861 $fp = $data;
1862 break;
1863 case $mode & self::SOURCE_LOCAL_FILE:
1864 if (!is_file($data)) {
1865 user_error("$data is not a valid file");
1866 return false;
1867 }
1868 $fp = @fopen($data, 'rb');
1869 if (!$fp) {
1870 return false;
1871 }
1872 }
1873
1874 if (isset($fp)) {
1875 $stat = fstat($fp);
1876 $size = $stat['size'];
1877
1878 if ($local_start >= 0) {
1879 fseek($fp, $local_start);
1880 $size-= $local_start;
1881 }
1882 } elseif ($dataCallback) {
1883 $size = 0;
1884 } else {
1885 $size = strlen($data);
1886 }
1887
1888 $sent = 0;
1889 $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
1890
1891 $sftp_packet_size = 4096; // PuTTY uses 4096
1892 // make the SFTP packet be exactly 4096 bytes by including the bytes in the NET_SFTP_WRITE packets "header"
1893 $sftp_packet_size-= strlen($handle) + 25;
1894 $i = 0;
1895 while ($dataCallback || $sent < $size) {
1896 if ($dataCallback) {
1897 $temp = call_user_func($dataCallback, $sftp_packet_size);
1898 if (is_null($temp)) {
1899 break;
1900 }
1901 } else {
1902 $temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size);
1903 }
1904 $subtemp = $offset + $sent;
1905 $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp);
1906 if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) {
1907 if ($mode & self::SOURCE_LOCAL_FILE) {
1908 fclose($fp);
1909 }
1910 return false;
1911 }
1912 $sent+= strlen($temp);
1913 if (is_callable($progressCallback)) {
1914 call_user_func($progressCallback, $sent);
1915 }
1916
1917 $i++;
1918
1919 if ($i == NET_SFTP_QUEUE_SIZE) {
1920 if (!$this->_read_put_responses($i)) {
1921 $i = 0;
1922 break;
1923 }
1924 $i = 0;
1925 }
1926 }
1927
1928 if (!$this->_read_put_responses($i)) {
1929 if ($mode & self::SOURCE_LOCAL_FILE) {
1930 fclose($fp);
1931 }
1932 $this->_close_handle($handle);
1933 return false;
1934 }
1935
1936 if ($mode & self::SOURCE_LOCAL_FILE) {
1937 fclose($fp);
1938 }
1939
1940 return $this->_close_handle($handle);
1941 }
1942
1954 {
1955 while ($i--) {
1956 $response = $this->_get_sftp_packet();
1957 if ($this->packet_type != NET_SFTP_STATUS) {
1958 user_error('Expected SSH_FXP_STATUS');
1959 return false;
1960 }
1961
1962 extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1963 if ($status != NET_SFTP_STATUS_OK) {
1964 $this->_logError($response, $status);
1965 break;
1966 }
1967 }
1968
1969 return $i < 0;
1970 }
1971
1979 function _close_handle($handle)
1980 {
1981 if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
1982 return false;
1983 }
1984
1985 // "The client MUST release all resources associated with the handle regardless of the status."
1986 // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
1987 $response = $this->_get_sftp_packet();
1988 if ($this->packet_type != NET_SFTP_STATUS) {
1989 user_error('Expected SSH_FXP_STATUS');
1990 return false;
1991 }
1992
1993 extract(unpack('Nstatus', $this->_string_shift($response, 4)));
1994 if ($status != NET_SFTP_STATUS_OK) {
1995 $this->_logError($response, $status);
1996 return false;
1997 }
1998
1999 return true;
2000 }
2001
2018 function get($remote_file, $local_file = false, $offset = 0, $length = -1)
2019 {
2020 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
2021 return false;
2022 }
2023
2024 $remote_file = $this->_realpath($remote_file);
2025 if ($remote_file === false) {
2026 return false;
2027 }
2028
2029 $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0);
2030 if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
2031 return false;
2032 }
2033
2034 $response = $this->_get_sftp_packet();
2035 switch ($this->packet_type) {
2036 case NET_SFTP_HANDLE:
2037 $handle = substr($response, 4);
2038 break;
2039 case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2040 $this->_logError($response);
2041 return false;
2042 default:
2043 user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
2044 return false;
2045 }
2046
2047 if (is_resource($local_file)) {
2048 $fp = $local_file;
2049 $stat = fstat($fp);
2050 $res_offset = $stat['size'];
2051 } else {
2052 $res_offset = 0;
2053 if ($local_file !== false) {
2054 $fp = fopen($local_file, 'wb');
2055 if (!$fp) {
2056 return false;
2057 }
2058 } else {
2059 $content = '';
2060 }
2061 }
2062
2063 $fclose_check = $local_file !== false && !is_resource($local_file);
2064
2065 $start = $offset;
2066 $size = $this->max_sftp_packet < $length || $length < 0 ? $this->max_sftp_packet : $length;
2067 while (true) {
2068 $packet = pack('Na*N3', strlen($handle), $handle, $offset / 4294967296, $offset, $size);
2069 if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) {
2070 if ($fclose_check) {
2071 fclose($fp);
2072 }
2073 return false;
2074 }
2075
2076 $response = $this->_get_sftp_packet();
2077 switch ($this->packet_type) {
2078 case NET_SFTP_DATA:
2079 $temp = substr($response, 4);
2080 $offset+= strlen($temp);
2081 if ($local_file === false) {
2082 $content.= $temp;
2083 } else {
2084 fputs($fp, $temp);
2085 }
2086 break;
2087 case NET_SFTP_STATUS:
2088 // could, in theory, return false if !strlen($content) but we'll hold off for the time being
2089 $this->_logError($response);
2090 break 2;
2091 default:
2092 user_error('Expected SSH_FXP_DATA or SSH_FXP_STATUS');
2093 if ($fclose_check) {
2094 fclose($fp);
2095 }
2096 return false;
2097 }
2098
2099 if ($length > 0 && $length <= $offset - $start) {
2100 break;
2101 }
2102 }
2103
2104 if ($length > 0 && $length <= $offset - $start) {
2105 if ($local_file === false) {
2106 $content = substr($content, 0, $length);
2107 } else {
2108 ftruncate($fp, $length + $res_offset);
2109 }
2110 }
2111
2112 if ($fclose_check) {
2113 fclose($fp);
2114 }
2115
2116 if (!$this->_close_handle($handle)) {
2117 return false;
2118 }
2119
2120 // if $content isn't set that means a file was written to
2121 return isset($content) ? $content : true;
2122 }
2123
2132 function delete($path, $recursive = true)
2133 {
2134 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
2135 return false;
2136 }
2137
2138 $path = $this->_realpath($path);
2139 if ($path === false) {
2140 return false;
2141 }
2142
2143 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
2144 if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) {
2145 return false;
2146 }
2147
2148 $response = $this->_get_sftp_packet();
2149 if ($this->packet_type != NET_SFTP_STATUS) {
2150 user_error('Expected SSH_FXP_STATUS');
2151 return false;
2152 }
2153
2154 // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2155 extract(unpack('Nstatus', $this->_string_shift($response, 4)));
2156 if ($status != NET_SFTP_STATUS_OK) {
2157 $this->_logError($response, $status);
2158 if (!$recursive) {
2159 return false;
2160 }
2161 $i = 0;
2162 $result = $this->_delete_recursive($path, $i);
2163 $this->_read_put_responses($i);
2164 return $result;
2165 }
2166
2168
2169 return true;
2170 }
2171
2183 {
2184 if (!$this->_read_put_responses($i)) {
2185 return false;
2186 }
2187 $i = 0;
2188 $entries = $this->_list($path, true);
2189
2190 // normally $entries would have at least . and .. but it might not if the directories
2191 // permissions didn't allow reading
2192 if (empty($entries)) {
2193 return false;
2194 }
2195
2196 unset($entries['.'], $entries['..']);
2197 foreach ($entries as $filename => $props) {
2198 if (!isset($props['type'])) {
2199 return false;
2200 }
2201
2202 $temp = $path . '/' . $filename;
2203 if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
2204 if (!$this->_delete_recursive($temp, $i)) {
2205 return false;
2206 }
2207 } else {
2208 if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($temp), $temp))) {
2209 return false;
2210 }
2211 $this->_remove_from_stat_cache($temp);
2212
2213 $i++;
2214
2215 if ($i >= NET_SFTP_QUEUE_SIZE) {
2216 if (!$this->_read_put_responses($i)) {
2217 return false;
2218 }
2219 $i = 0;
2220 }
2221 }
2222 }
2223
2224 if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($path), $path))) {
2225 return false;
2226 }
2228
2229 $i++;
2230
2231 if ($i >= NET_SFTP_QUEUE_SIZE) {
2232 if (!$this->_read_put_responses($i)) {
2233 return false;
2234 }
2235 $i = 0;
2236 }
2237
2238 return true;
2239 }
2240
2249 {
2250 if ($this->use_stat_cache) {
2251 $path = $this->_realpath($path);
2252
2254
2255 if (isset($result)) {
2256 // return true if $result is an array or if it's an stdClass object
2257 return $result !== false;
2258 }
2259 }
2260
2261 return $this->stat($path) !== false;
2262 }
2263
2271 function is_dir($path)
2272 {
2273 $result = $this->_get_stat_cache_prop($path, 'type');
2274 if ($result === false) {
2275 return false;
2276 }
2277 return $result === NET_SFTP_TYPE_DIRECTORY;
2278 }
2279
2287 function is_file($path)
2288 {
2289 $result = $this->_get_stat_cache_prop($path, 'type');
2290 if ($result === false) {
2291 return false;
2292 }
2293 return $result === NET_SFTP_TYPE_REGULAR;
2294 }
2295
2303 function is_link($path)
2304 {
2305 $result = $this->_get_lstat_cache_prop($path, 'type');
2306 if ($result === false) {
2307 return false;
2308 }
2309 return $result === NET_SFTP_TYPE_SYMLINK;
2310 }
2311
2320 {
2321 return $this->_get_stat_cache_prop($path, 'atime');
2322 }
2323
2332 {
2333 return $this->_get_stat_cache_prop($path, 'mtime');
2334 }
2335
2344 {
2345 return $this->_get_stat_cache_prop($path, 'permissions');
2346 }
2347
2356 {
2357 return $this->_get_stat_cache_prop($path, 'uid');
2358 }
2359
2368 {
2369 return $this->_get_stat_cache_prop($path, 'gid');
2370 }
2371
2379 function filesize($path)
2380 {
2381 return $this->_get_stat_cache_prop($path, 'size');
2382 }
2383
2391 function filetype($path)
2392 {
2393 $type = $this->_get_stat_cache_prop($path, 'type');
2394 if ($type === false) {
2395 return false;
2396 }
2397
2398 switch ($type) {
2399 case NET_SFTP_TYPE_BLOCK_DEVICE:
2400 return 'block';
2401 case NET_SFTP_TYPE_CHAR_DEVICE:
2402 return 'char';
2403 case NET_SFTP_TYPE_DIRECTORY:
2404 return 'dir';
2405 case NET_SFTP_TYPE_FIFO:
2406 return 'fifo';
2407 case NET_SFTP_TYPE_REGULAR:
2408 return 'file';
2409 case NET_SFTP_TYPE_SYMLINK:
2410 return 'link';
2411 default:
2412 return false;
2413 }
2414 }
2415
2427 {
2428 return $this->_get_xstat_cache_prop($path, $prop, 'stat');
2429 }
2430
2442 {
2443 return $this->_get_xstat_cache_prop($path, $prop, 'lstat');
2444 }
2445
2457 {
2458 if ($this->use_stat_cache) {
2459 $path = $this->_realpath($path);
2460
2462
2463 if (is_object($result) && isset($result->$type)) {
2464 return $result->{$type}[$prop];
2465 }
2466 }
2467
2468 $result = $this->$type($path);
2469
2470 if ($result === false || !isset($result[$prop])) {
2471 return false;
2472 }
2473
2474 return $result[$prop];
2475 }
2476
2485 function rename($oldname, $newname)
2486 {
2487 if (!($this->bitmap & SSH2::MASK_LOGIN)) {
2488 return false;
2489 }
2490
2491 $oldname = $this->_realpath($oldname);
2492 $newname = $this->_realpath($newname);
2493 if ($oldname === false || $newname === false) {
2494 return false;
2495 }
2496
2497 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
2498 $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
2499 if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
2500 return false;
2501 }
2502
2503 $response = $this->_get_sftp_packet();
2504 if ($this->packet_type != NET_SFTP_STATUS) {
2505 user_error('Expected SSH_FXP_STATUS');
2506 return false;
2507 }
2508
2509 // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
2510 extract(unpack('Nstatus', $this->_string_shift($response, 4)));
2511 if ($status != NET_SFTP_STATUS_OK) {
2512 $this->_logError($response, $status);
2513 return false;
2514 }
2515
2516 // don't move the stat cache entry over since this operation could very well change the
2517 // atime and mtime attributes
2518 //$this->_update_stat_cache($newname, $this->_query_stat_cache($oldname));
2519 $this->_remove_from_stat_cache($oldname);
2520 $this->_remove_from_stat_cache($newname);
2521
2522 return true;
2523 }
2524
2535 {
2536 $attr = array();
2537 extract(unpack('Nflags', $this->_string_shift($response, 4)));
2538 // SFTPv4+ have a type field (a byte) that follows the above flag field
2539 foreach ($this->attributes as $key => $value) {
2540 switch ($flags & $key) {
2541 case NET_SFTP_ATTR_SIZE: // 0x00000001
2542 // The size attribute is defined as an unsigned 64-bit integer.
2543 // The following will use floats on 32-bit platforms, if necessary.
2544 // As can be seen in the BigInteger class, floats are generally
2545 // IEEE 754 binary64 "double precision" on such platforms and
2546 // as such can represent integers of at least 2^50 without loss
2547 // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
2548 $attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
2549 break;
2550 case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
2551 $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
2552 break;
2553 case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
2554 $attr+= unpack('Npermissions', $this->_string_shift($response, 4));
2555 // mode == permissions; permissions was the original array key and is retained for bc purposes.
2556 // mode was added because that's the more industry standard terminology
2557 $attr+= array('mode' => $attr['permissions']);
2558 $fileType = $this->_parseMode($attr['permissions']);
2559 if ($fileType !== false) {
2560 $attr+= array('type' => $fileType);
2561 }
2562 break;
2563 case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
2564 $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
2565 break;
2566 case NET_SFTP_ATTR_EXTENDED: // 0x80000000
2567 extract(unpack('Ncount', $this->_string_shift($response, 4)));
2568 for ($i = 0; $i < $count; $i++) {
2569 extract(unpack('Nlength', $this->_string_shift($response, 4)));
2570 $key = $this->_string_shift($response, $length);
2571 extract(unpack('Nlength', $this->_string_shift($response, 4)));
2572 $attr[$key] = $this->_string_shift($response, $length);
2573 }
2574 }
2575 }
2576 return $attr;
2577 }
2578
2588 function _parseMode($mode)
2589 {
2590 // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12
2591 // see, also, http://linux.die.net/man/2/stat
2592 switch ($mode & 0170000) {// ie. 1111 0000 0000 0000
2593 case 0000000: // no file type specified - figure out the file type using alternative means
2594 return false;
2595 case 0040000:
2596 return NET_SFTP_TYPE_DIRECTORY;
2597 case 0100000:
2598 return NET_SFTP_TYPE_REGULAR;
2599 case 0120000:
2600 return NET_SFTP_TYPE_SYMLINK;
2601 // new types introduced in SFTPv5+
2602 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
2603 case 0010000: // named pipe (fifo)
2604 return NET_SFTP_TYPE_FIFO;
2605 case 0020000: // character special
2606 return NET_SFTP_TYPE_CHAR_DEVICE;
2607 case 0060000: // block special
2608 return NET_SFTP_TYPE_BLOCK_DEVICE;
2609 case 0140000: // socket
2610 return NET_SFTP_TYPE_SOCKET;
2611 case 0160000: // whiteout
2612 // "SPECIAL should be used for files that are of
2613 // a known type which cannot be expressed in the protocol"
2614 return NET_SFTP_TYPE_SPECIAL;
2615 default:
2616 return NET_SFTP_TYPE_UNKNOWN;
2617 }
2618 }
2619
2635 function _parseLongname($longname)
2636 {
2637 // http://en.wikipedia.org/wiki/Unix_file_types
2638 // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions
2639 if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) {
2640 switch ($longname[0]) {
2641 case '-':
2642 return NET_SFTP_TYPE_REGULAR;
2643 case 'd':
2644 return NET_SFTP_TYPE_DIRECTORY;
2645 case 'l':
2646 return NET_SFTP_TYPE_SYMLINK;
2647 default:
2648 return NET_SFTP_TYPE_SPECIAL;
2649 }
2650 }
2651
2652 return false;
2653 }
2654
2668 {
2669 $packet = $this->request_id !== false ?
2670 pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) :
2671 pack('NCa*', strlen($data) + 1, $type, $data);
2672
2673 $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
2674 $result = $this->_send_channel_packet(self::CHANNEL, $packet);
2675 $stop = strtok(microtime(), ' ') + strtok('');
2676
2677 if (defined('NET_SFTP_LOGGING')) {
2678 $packet_type = '-> ' . $this->packet_types[$type] .
2679 ' (' . round($stop - $start, 4) . 's)';
2680 if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) {
2681 echo "<pre>\r\n" . $this->_format_log(array($data), array($packet_type)) . "\r\n</pre>\r\n";
2682 flush();
2683 ob_flush();
2684 } else {
2685 $this->packet_type_log[] = $packet_type;
2686 if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
2687 $this->packet_log[] = $data;
2688 }
2689 }
2690 }
2691
2692 return $result;
2693 }
2694
2709 {
2710 $this->curTimeout = false;
2711
2712 $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
2713
2714 // SFTP packet length
2715 while (strlen($this->packet_buffer) < 4) {
2716 $temp = $this->_get_channel_packet(self::CHANNEL);
2717 if (is_bool($temp)) {
2718 $this->packet_type = false;
2719 $this->packet_buffer = '';
2720 return false;
2721 }
2722 $this->packet_buffer.= $temp;
2723 }
2724 extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4)));
2725 $tempLength = $length;
2726 $tempLength-= strlen($this->packet_buffer);
2727
2728 // SFTP packet type and data payload
2729 while ($tempLength > 0) {
2730 $temp = $this->_get_channel_packet(self::CHANNEL);
2731 if (is_bool($temp)) {
2732 $this->packet_type = false;
2733 $this->packet_buffer = '';
2734 return false;
2735 }
2736 $this->packet_buffer.= $temp;
2737 $tempLength-= strlen($temp);
2738 }
2739
2740 $stop = strtok(microtime(), ' ') + strtok('');
2741
2742 $this->packet_type = ord($this->_string_shift($this->packet_buffer));
2743
2744 if ($this->request_id !== false) {
2745 $this->_string_shift($this->packet_buffer, 4); // remove the request id
2746 $length-= 5; // account for the request id and the packet type
2747 } else {
2748 $length-= 1; // account for the packet type
2749 }
2750
2751 $packet = $this->_string_shift($this->packet_buffer, $length);
2752
2753 if (defined('NET_SFTP_LOGGING')) {
2754 $packet_type = '<- ' . $this->packet_types[$this->packet_type] .
2755 ' (' . round($stop - $start, 4) . 's)';
2756 if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) {
2757 echo "<pre>\r\n" . $this->_format_log(array($packet), array($packet_type)) . "\r\n</pre>\r\n";
2758 flush();
2759 ob_flush();
2760 } else {
2761 $this->packet_type_log[] = $packet_type;
2762 if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
2763 $this->packet_log[] = $packet;
2764 }
2765 }
2766 }
2767
2768 return $packet;
2769 }
2770
2779 function getSFTPLog()
2780 {
2781 if (!defined('NET_SFTP_LOGGING')) {
2782 return false;
2783 }
2784
2785 switch (NET_SFTP_LOGGING) {
2786 case NET_SFTP_LOG_COMPLEX:
2787 return $this->_format_log($this->packet_log, $this->packet_type_log);
2788 break;
2789 //case NET_SFTP_LOG_SIMPLE:
2790 default:
2791 return $this->packet_type_log;
2792 }
2793 }
2794
2801 function getSFTPErrors()
2802 {
2803 return $this->sftp_errors;
2804 }
2805
2813 {
2814 return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : '';
2815 }
2816
2824 {
2825 $temp = array('version' => $this->version);
2826 if (isset($this->extensions['versions'])) {
2827 $temp['extensions'] = $this->extensions['versions'];
2828 }
2829 return $temp;
2830 }
2831
2839 function _disconnect($reason)
2840 {
2841 $this->pwd = false;
2842 parent::_disconnect($reason);
2843 }
2844}
$result
$size
Definition: RandomTest.php:84
$path
Definition: aliased.php:25
$filename
Definition: buildRTE.php:89
An exception for terminatinating execution or to throw for unit testing.
fileperms($path)
Gets file permissions.
Definition: SFTP.php:2343
_nlist_helper($dir, $recursive, $relativeDir)
Helper method for nlist.
Definition: SFTP.php:749
chown($filename, $uid, $recursive=false)
Changes file or directory owner.
Definition: SFTP.php:1363
const RESUME
Resumes an upload.
Definition: SFTP.php:83
nlist($dir='.', $recursive=false)
Returns a list of files in the given directory.
Definition: SFTP.php:735
__construct($host, $port=22, $timeout=10)
Default Constructor.
Definition: SFTP.php:252
_parseAttributes(&$response)
Parse Attributes.
Definition: SFTP.php:2534
_send_sftp_packet($type, $data)
Sends SFTP Packets.
Definition: SFTP.php:2667
setListOrder()
Defines how nlist() and rawlist() will be sorted - if at all.
Definition: SFTP.php:1002
_read_put_responses($i)
Reads multiple successive SSH_FXP_WRITE responses.
Definition: SFTP.php:1953
_stat($filename, $type)
Returns general information about a file or symbolic link.
Definition: SFTP.php:1265
login($username)
Login.
Definition: SFTP.php:389
_parseMode($mode)
Attempt to identify the file type.
Definition: SFTP.php:2588
clearStatCache()
Clear the stat cache.
Definition: SFTP.php:566
const SOURCE_CALLBACK
Reads data from callback: function callback($length) returns string to proceed, null for EOF.
Definition: SFTP.php:79
_disconnect($reason)
Disconnect.
Definition: SFTP.php:2839
stat($filename)
Returns general information about a file.
Definition: SFTP.php:1141
_delete_recursive($path, &$i)
Recursively deletes directories on the SFTP server.
Definition: SFTP.php:2182
chdir($dir)
Changes the current directory.
Definition: SFTP.php:672
getSupportedVersions()
Get supported SFTP versions.
Definition: SFTP.php:2823
rawlist($dir='.', $recursive=false)
Returns a detailed list of files in the given directory.
Definition: SFTP.php:784
_update_stat_cache($path, $value)
Save files / directories to cache.
Definition: SFTP.php:1047
touch($filename, $time=null, $atime=null)
Sets access and modification time of file.
Definition: SFTP.php:1312
_close_handle($handle)
Close handle.
Definition: SFTP.php:1979
rename($oldname, $newname)
Renames a file or a directory on the SFTP server.
Definition: SFTP.php:2485
size($filename)
Returns the file size, in bytes, or false, on failure.
Definition: SFTP.php:1027
mkdir($dir, $mode=-1, $recursive=false)
Creates a directory.
Definition: SFTP.php:1657
readlink($link)
Return the target of a symbolic link.
Definition: SFTP.php:1577
chgrp($filename, $gid, $recursive=false)
Changes file or directory group.
Definition: SFTP.php:1383
getSFTPErrors()
Returns all errors.
Definition: SFTP.php:2801
_get_stat_cache_prop($path, $prop)
Return a stat properity.
Definition: SFTP.php:2426
rmdir($dir)
Removes a directory.
Definition: SFTP.php:1720
is_dir($path)
Tells whether the filename is a directory.
Definition: SFTP.php:2271
disableStatCache()
Disable the stat cache.
Definition: SFTP.php:546
_remove_from_stat_cache($path)
Remove files / directories from cache.
Definition: SFTP.php:1091
chmod($mode, $filename, $recursive=false)
Set permissions on a file.
Definition: SFTP.php:1402
_comparator($a, $b)
Compares two rawlist entries using parameters set by setListOrder()
Definition: SFTP.php:928
_list($dir, $raw=true)
Reads a list, be it detailed or not, of files in the given directory.
Definition: SFTP.php:818
pwd()
Returns the current directory name.
Definition: SFTP.php:577
getLastSFTPError()
Returns the last error.
Definition: SFTP.php:2812
fileowner($path)
Gets file owner.
Definition: SFTP.php:2355
_parseLongname($longname)
Parse Longname.
Definition: SFTP.php:2635
is_link($path)
Tells whether the filename is a symbolic link.
Definition: SFTP.php:2303
filemtime($path)
Gets file modification time.
Definition: SFTP.php:2331
_get_xstat_cache_prop($path, $prop, $type)
Return a stat or lstat properity.
Definition: SFTP.php:2456
enableStatCache()
Enable the stat cache.
Definition: SFTP.php:556
_logError($response, $status=-1)
Logs errors.
Definition: SFTP.php:589
_setstat($filename, $attr, $recursive)
Sets information about a file.
Definition: SFTP.php:1450
_setstat_recursive($path, $attr, &$i)
Recursively sets information on directories on the SFTP server.
Definition: SFTP.php:1509
_get_lstat_cache_prop($path, $prop)
Return an lstat properity.
Definition: SFTP.php:2441
truncate($filename, $new_size)
Truncates a file to a given length.
Definition: SFTP.php:1294
const SOURCE_STRING
Reads data from a string.
Definition: SFTP.php:74
const SOURCE_LOCAL_FILE
#+ @access public
Definition: SFTP.php:69
_mkdir_helper($dir, $attr)
Helper function for directory creation.
Definition: SFTP.php:1692
_realpath($path)
Canonicalize the Server-Side Path Name.
Definition: SFTP.php:616
const CHANNEL
SFTP channel constant.
Definition: SFTP.php:60
filegroup($path)
Gets file group.
Definition: SFTP.php:2367
filetype($path)
Gets file type.
Definition: SFTP.php:2391
is_file($path)
Tells whether the filename is a regular file.
Definition: SFTP.php:2287
_query_stat_cache($path)
Checks cache for path.
Definition: SFTP.php:1118
getSFTPLog()
Returns a log of the packets that have been sent and received.
Definition: SFTP.php:2779
symlink($target, $link)
Create a symlink.
Definition: SFTP.php:1621
fileatime($path)
Gets last access time of file.
Definition: SFTP.php:2319
filesize($path)
Gets file size.
Definition: SFTP.php:2379
lstat($filename)
Returns general information about a file or symbolic link.
Definition: SFTP.php:1198
const RESUME_START
Append a local file to an already existing remote file.
Definition: SFTP.php:87
put($remote_file, $data, $mode=self::SOURCE_STRING, $start=-1, $local_start=-1, $progressCallback=null)
Uploads a file to the SFTP server.
Definition: SFTP.php:1802
_get_sftp_packet()
Receives SFTP Packets.
Definition: SFTP.php:2708
file_exists($path)
Checks whether a file or directory exists.
Definition: SFTP.php:2248
_get_channel_packet($client_channel, $skip_extended=false)
Gets channel data.
Definition: SSH2.php:3175
const MASK_LOGIN
Definition: SSH2.php:82
_string_shift(&$string, $index=1)
String Shift.
Definition: SSH2.php:3631
_define_array()
Define Array.
Definition: SSH2.php:3648
_send_binary_packet($data, $logged=null)
Sends Binary Packets.
Definition: SSH2.php:3393
$timeout
Timeout.
Definition: SSH2.php:640
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
if(array_key_exists('yes', $_REQUEST)) $attributes
Definition: getconsent.php:85
$time
Definition: cron.php:21
$target
Definition: test.php:19
$files
Definition: metarefresh.php:49
Pure-PHP implementations of SFTP.
Pure-PHP implementation of SSHv2.
$type
$response
$start
Definition: bench.php:8
$data
Definition: bench.php:6