PluginProbe
ManageWP Worker / 4.9.25
ManageWP Worker v4.9.25
4.9.38 4.9.37 4.9.36 4.9.35 4.9.34 3.8.7 3.8.8 3.9.0 3.9.1 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.18 3.9.19 3.9.2 3.9.20 3.9.21 3.9.22 3.9.23 3.9.24 All 73 releases
worker / src / PHPSecLib / Net / SFTP / Stream.php

Stream.php in ManageWP Worker 4.9.25, at src/PHPSecLib/Net/SFTP/Stream.php

835 lines 22.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * SFTP Stream Wrapper
5 *
6 * Creates an sftp:// protocol handler that can be used with, for example, fopen(), dir(), etc.
7 *
8 * PHP version 5
9 *
10 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 * THE SOFTWARE.
27 *
28 * @category Net
29 * @package Net_SFTP_Stream
30 * @author Jim Wigginton <terrafrost@php.net>
31 * @copyright MMXIII Jim Wigginton
32 * @license http://www.opensource.org/licenses/mit-license.html MIT License
33 * @link http://phpseclib.sourceforge.net
34 */
35
36 /**
37 * SFTP Stream Wrapper
38 *
39 * @package Net_SFTP_Stream
40 * @author Jim Wigginton <terrafrost@php.net>
41 * @access public
42 */
43 class Net_SFTP_Stream
44 {
45 /**
46 * SFTP instances
47 *
48 * Rather than re-create the connection we re-use instances if possible
49 *
50 * @var Array
51 * @access static
52 */
53 public static $instances;
54
55 /**
56 * SFTP instance
57 *
58 * @var Object
59 * @access private
60 */
61 public $sftp;
62
63 /**
64 * Path
65 *
66 * @var String
67 * @access private
68 */
69 public $path;
70
71 /**
72 * Mode
73 *
74 * @var String
75 * @access private
76 */
77 public $mode;
78
79 /**
80 * Position
81 *
82 * @var Integer
83 * @access private
84 */
85 public $pos;
86
87 /**
88 * Size
89 *
90 * @var Integer
91 * @access private
92 */
93 public $size;
94
95 /**
96 * Directory entries
97 *
98 * @var Array
99 * @access private
100 */
101 public $entries;
102
103 /**
104 * EOF flag
105 *
106 * @var Boolean
107 * @access private
108 */
109 public $eof;
110
111 /**
112 * Context resource
113 *
114 * Technically this needs to be publically accessible so PHP can set it directly
115 *
116 * @var Resource
117 * @access public
118 */
119 public $context;
120
121 /**
122 * Notification callback function
123 *
124 * @var Callable
125 * @access public
126 */
127 public $notification;
128
129 /**
130 * Registers this class as a URL wrapper.
131 *
132 * @param optional String $protocol The wrapper name to be registered.
133 *
134 * @return Boolean True on success, false otherwise.
135 * @access public
136 */
137 public static function register($protocol = 'sftp')
138 {
139 if (in_array($protocol, stream_get_wrappers(), true)) {
140 return false;
141 }
142 $class = function_exists('get_called_class') ? get_called_class() : __CLASS__;
143
144 return stream_wrapper_register($protocol, $class);
145 }
146
147 /**
148 * The Constructor
149 *
150 * @access public
151 */
152 public function __construct()
153 {
154 if (defined('NET_SFTP_STREAM_LOGGING')) {
155 echo "__construct()\r\n";
156 }
157
158 if (!class_exists('Net_SFTP')) {
159 require_once dirname(__FILE__).'/../../Net/SFTP.php';
160 }
161 }
162
163 /**
164 * Path Parser
165 *
166 * Extract a path from a URI and actually connect to an SSH server if appropriate
167 *
168 * If "notification" is set as a context parameter the message code for successful login is
169 * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE.
170 *
171 * @param String $path
172 *
173 * @return String
174 * @access private
175 */
176 public function _parse_path($path)
177 {
178 extract(parse_url($path) + array('port' => 22));
179
180 if (!isset($host)) {
181 return false;
182 }
183
184 if (isset($this->context)) {
185 $context = stream_context_get_params($this->context);
186 if (isset($context['notification'])) {
187 $this->notification = $context['notification'];
188 }
189 }
190
191 if ($host[0] == '$') {
192 $host = substr($host, 1);
193 $host = $GLOBALS[$host];
194 if (!is_object($host) || get_class($host) != 'Net_SFTP') {
195 return false;
196 }
197 $this->sftp = $host;
198 } else {
199 if (isset($this->context)) {
200 $context = stream_context_get_options($this->context);
201 }
202 if (isset($context[$scheme]['session'])) {
203 $sftp = $context[$scheme]['session'];
204 }
205 if (isset($context[$scheme]['sftp'])) {
206 $sftp = $context[$scheme]['sftp'];
207 }
208 if (isset($sftp) && is_object($sftp) && get_class($sftp) == 'Net_SFTP') {
209 $this->sftp = $sftp;
210
211 return $path;
212 }
213 if (isset($context[$scheme]['username'])) {
214 $user = $context[$scheme]['username'];
215 }
216 if (isset($context[$scheme]['password'])) {
217 $pass = $context[$scheme]['password'];
218 }
219 if (isset($context[$scheme]['privkey']) && is_object($context[$scheme]['privkey']) && get_Class($context[$scheme]['privkey']) == 'Crypt_RSA') {
220 $pass = $context[$scheme]['privkey'];
221 }
222
223 if (!isset($user) || !isset($pass)) {
224 return false;
225 }
226
227 // casting $pass to a string is necessary in the event that it's a Crypt_RSA object
228 if (isset(self::$instances[$host][$port][$user][(string) $pass])) {
229 $this->sftp = self::$instances[$host][$port][$user][(string) $pass];
230 } else {
231 $this->sftp = new Net_SFTP($host, $port);
232 $this->sftp->disableStatCache();
233 if (isset($this->notification) && is_callable($this->notification)) {
234 /* if !is_callable($this->notification) we could do this:
235
236 user_error('fopen(): failed to call user notifier', E_USER_WARNING);
237
238 the ftp wrapper gives errors like that when the notifier isn't callable.
239 i've opted not to do that, however, since the ftp wrapper gives the line
240 on which the fopen occurred as the line number - not the line that the
241 user_error is on.
242 */
243 call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
244 call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
245 if (!$this->sftp->login($user, $pass)) {
246 call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0);
247
248 return false;
249 }
250 call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0);
251 } else {
252 if (!$this->sftp->login($user, $pass)) {
253 return false;
254 }
255 }
256 self::$instances[$host][$port][$user][(string) $pass] = $this->sftp;
257 }
258 }
259
260 return $path;
261 }
262
263 /**
264 * Opens file or URL
265 *
266 * @param String $path
267 * @param String $mode
268 * @param Integer $options
269 * @param String $opened_path
270 *
271 * @return Boolean
272 * @access public
273 */
274 public function _stream_open($path, $mode, $options, &$opened_path)
275 {
276 $path = $this->_parse_path($path);
277
278 if ($path === false) {
279 return false;
280 }
281 $this->path = $path;
282
283 $this->size = $this->sftp->size($path);
284 $this->mode = preg_replace('#[bt]$#', '', $mode);
285 $this->eof = false;
286
287 if ($this->size === false) {
288 if ($this->mode[0] == 'r') {
289 return false;
290 }
291 } else {
292 switch ($this->mode[0]) {
293 case 'x':
294 return false;
295 case 'w':
296 case 'c':
297 $this->sftp->truncate($path, 0);
298 }
299 }
300
301 $this->pos = $this->mode[0] != 'a' ? 0 : $this->size;
302
303 return true;
304 }
305
306 /**
307 * Read from stream
308 *
309 * @param Integer $count
310 *
311 * @return Mixed
312 * @access public
313 */
314 public function _stream_read($count)
315 {
316 switch ($this->mode) {
317 case 'w':
318 case 'a':
319 case 'x':
320 case 'c':
321 return false;
322 }
323
324 // commented out because some files - eg. /dev/urandom - will say their size is 0 when in fact it's kinda infinite
325 //if ($this->pos >= $this->size) {
326 // $this->eof = true;
327 // return false;
328 //}
329
330 $result = $this->sftp->get($this->path, false, $this->pos, $count);
331 if (isset($this->notification) && is_callable($this->notification)) {
332 if ($result === false) {
333 call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
334
335 return 0;
336 }
337 // seems that PHP calls stream_read in 8k chunks
338 call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size);
339 }
340
341 if (empty($result)) { // ie. false or empty string
342 $this->eof = true;
343
344 return false;
345 }
346 $this->pos += strlen($result);
347
348 return $result;
349 }
350
351 /**
352 * Write to stream
353 *
354 * @param String $data
355 *
356 * @return Mixed
357 * @access public
358 */
359 public function _stream_write($data)
360 {
361 switch ($this->mode) {
362 case 'r':
363 return false;
364 }
365
366 $result = $this->sftp->put($this->path, $data, NET_SFTP_STRING, $this->pos);
367 if (isset($this->notification) && is_callable($this->notification)) {
368 if (!$result) {
369 call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
370
371 return 0;
372 }
373 // seems that PHP splits up strings into 8k blocks before calling stream_write
374 call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data));
375 }
376
377 if ($result === false) {
378 return false;
379 }
380 $this->pos += strlen($data);
381 if ($this->pos > $this->size) {
382 $this->size = $this->pos;
383 }
384 $this->eof = false;
385
386 return strlen($data);
387 }
388
389 /**
390 * Retrieve the current position of a stream
391 *
392 * @return Integer
393 * @access public
394 */
395 public function _stream_tell()
396 {
397 return $this->pos;
398 }
399
400 /**
401 * Tests for end-of-file on a file pointer
402 *
403 * In my testing there are four classes functions that normally effect the pointer:
404 * fseek, fputs / fwrite, fgets / fread and ftruncate.
405 *
406 * Only fgets / fread, however, results in feof() returning true. do fputs($fp, 'aaa') on a blank file and feof()
407 * will return false. do fread($fp, 1) and feof() will then return true. do fseek($fp, 10) on ablank file and feof()
408 * will return false. do fread($fp, 1) and feof() will then return true.
409 *
410 * @return Boolean
411 * @access public
412 */
413 public function _stream_eof()
414 {
415 return $this->eof;
416 }
417
418 /**
419 * Seeks to specific location in a stream
420 *
421 * @param Integer $offset
422 * @param Integer $whence
423 *
424 * @return Boolean
425 * @access public
426 */
427 public function _stream_seek($offset, $whence)
428 {
429 switch ($whence) {
430 case SEEK_SET:
431 if ($offset >= $this->size || $offset < 0) {
432 return false;
433 }
434 break;
435 case SEEK_CUR:
436 $offset += $this->pos;
437 break;
438 case SEEK_END:
439 $offset += $this->size;
440 }
441
442 $this->pos = $offset;
443 $this->eof = false;
444
445 return true;
446 }
447
448 /**
449 * Change stream options
450 *
451 * @param String $path
452 * @param Integer $option
453 * @param Mixed $var
454 *
455 * @return Boolean
456 * @access public
457 */
458 public function _stream_metadata($path, $option, $var)
459 {
460 $path = $this->_parse_path($path);
461 if ($path === false) {
462 return false;
463 }
464
465 // stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the constants haven't been defined
466 // see http://www.php.net/streamwrapper.stream-metadata and https://bugs.php.net/64246
467 // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592
468 switch ($option) {
469 case 1: // PHP_STREAM_META_TOUCH
470 return $this->sftp->touch($path, $var[0], $var[1]);
471 case 2: // PHP_STREAM_OWNER_NAME
472 case 3: // PHP_STREAM_GROUP_NAME
473 return false;
474 case 4: // PHP_STREAM_META_OWNER
475 return $this->sftp->chown($path, $var);
476 case 5: // PHP_STREAM_META_GROUP
477 return $this->sftp->chgrp($path, $var);
478 case 6: // PHP_STREAM_META_ACCESS
479 return $this->sftp->chmod($path, $var) !== false;
480 }
481 }
482
483 /**
484 * Retrieve the underlaying resource
485 *
486 * @param Integer $cast_as
487 *
488 * @return Resource
489 * @access public
490 */
491 public function _stream_cast($cast_as)
492 {
493 return $this->sftp->fsock;
494 }
495
496 /**
497 * Advisory file locking
498 *
499 * @param Integer $operation
500 *
501 * @return Boolean
502 * @access public
503 */
504 public function _stream_lock($operation)
505 {
506 return false;
507 }
508
509 /**
510 * Renames a file or directory
511 *
512 * Attempts to rename oldname to newname, moving it between directories if necessary.
513 * If newname exists, it will be overwritten. This is a departure from what Net_SFTP
514 * does.
515 *
516 * @param String $path_from
517 * @param String $path_to
518 *
519 * @return Boolean
520 * @access public
521 */
522 public function _rename($path_from, $path_to)
523 {
524 $path1 = parse_url($path_from);
525 $path2 = parse_url($path_to);
526 unset($path1['path'], $path2['path']);
527 if ($path1 != $path2) {
528 return false;
529 }
530
531 $path_from = $this->_parse_path($path_from);
532 $path_to = parse_url($path_to);
533 if ($path_from == false) {
534 return false;
535 }
536
537 $path_to = $path_to['path']; // the $component part of parse_url() was added in PHP 5.1.2
538 // "It is an error if there already exists a file with the name specified by newpath."
539 // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5
540 if (!$this->sftp->rename($path_from, $path_to)) {
541 if ($this->sftp->stat($path_to)) {
542 return $this->sftp->delete($path_to, true) && $this->sftp->rename($path_from, $path_to);
543 }
544
545 return false;
546 }
547
548 return true;
549 }
550
551 /**
552 * Open directory handle
553 *
554 * The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and
555 * removed in 5.4 I'm just going to ignore it.
556 *
557 * Also, nlist() is the best that this function is realistically going to be able to do. When an SFTP client
558 * sends a SSH_FXP_READDIR packet you don't generally get info on just one file but on multiple files. Quoting
559 * the SFTP specs:
560 *
561 * The SSH_FXP_NAME response has the following format:
562 *
563 * uint32 id
564 * uint32 count
565 * repeats count times:
566 * string filename
567 * string longname
568 * ATTRS attrs
569 *
570 * @param String $path
571 * @param Integer $options
572 *
573 * @return Boolean
574 * @access public
575 */
576 public function _dir_opendir($path, $options)
577 {
578 $path = $this->_parse_path($path);
579 if ($path === false) {
580 return false;
581 }
582 $this->pos = 0;
583 $this->entries = $this->sftp->nlist($path);
584
585 return $this->entries !== false;
586 }
587
588 /**
589 * Read entry from directory handle
590 *
591 * @return Mixed
592 * @access public
593 */
594 public function _dir_readdir()
595 {
596 if (isset($this->entries[$this->pos])) {
597 return $this->entries[$this->pos++];
598 }
599
600 return false;
601 }
602
603 /**
604 * Rewind directory handle
605 *
606 * @return Boolean
607 * @access public
608 */
609 public function _dir_rewinddir()
610 {
611 $this->pos = 0;
612
613 return true;
614 }
615
616 /**
617 * Close directory handle
618 *
619 * @return Boolean
620 * @access public
621 */
622 public function _dir_closedir()
623 {
624 return true;
625 }
626
627 /**
628 * Create a directory
629 *
630 * Only valid $options is STREAM_MKDIR_RECURSIVE
631 *
632 * @param String $path
633 * @param Integer $mode
634 * @param Integer $options
635 *
636 * @return Boolean
637 * @access public
638 */
639 public function _mkdir($path, $mode, $options)
640 {
641 $path = $this->_parse_path($path);
642 if ($path === false) {
643 return false;
644 }
645
646 return $this->sftp->mkdir($path, $mode, $options & STREAM_MKDIR_RECURSIVE);
647 }
648
649 /**
650 * Removes a directory
651 *
652 * Only valid $options is STREAM_MKDIR_RECURSIVE per <http://php.net/streamwrapper.rmdir>, however,
653 * <http://php.net/rmdir> does not have a $recursive parameter as mkdir() does so I don't know how
654 * STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it out with rmdir() I get 8 as
655 * $options. What does 8 correspond to?
656 *
657 * @param String $path
658 * @param Integer $mode
659 * @param Integer $options
660 *
661 * @return Boolean
662 * @access public
663 */
664 public function _rmdir($path, $options)
665 {
666 $path = $this->_parse_path($path);
667 if ($path === false) {
668 return false;
669 }
670
671 return $this->sftp->rmdir($path);
672 }
673
674 /**
675 * Flushes the output
676 *
677 * See <http://php.net/fflush>. Always returns true because Net_SFTP doesn't cache stuff before writing
678 *
679 * @return Boolean
680 * @access public
681 */
682 public function _stream_flush()
683 {
684 return true;
685 }
686
687 /**
688 * Retrieve information about a file resource
689 *
690 * @return Mixed
691 * @access public
692 */
693 public function _stream_stat()
694 {
695 $results = $this->sftp->stat($this->path);
696 if ($results === false) {
697 return false;
698 }
699
700 return $results;
701 }
702
703 /**
704 * Delete a file
705 *
706 * @param String $path
707 *
708 * @return Boolean
709 * @access public
710 */
711 public function _unlink($path)
712 {
713 $path = $this->_parse_path($path);
714 if ($path === false) {
715 return false;
716 }
717
718 return $this->sftp->delete($path, false);
719 }
720
721 /**
722 * Retrieve information about a file
723 *
724 * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of Net_SFTP_Stream is quiet by default
725 * might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll
726 * cross that bridge when and if it's reached
727 *
728 * @param String $path
729 * @param Integer $flags
730 *
731 * @return Mixed
732 * @access public
733 */
734 public function _url_stat($path, $flags)
735 {
736 $path = $this->_parse_path($path);
737 if ($path === false) {
738 return false;
739 }
740
741 $results = $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path);
742 if ($results === false) {
743 return false;
744 }
745
746 return $results;
747 }
748
749 /**
750 * Truncate stream
751 *
752 * @param Integer $new_size
753 *
754 * @return Boolean
755 * @access public
756 */
757 public function _stream_truncate($new_size)
758 {
759 if (!$this->sftp->truncate($this->path, $new_size)) {
760 return false;
761 }
762
763 $this->eof = false;
764 $this->size = $new_size;
765
766 return true;
767 }
768
769 /**
770 * Change stream options
771 *
772 * STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't.
773 * The other two aren't supported because of limitations in Net_SFTP.
774 *
775 * @param Integer $option
776 * @param Integer $arg1
777 * @param Integer $arg2
778 *
779 * @return Boolean
780 * @access public
781 */
782 public function _stream_set_option($option, $arg1, $arg2)
783 {
784 return false;
785 }
786
787 /**
788 * Close an resource
789 *
790 * @access public
791 */
792 public function _stream_close()
793 {
794 }
795
796 /**
797 * __call Magic Method
798 *
799 * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you.
800 * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function
801 * lets you figure that out.
802 *
803 * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not
804 * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method.
805 *
806 * @param String
807 * @param Array
808 *
809 * @return Mixed
810 * @access public
811 */
812 public function __call($name, $arguments)
813 {
814 if (defined('NET_SFTP_STREAM_LOGGING')) {
815 echo $name.'(';
816 $last = count($arguments) - 1;
817 foreach ($arguments as $i => $argument) {
818 var_export($argument);
819 if ($i != $last) {
820 echo ',';
821 }
822 }
823 echo ")\r\n";
824 }
825 $name = '_'.$name;
826 if (!method_exists($this, $name)) {
827 return false;
828 }
829
830 return call_user_func_array(array($this, $name), $arguments);
831 }
832 }
833
834 Net_SFTP_Stream::register();
835