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 | |
---|
32 | set_include_path(get_include_path() . PATH_SEPARATOR . ABSPATH . '/phpseclib'); |
---|
33 | require_once('Net/SFTP.php'); |
---|
34 | |
---|
35 | |
---|
36 | class 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 | ?> |
---|