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