PluginProbe
ManageWP Worker / 3.9.28
ManageWP Worker v3.9.28
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 / lib / PHPSecLib / Net / SFTP / Stream.php

Stream.php in ManageWP Worker 3.9.28, at lib/PHPSecLib/Net/SFTP/Stream.php

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