WordPress.org

Make WordPress Core

Ticket #21610: class-wp-filesystem-sftp.php

File class-wp-filesystem-sftp.php, 9.9 KB (added by albert@…, 20 months ago)

class WP_Filesystem_SFTP

Line 
1<?php
2/**
3 * WordPress SFTP Filesystem.
4 *
5 * @package WordPress
6 * @subpackage Filesystem
7 */
8
9/**
10 * WordPress Filesystem Class for implementing SFTP (including sftp-internal support).
11 * Requires phpseclib (http://phpseclib.sourceforge.net)
12 * @contrib http://www.mediacaster.nl/  Albert Siersema  albert@mediacaster.nl
13 *
14 * Installation instructions
15 * -------------------------
16 * Unpack/copy phpseclib in your core WordPress directory (i.e. where wp-config.php lives.)
17 * See set_include_path() below.
18 *
19 * wp-admin/includes/file.php, function get_filesystem_method() :
20 * comment out the ssh2 line, add the sftp line:
21 *    // if ( ! $method && isset($args['connection_type']) && 'ssh' == $args['connection_type'] && extension_loaded('ssh2') && function_exists('stream_get_contents') ) $method = 'ssh2';
22 *    if ( ! $method && isset($args['connection_type']) && 'ssh' == $args['connection_type'] ) $method = 'sftp';
23 *
24 * @since 2.7
25 * @package WordPress
26 * @subpackage Filesystem
27 * @uses WP_Filesystem_Base Extends class
28 *
29 * TODO: caching for stat's ?
30 */
31
32set_include_path(get_include_path() . PATH_SEPARATOR . ABSPATH . '/phpseclib');
33require_once('Net/SFTP.php');
34
35
36class WP_Filesystem_SFTP extends WP_Filesystem_Base {
37
38        var $link = false;
39        var $sftp_link = false;
40        var $errors = array();
41        var $options = array();
42
43
44        function __construct($opt='') {
45                $this->method = 'sftp';
46                $this->errors = new WP_Error();
47
48                // Set defaults:
49                if ( empty($opt['port']) )
50                        $this->options['port'] = 22;
51                else
52                        $this->options['port'] = $opt['port'];
53
54                if ( empty($opt['hostname']) )
55                        $this->errors->add('empty_hostname', __('SFTP hostname is required'));
56                else
57                        $this->options['hostname'] = $opt['hostname'];
58
59                if ( ! empty($opt['base']) )
60                        $this->wp_base = $opt['base'];
61
62                if ( empty ($opt['username']) ) {
63                        $this->errors->add('empty_username', __('SFTP username is required'));
64                }
65
66                if ( !empty($opt['username']) )
67                        $this->options['username'] = $opt['username'];
68
69                if ( empty ($opt['password']) ) {
70                        $this->errors->add('empty_password', __('SFTP password is required'));
71                } else {
72                        $this->options['password'] = $opt['password'];
73                }
74
75        }
76
77
78        function connect() {
79                $this->link = new Net_SFTP($this->options['hostname'], $this->options['port']);
80                if ( ! $this->link ) {
81                        $this->errors->add('connect', sprintf(__('Failed to connect to SFTP Server %1$s:%2$s'), $this->options['hostname'], $this->options['port']));
82                        return false;
83                }
84                if ( ! $this->link->login($this->options['username'], $this->options['password']) ) {
85                        $this->errors->add('auth', sprintf(__('Username/Password incorrect for %s'), $this->options['username']));
86                        return false;
87                }
88                return true;
89        }
90
91
92        function run_command( $command, $returnbool = false) {
93
94                $validSFTPCommands = array(
95                /*
96                sftp CLI commands:
97                 'cd',
98                 'chgrp',
99                 'chmod',
100                 'chown',
101                 'df',
102                 'get',
103                 'ln',
104                 'ls',
105                 'mkdir',
106                 'put',
107                 'pwd',
108                 'rename',
109                 'rm',
110                 'rmdir',
111                 'symlink'
112                */
113                // Available Net_SFTP commands:
114                 'pwd',
115                 'chmod', // ignored though
116                 'chgrp', // ignored though
117                 'chown'  // ignored though
118                );
119                if ( ! $this->link )
120                        return false;
121                $cmdline = preg_split('/[[:blank:]]+/', $command);
122                if ( ! in_array(($cmd=$cmdline[0]), $validSFTPCommands) )
123                        return false;
124                if (substr($cmd, 0, 2) == 'ch') return true;
125                $data = $this->link->$cmd();
126                if ( $returnbool )
127                        return ( $data === false ) ? false : '' != trim($data);
128                else
129                        return $data;
130        }
131
132                // strip FTP_BASE part of path; reduce to relative path
133        function fixPath($file) {
134                if (defined('FTP_BASE')) {
135                        if (substr($file, 0, ($l=strlen(FTP_BASE))) == FTP_BASE)
136                                $file = ltrim(substr($file, $l), '/');
137                }
138                return $file;
139        }
140       
141
142        function get_contents($file, $type = '', $resumepos = 0 ) {
143                return $this->link->get($this->fixPath($file));
144        }
145
146
147        function get_contents_array($file) {
148                return preg_split("/\n+/", $this->get_contents($file));
149        }
150
151
152        function put_contents($file, $contents, $mode = false ) {
153                $file = $this->fixPath($file);
154                $ret = $this->link->put($file, $contents);
155                if ($mode !== false) $this->link->chmod($mode, $file);
156                return false !== $ret;
157        }
158
159
160        function cwd() {
161                $cwd = $this->run_command('pwd');
162                if ( $cwd )
163                        $cwd = trailingslashit($cwd);
164                return $cwd;
165        }
166
167
168        function chdir($dir) {
169                return $this->link->chdir($this->fixPath($dir));
170        }
171
172
173        function chgrp($file, $group, $recursive = false ) {
174                return true; // not supported
175        }
176
177
178        function chmod($file, $mode = false, $recursive = false) {
179                return true; // SFTP does support chmod, better though to configure the right (default) permissions on the server side
180        }
181
182
183        function chown($file, $owner, $recursive = false ) {
184                return true; // not supported
185        }
186       
187
188        function stat($file) {
189                $file = $this->fixPath($file);
190                $stat = $this->link->stat($file);
191                if ($stat !== false) {
192                  if (!isset($stat['permissions'])) {
193                    return false;
194                  }
195                  $stat['size'] = $this->link->size($file);
196                }
197                else {
198                }
199                return $stat;
200        }
201
202
203        function owner($file) {
204                $stat = $this->stat($file);
205                if ( ! $stat )
206                        return false;
207                if ( ! isset($stat['uid']) )
208                        return false;
209                $owneruid = $stat['uid'];
210                if ( ! function_exists('posix_getpwuid') )
211                        return $owneruid;
212                $ownerarray = posix_getpwuid($owneruid);
213                return $ownerarray['name'];
214        }
215
216
217        function getchmod($file) {
218                $stat = $this->stat($file);
219                return substr(($stat['permissions'] & 000777), -3);
220        }
221
222
223        function group($file) {
224                $stat = $this->stat($file);
225                if ( ! $stat )
226                        return false;
227                if ( ! isset($stat['gid']) )
228                        return false;
229                $ownergid = $stat['gid'];
230                if ( ! function_exists('posix_getgrgid') )
231                        return $gid;
232                $grouparray = posix_getgrgid($ownergid);
233                return $grouparray['name'];
234        }
235
236
237        function copy($source, $destination, $overwrite = false, $mode = false) {
238                if ( ! $overwrite && $this->exists($destination) )
239                        return false;
240                $content = $this->get_contents($source);
241                if ( false === $content)
242                        return false;
243                return $this->put_contents($destination, $content, $mode);
244        }
245
246
247        function move($source, $destination, $overwrite = false) {
248                return $this->link->rename($this->fixPath($source), $this->fixPath($destination));
249        }
250
251
252        function delete($file, $recursive = false, $type = false) {
253                $file = $this->fixPath($file);
254                if ( 'f' === $type || $this->is_file($file) ) {
255                        return $this->link->delete($file);
256                }
257                if ( ! $recursive ) {
258                         return $this->link->rmdir($file);
259                }
260                //At this point its a folder, and we're in recursive mode
261                $file = trailingslashit($file);
262                $filelist = $this->dirlist($file, true);
263                $retval = true;
264                if ( is_array($filelist) ) //false if no files, So check first.
265                        foreach ($filelist as $filename => $fileinfo)
266                                if ( ! $this->delete($file . $filename, $recursive, $fileinfo['type']) )
267                                        $retval = false;
268
269                if ( $this->exists($file) && ! $this->link->rmdir($file) )
270                        $retval = false;
271                return $retval;
272        }
273
274
275        function exists($file) {
276                return $this->stat($file) !== false;
277        }
278
279
280        function S_ISDIR($stat) {
281                return( ($stat['permissions'] & 040000) == 040000 );
282        }
283       
284
285        function S_ISREG($stat) {
286                return( ($stat['permissions'] & 0100000) == 0100000 );
287        }
288
289
290        function is_file($file) {
291                return $this->S_ISREG($this->stat($file));
292        }
293
294
295        function is_dir($path) {
296                return $this->S_ISDIR($this->stat($path));
297        }
298
299
300        function is_readable($file) {
301                $stat = $this->stat($file);
302                $perms = $stat['permissions'];
303                return ($perms & 0x000400);
304        }
305
306
307        function is_writable($file) {
308                $stat = $this->stat($file);
309                $perms = $stat['permissions'];
310                return ($perms & 0x000200);
311        }
312
313
314        function atime($file) {
315                $stat = $this->stat($file);
316                return $stat['atime'];
317        }
318
319
320        function mtime($file) {
321                $stat = $this->stat($file);
322                return $stat['mtime'];
323        }
324
325
326        function size($file) {
327                $stat = $this->stat($file);
328                return $stat['size'];
329        }
330
331
332        function touch($file, $time = 0, $atime = 0) {
333                //Not implemented.
334        }
335
336
337        function mkdir($path, $chmod = false, $chown = false, $chgrp = false) {
338                $path = untrailingslashit($this->fixPath($path));
339                if ( empty($path) )
340                        return false;
341                return $this->link->mkdir($path);
342        }
343
344
345        function rmdir($path, $recursive = false) {
346                return $this->delete($path, $recursive);
347        }
348
349
350        function dirlist($path, $include_hidden = true, $recursive = false) {
351                if ( $this->is_file($path) ) {
352                        $limit_file = basename($path);
353                        $path = dirname($path);
354                } else {
355                        $limit_file = false;
356                }
357
358                if ( ! $this->is_dir($path) )
359                        return false;
360
361                $ret = array();
362                $curdir = $this->fixPath($path);
363                $dir = $this->link->nlist($curdir);
364
365                if ( ! $dir )
366                        return false;
367
368                foreach ($dir as $entry) {
369                        $struc = $this->stat($curdir.'/'.$entry);
370                        $struc['name'] = $entry;
371
372
373                        if ( '.' == $struc['name'] || '..' == $struc['name'] )
374                                continue; //Do not care about these folders.
375
376                        if ( ! $include_hidden && '.' == $struc['name'][0] )
377                                continue;
378
379                        if ( $limit_file && $struc['name'] != $limit_file )
380                                continue;
381
382                        $struc['perms']         = $this->gethchmod($path.'/'.$entry);
383                        $struc['permsn']        = $struc['permissions'] & 000777;
384                        $struc['number']        = false;
385                        $struc['owner']         = $this->owner($path.'/'.$entry);
386                        $struc['group']         = $this->group($path.'/'.$entry);
387                        $struc['size']          = $this->size($path.'/'.$entry);
388                        $struc['lastmodunix']= $this->mtime($path.'/'.$entry);
389                        $struc['lastmod']   = date('M j',$struc['lastmodunix']);
390                        $struc['time']          = date('h:i:s',$struc['lastmodunix']);
391                        $struc['type']          = $this->is_dir($path.'/'.$entry) ? 'd' : 'f';
392
393                        if ( 'd' == $struc['type'] ) {
394                                if ( $recursive )
395                                        $struc['files'] = $this->dirlist($path . '/' . $struc['name'], $include_hidden, $recursive);
396                                else
397                                        $struc['files'] = array();
398                        }
399
400                        $ret[ $struc['name'] ] = $struc;
401                }
402                return $ret;
403        }
404       
405
406        function lastError() {
407          return $this->link->getLastSFTPError();
408        }
409
410
411        function getErrors() {
412          return $this->link->getSFTPErrors();
413        }
414}
415
416
417?>