Make WordPress Core

Ticket #7690: class-wp-filesystem-ssh2.php

File class-wp-filesystem-ssh2.php, 12.3 KB (added by ShaneF, 18 years ago)

This is the SSH2 Transfer Class -- UNTESTED -- Needs testing on Unix/Linux boxes with libssh2.so enabled and working

Line 
1<?php
2/**
3 * WordPress SSH2 Filesystem.
4 *
5 * @package WordPress
6 * @subpackage Filesystem
7 */
8
9/**
10 * WordPress Filesystem Class for implementing SSH2.
11 *
12 * @since 2.7
13 * @package WordPress
14 * @subpackage Filesystem
15 * @uses WP_Filesystem_Base Extends class
16 */
17class WP_Filesystem_SSH2 extends WP_Filesystem_Base {
18
19 var $debugtest = true; // this is my var that will output the text when debuggin this class
20
21 var $link;
22 var $timeout = 5;
23 var $errors = array();
24 var $options = array();
25
26 var $permission = null;
27
28 var $filetypes = array(
29 'php'=>FTP_ASCII,
30 'css'=>FTP_ASCII,
31 'txt'=>FTP_ASCII,
32 'js'=>FTP_ASCII,
33 'html'=>FTP_ASCII,
34 'htm'=>FTP_ASCII,
35 'xml'=>FTP_ASCII,
36
37 'jpg'=>FTP_BINARY,
38 'png'=>FTP_BINARY,
39 'gif'=>FTP_BINARY,
40 'bmp'=>FTP_BINARY
41 );
42
43 function WP_Filesystem_SSH2($opt='') {
44 $this->method = 'ssh2';
45 $this->errors = new WP_Error();
46
47 //Check if possible to use ftp functions.
48 if ( ! extension_loaded('ssh2') ) {
49 $this->errors->add('no_ssh2_ext', __('The ssh2 PHP extension is not available'));
50 return false;
51 }
52
53 // Set defaults:
54 if ( empty($opt['port']) )
55 $this->options['port'] = 22;
56 else
57 $this->options['port'] = $opt['port'];
58
59 if ( empty($opt['hostname']) )
60 $this->errors->add('empty_hostname', __('SSH2 hostname is required'));
61 else
62 $this->options['hostname'] = $opt['hostname'];
63
64 if ( isset($opt['base']) && ! empty($opt['base']) )
65 $this->wp_base = $opt['base'];
66
67 // Check if the options provided are OK.
68 if ( empty ($opt['username']) )
69 $this->errors->add('empty_username', __('SSH2 username is required'));
70 else
71 $this->options['username'] = $opt['username'];
72
73 if ( empty ($opt['password']) )
74 $this->errors->add('empty_password', __('SSH password is required'));
75 else
76 $this->options['password'] = $opt['password'];
77
78 }
79
80 function connect() {
81 $this->debug("connect();");
82 $this->link = @ssh2_connect($this->options['hostname'], $this->options['port']);
83
84 if ( ! $this->link ) {
85 $this->errors->add('connect', sprintf(__('Failed to connect to SSH2 Server %1$s:%2$s'), $this->options['hostname'], $this->options['port']));
86 return false;
87 }
88
89 if ( ! @ssh2_auth_password($this->link,$this->options['username'], $this->options['password']) ) {
90 $this->errors->add('auth', sprintf(__('Username/Password incorrect for %s'), $this->options['username']));
91 return false;
92 }
93
94 return true;
95 }
96
97 function run_command($link, $command, $returnbool = false) {
98 $this->debug("run_command(".$command.");");
99 if(!($stream = @ssh2_exec( $link, $command ))) {
100 $this->errors->add('command', sprintf(__('Unable to preform command: %s'), $command));
101 } else {
102 stream_set_blocking( $stream, true );
103 $time_start = time();
104 $data = "";
105 while( true ) {
106 if( (time()-$time_start) > $this->timeout ){
107 $this->errors->add('command', sprintf(__('Connection to the server has timeout after %s seconds.'), $this->timeout));
108 break;
109 }
110 while( $buf = fread( $stream, strlen($stream) ) ){
111 $data .= $buf;
112 }
113 }
114 fclose($stream);
115 if (($returnbool) && ($data)) {
116 $this->debug("Data: " . print_r($data, true) . " Returning: True");
117 return true;
118 } elseif (($returnbool) && (!$data)) {
119 $this->debug("Data: " . print_r($data, true) . " Returning: False");
120 return false;
121 } else {
122 $this->debug("Data: " . print_r($data, true));
123 return $data;
124 }
125 }
126 }
127
128 function debug($text)
129 {
130 if ($this->debugtest)
131 {
132 echo $text . "<br/>";
133 }
134 }
135
136 function setDefaultPermissions($perm) {
137 $this->permission = $perm;
138 }
139
140 function get_contents($file, $type = '', $resumepos = 0 ){
141 if( empty($type) ){
142 $extension = substr(strrchr($file, "."), 1);
143 $type = isset($this->filetypes[ $extension ]) ? $this->filetypes[ $extension ] : FTP_ASCII;
144 }
145 $temp = tmpfile();
146 if ( ! $temp )
147 return false;
148 if( ! @ssh2_scp_recv($this->link, $temp, $file) )
149 return false;
150 fseek($temp, 0); //Skip back to the start of the file being written to
151 $contents = '';
152 while (!feof($temp)) {
153 $contents .= fread($temp, 8192);
154 }
155 fclose($temp);
156 return $contents;
157 }
158
159 function get_contents_array($file) {
160 return explode("\n", $this->get_contents($file));
161 }
162
163 function put_contents($file, $contents, $type = '' ) {
164 if( empty($type) ) {
165 $extension = substr(strrchr($file, "."), 1);
166 $type = isset($this->filetypes[ $extension ]) ? $this->filetypes[ $extension ] : FTP_ASCII;
167 }
168 $temp = tmpfile();
169 if ( ! $temp )
170 return false;
171 fwrite($temp, $contents);
172 fseek($temp, 0); //Skip back to the start of the file being written to
173 $ret = @ssh2_scp_send($this->link, $file, $temp, $type);
174 fclose($temp);
175 return $ret;
176 }
177
178 function cwd() {
179 $cwd = $this->run_command($this->link, "pwd");
180 if( $cwd )
181 $cwd = trailingslashit($cwd);
182 return $cwd;
183 }
184
185 function chdir($dir) {
186 if ($this->run_command($this->link, "cd " . $dir, true)) {
187 return true;
188 }
189 return false;
190 }
191
192 function chgrp($file, $group, $recursive = false ) {
193 return false;
194 }
195
196 function chmod($file, $mode = false, $recursive = false) {
197 if( ! $mode )
198 $mode = $this->permission;
199 if( ! $mode )
200 return false;
201 if ( ! $this->exists($file) )
202 return false;
203 if ( ! $recursive || ! $this->is_dir($file) ) {
204 return $this->run_command($this->link, sprintf('CHMOD %o %s', $mode, $file), true);
205 }
206 //Is a directory, and we want recursive
207 $filelist = $this->dirlist($file);
208 foreach($filelist as $filename){
209 $this->chmod($file . '/' . $filename, $mode, $recursive);
210 }
211 return true;
212 }
213
214 function chown($file, $owner, $recursive = false ) {
215 return false;
216 }
217
218 function owner($file) {
219 $dir = $this->dirlist($file);
220 return $dir[$file]['owner'];
221 }
222
223 function getchmod($file) {
224 $dir = $this->dirlist($file);
225 return $dir[$file]['permsn'];
226 }
227
228 function group($file) {
229 $dir = $this->dirlist($file);
230 return $dir[$file]['group'];
231 }
232
233 function copy($source, $destination, $overwrite = false ) {
234 if( ! $overwrite && $this->exists($destination) )
235 return false;
236 $content = $this->get_contents($source);
237 if( false === $content)
238 return false;
239 return $this->put_contents($destination, $content);
240 }
241
242 function move($source, $destination, $overwrite = false) {
243 return @ssh2_sftp_rename($this->link, $source, $destination);
244 }
245
246 function delete($file, $recursive=false) {
247 if ( $this->is_file($file) )
248 return @ssh2_sftp_unlink($this->link, $file);
249 if ( !$recursive )
250 return @ssh2_sftp_rmdir($this->link, $file);
251 $filelist = $this->dirlist($file);
252 foreach ((array) $filelist as $filename => $fileinfo) {
253 $this->delete($file . '/' . $filename, $recursive);
254 }
255 return @ssh2_sftp_rmdir($this->link, $file);
256 }
257
258 function exists($file) {
259 $list = $this->run_command($this->link, sprintf('ls -la %s', $file));
260 if( ! $list )
261 return false;
262 return count($list) == 1 ? true : false;
263 }
264
265 function is_file($file) {
266 return $this->is_dir($file) ? false : true;
267 }
268
269 function is_dir($path) {
270 $cwd = $this->cwd();
271 $result = $this->run_command($this->link, sprintf('cd %s', $path), true);
272 if( $result && $path == $this->cwd() || $this->cwd() != $cwd ) {
273 // @todo: use ssh2_exec
274 @ftp_chdir($this->link, $cwd);
275 return true;
276 }
277 return false;
278 }
279
280 function is_readable($file) {
281 //Get dir list, Check if the file is writable by the current user??
282 return true;
283 }
284
285 function is_writable($file) {
286 //Get dir list, Check if the file is writable by the current user??
287 return true;
288 }
289
290 function atime($file) {
291 return false;
292 }
293
294 function mtime($file) {
295 return; // i have to look up to see if there is a way in SSH2 to look the modifed date
296 // return ftp_mdtm($this->link, $file);
297 }
298
299 function size($file) {
300 return; // i have to look up to see if there is a way in SSH2 to get the file size
301 // return ftp_size($this->link, $file);
302 }
303
304 function touch($file, $time = 0, $atime = 0) {
305 return false;
306 }
307
308 function mkdir($path, $chmod = false, $chown = false, $chgrp = false) {
309 if( !@ssh2_sftp_mkdir($this->link, $path) )
310 return false;
311 if( $chmod )
312 $this->chmod($path, $chmod);
313 if( $chown )
314 $this->chown($path, $chown);
315 if( $chgrp )
316 $this->chgrp($path, $chgrp);
317 return true;
318 }
319
320 function rmdir($path, $recursive = false) {
321 if( ! $recursive )
322 return @ssh2_sftp_rmdir($this->link, $path);
323
324 //TODO: Recursive Directory delete, Have to delete files from the folder first.
325 //$dir = $this->dirlist($path);
326 //foreach($dir as $file)
327
328 }
329
330 function parselisting($line) {
331 $is_windows = ($this->OS_remote == FTP_OS_Windows);
332 if ($is_windows && preg_match("/([0-9]{2})-([0-9]{2})-([0-9]{2}) +([0-9]{2}):([0-9]{2})(AM|PM) +([0-9]+|<DIR>) +(.+)/", $line, $lucifer)) {
333 $b = array();
334 if ($lucifer[3]<70) { $lucifer[3] +=2000; } else { $lucifer[3]+=1900; } // 4digit year fix
335 $b['isdir'] = ($lucifer[7]=="<DIR>");
336 if ( $b['isdir'] )
337 $b['type'] = 'd';
338 else
339 $b['type'] = 'f';
340 $b['size'] = $lucifer[7];
341 $b['month'] = $lucifer[1];
342 $b['day'] = $lucifer[2];
343 $b['year'] = $lucifer[3];
344 $b['hour'] = $lucifer[4];
345 $b['minute'] = $lucifer[5];
346 $b['time'] = @mktime($lucifer[4]+(strcasecmp($lucifer[6],"PM")==0?12:0),$lucifer[5],0,$lucifer[1],$lucifer[2],$lucifer[3]);
347 $b['am/pm'] = $lucifer[6];
348 $b['name'] = $lucifer[8];
349 } else if (!$is_windows && $lucifer=preg_split("/[ ]/",$line,9,PREG_SPLIT_NO_EMPTY)) {
350 //echo $line."\n";
351 $lcount=count($lucifer);
352 if ($lcount<8) return '';
353 $b = array();
354 $b['isdir'] = $lucifer[0]{0} === "d";
355 $b['islink'] = $lucifer[0]{0} === "l";
356 if ( $b['isdir'] )
357 $b['type'] = 'd';
358 elseif ( $b['islink'] )
359 $b['type'] = 'l';
360 else
361 $b['type'] = 'f';
362 $b['perms'] = $lucifer[0];
363 $b['number'] = $lucifer[1];
364 $b['owner'] = $lucifer[2];
365 $b['group'] = $lucifer[3];
366 $b['size'] = $lucifer[4];
367 if ($lcount==8) {
368 sscanf($lucifer[5],"%d-%d-%d",$b['year'],$b['month'],$b['day']);
369 sscanf($lucifer[6],"%d:%d",$b['hour'],$b['minute']);
370 $b['time'] = @mktime($b['hour'],$b['minute'],0,$b['month'],$b['day'],$b['year']);
371 $b['name'] = $lucifer[7];
372 } else {
373 $b['month'] = $lucifer[5];
374 $b['day'] = $lucifer[6];
375 if (preg_match("/([0-9]{2}):([0-9]{2})/",$lucifer[7],$l2)) {
376 $b['year'] = date("Y");
377 $b['hour'] = $l2[1];
378 $b['minute'] = $l2[2];
379 } else {
380 $b['year'] = $lucifer[7];
381 $b['hour'] = 0;
382 $b['minute'] = 0;
383 }
384 $b['time'] = strtotime(sprintf("%d %s %d %02d:%02d",$b['day'],$b['month'],$b['year'],$b['hour'],$b['minute']));
385 $b['name'] = $lucifer[8];
386 }
387 }
388
389 return $b;
390 }
391
392 function dirlist($path = '.', $incdot = false, $recursive = false) {
393 if( $this->is_file($path) ) {
394 $limitFile = basename($path);
395 $path = dirname($path) . '/';
396 } else {
397 $limitFile = false;
398 }
399
400 $list = $this->run_command($this->link, sprintf('ls -a %s', $path));
401
402 if ( $list === false )
403 return false;
404
405 $dirlist = array();
406 foreach ( $list as $k => $v ) {
407 $entry = $this->parselisting($v);
408 if ( empty($entry) )
409 continue;
410
411 if ( '.' == $entry["name"] || '..' == $entry["name"] )
412 continue;
413
414 $dirlist[ $entry['name'] ] = $entry;
415 }
416
417 if ( ! $dirlist )
418 return false;
419 if ( empty($dirlist) )
420 return array();
421
422 $ret = array();
423 foreach ( $dirlist as $struc ) {
424
425 if ( 'd' == $struc['type'] ) {
426 $struc['files'] = array();
427
428 if ( $incdot ){
429 //We're including the doted starts
430 if( '.' != $struc['name'] && '..' != $struc['name'] ){ //Ok, It isnt a special folder
431 if ($recursive)
432 $struc['files'] = $this->dirlist($path . '/' . $struc['name'], $incdot, $recursive);
433 }
434 } else { //No dots
435 if ($recursive)
436 $struc['files'] = $this->dirlist($path . '/' . $struc['name'], $incdot, $recursive);
437 }
438 }
439 //File
440 $ret[$struc['name']] = $struc;
441 }
442 return $ret;
443 }
444
445 function __destruct(){
446 if( $this->link )
447 unset($this->link);
448 }
449}
450
451?>