Wordpress 2.5.1
[autoinstalls/wordpress.git] / wp-admin / includes / class-wp-filesystem-ftpext.php
1 <?php
2 class WP_Filesystem_FTPext{
3         var $link;
4         var $timeout = 5;
5         var $errors = array();
6         var $options = array();
7
8         var $wp_base = '';
9         var $permission = null;
10
11         var $filetypes = array(
12                                                         'php'=>FTP_ASCII,
13                                                         'css'=>FTP_ASCII,
14                                                         'txt'=>FTP_ASCII,
15                                                         'js'=>FTP_ASCII,
16                                                         'html'=>FTP_ASCII,
17                                                         'htm'=>FTP_ASCII,
18                                                         'xml'=>FTP_ASCII,
19
20                                                         'jpg'=>FTP_BINARY,
21                                                         'png'=>FTP_BINARY,
22                                                         'gif'=>FTP_BINARY,
23                                                         'bmp'=>FTP_BINARY
24                                                         );
25
26         function WP_Filesystem_FTPext($opt='') {
27                 $this->errors = new WP_Error();
28
29                 //Check if possible to use ftp functions.
30                 if ( ! extension_loaded('ftp') ) {
31                         $this->errors->add('no_ftp_ext', __('The ftp PHP extension is not available'));
32                         return false;
33                 }
34
35                 // Set defaults:
36                 if ( empty($opt['port']) )
37                         $this->options['port'] = 21;
38                 else
39                         $this->options['port'] = $opt['port'];
40
41                 if ( empty($opt['hostname']) )
42                         $this->errors->add('empty_hostname', __('FTP hostname is required'));
43                 else
44                         $this->options['hostname'] = $opt['hostname'];
45
46                 if ( isset($opt['base']) && ! empty($opt['base']) )
47                         $this->wp_base = $opt['base'];
48
49                 // Check if the options provided are OK.
50                 if ( empty ($opt['username']) )
51                         $this->errors->add('empty_username', __('FTP username is required'));
52                 else
53                         $this->options['username'] = $opt['username'];
54
55                 if ( empty ($opt['password']) )
56                         $this->errors->add('empty_password', __('FTP password is required'));
57                 else
58                         $this->options['password'] = $opt['password'];
59
60                 $this->options['ssl'] = ( !empty($opt['ssl']) );
61         }
62
63         function connect(){
64                 if ( $this->options['ssl'] && function_exists('ftp_ssl_connect') ) {
65                         $this->link = @ftp_ssl_connect($this->options['hostname'], $this->options['port'],$this->timeout);
66                 } else {
67                         $this->link = @ftp_connect($this->options['hostname'], $this->options['port'],$this->timeout);
68                 }
69
70                 if ( ! $this->link ) {
71                         $this->errors->add('connect', sprintf(__('Failed to connect to FTP Server %1$s:%2$s'), $this->options['hostname'], $this->options['port']));
72                         return false;
73                 }
74
75                 if ( ! @ftp_login($this->link,$this->options['username'], $this->options['password']) ) {
76                         $this->errors->add('auth', sprintf(__('Username/Password incorrect for %s'), $this->options['username']));
77                         return false;
78                 }
79
80                 return true;
81         }
82
83         function setDefaultPermissions($perm){
84                 $this->permission = $perm;
85         }
86
87         function find_base_dir($base = '.',$echo = false, $loop = false) {
88                 //Sanitize the Windows path formats, This allows easier conparison and aligns it to FTP output.
89                 $abspath = str_replace('\\','/',ABSPATH); //windows: Straighten up the paths..
90                 if( strpos($abspath, ':') ){ //Windows, Strip out the driveletter
91                         if( preg_match("|.{1}\:(.+)|i", $abspath, $mat) )
92                                 $abspath = $mat[1];
93                 }
94         
95                 //Set up the base directory (Which unless specified, is the current one)
96                 if( empty( $base ) || '.' == $base ) $base = $this->cwd();
97                 $base = trailingslashit($base);
98
99                 //Can we see the Current directory as part of the ABSPATH?
100                 $location = strpos($abspath, $base);
101                 if( false !== $location ) {
102                         $newbase = path_join($base, substr($abspath, $location + strlen($base)));
103
104                         if( false !== $this->chdir($newbase) ){ //chdir sometimes returns null under certain circumstances, even when its changed correctly, FALSE will be returned if it doesnt change correctly.
105                                 if($echo) printf( __('Changing to %s') . '<br/>', $newbase );
106                                 //Check to see if it exists in that folder.
107                                 if( $this->exists($newbase . 'wp-settings.php') ){
108                                         if($echo) printf( __('Found %s'),  $newbase . 'wp-settings.php<br/>' );
109                                         return $newbase;
110                                 }       
111                         }
112                 }
113         
114                 //Ok, Couldnt do a magic location from that particular folder level
115                 
116                 //Get a list of the files in the current directory, See if we can locate where we are in the folder stucture.
117                 $files = $this->dirlist($base);
118                 
119                 $arrPath = explode('/', $abspath);
120                 foreach($arrPath as $key){
121                         //Working from /home/ to /user/ to /wordpress/ see if that file exists within the current folder, 
122                         // If its found, change into it and follow through looking for it. 
123                         // If it cant find WordPress down that route, it'll continue onto the next folder level, and see if that matches, and so on.
124                         // If it reaches the end, and still cant find it, it'll return false for the entire function.
125                         if( isset($files[ $key ]) ){
126                                 //Lets try that folder:
127                                 $folder = path_join($base, $key);
128                                 if($echo) printf( __('Changing to %s') . '<br/>', $folder );
129                                 $ret = $this->find_base_dir( $folder, $echo, $loop);
130                                 if( $ret )
131                                         return $ret;
132                         }
133                 }
134                 //Only check this as a last resort, to prevent locating the incorrect install. All above proceeedures will fail quickly if this is the right branch to take.
135                 if(isset( $files[ 'wp-settings.php' ]) ){
136                         if($echo) printf( __('Found %s'),  $base . 'wp-settings.php<br/>' );
137                         return $base;
138                 }
139                 if( $loop )
140                         return false;//Prevent tihs function looping again.
141                 //As an extra last resort, Change back to / if the folder wasnt found. This comes into effect when the CWD is /home/user/ but WP is at /var/www/.... mainly dedicated setups.
142                 return $this->find_base_dir('/', $echo, true); 
143         }
144
145         function get_base_dir($base = '.', $echo = false){
146                 if( defined('FTP_BASE') )
147                         $this->wp_base = FTP_BASE;
148                 if( empty($this->wp_base) )
149                         $this->wp_base = $this->find_base_dir($base,$echo);
150                 return $this->wp_base;
151         }
152         function get_contents($file,$type='',$resumepos=0){
153                 if( empty($type) ){
154                         $extension = substr(strrchr($file, "."), 1);
155                         $type = isset($this->filetypes[ $extension ]) ? $this->filetypes[ $extension ] : FTP_ASCII;
156                 }
157                 $temp = tmpfile();
158                 if ( ! $temp )
159                         return false;
160                 if( ! @ftp_fget($this->link,$temp,$file,$type,$resumepos) )
161                         return false;
162                 fseek($temp, 0); //Skip back to the start of the file being written to
163                 $contents = '';
164                 while (!feof($temp)) {
165                         $contents .= fread($temp, 8192);
166                 }
167                 fclose($temp);
168                 return $contents;
169         }
170         function get_contents_array($file){
171                 return explode("\n",$this->get_contents($file));
172         }
173         function put_contents($file,$contents,$type=''){
174                 if( empty($type) ){
175                         $extension = substr(strrchr($file, "."), 1);
176                         $type = isset($this->filetypes[ $extension ]) ? $this->filetypes[ $extension ] : FTP_ASCII;
177                 }
178                 $temp = tmpfile();
179                 if ( ! $temp )
180                         return false;
181                 fwrite($temp,$contents);
182                 fseek($temp, 0); //Skip back to the start of the file being written to
183                 $ret = @ftp_fput($this->link,$file,$temp,$type);
184                 fclose($temp);
185                 return $ret;
186         }
187         function cwd(){
188                 $cwd = ftp_pwd($this->link);
189                 if( $cwd )
190                         $cwd = trailingslashit($cwd);
191                 return $cwd;
192         }
193         function chdir($dir){
194                 return @ftp_chdir($dir);
195         }
196         function chgrp($file,$group,$recursive=false){
197                 return false;
198         }
199         function chmod($file,$mode=false,$recursive=false){
200                 if( ! $mode )
201                         $mode = $this->permission;
202                 if( ! $mode )
203                         return false;
204                 if ( ! $this->exists($file) )
205                         return false;
206                 if ( ! $recursive || ! $this->is_dir($file) ){
207                         if (!function_exists('ftp_chmod'))
208                                 return @ftp_site($this->link, sprintf('CHMOD %o %s', $mode, $file));
209                         return @ftp_chmod($this->link,$mode,$file);
210                 }
211                 //Is a directory, and we want recursive
212                 $filelist = $this->dirlist($file);
213                 foreach($filelist as $filename){
214                         $this->chmod($file.'/'.$filename,$mode,$recursive);
215                 }
216                 return true;
217         }
218         function chown($file,$owner,$recursive=false){
219                 return false;
220         }
221         function owner($file){
222                 $dir = $this->dirlist($file);
223                 return $dir[$file]['owner'];
224         }
225         function getchmod($file){
226                 $dir = $this->dirlist($file);
227                 return $dir[$file]['permsn'];
228         }
229         function gethchmod($file){
230                 //From the PHP.net page for ...?
231                 $perms = $this->getchmod($file);
232                 if (($perms & 0xC000) == 0xC000) {
233                         // Socket
234                         $info = 's';
235                 } elseif (($perms & 0xA000) == 0xA000) {
236                         // Symbolic Link
237                         $info = 'l';
238                 } elseif (($perms & 0x8000) == 0x8000) {
239                         // Regular
240                         $info = '-';
241                 } elseif (($perms & 0x6000) == 0x6000) {
242                         // Block special
243                         $info = 'b';
244                 } elseif (($perms & 0x4000) == 0x4000) {
245                         // Directory
246                         $info = 'd';
247                 } elseif (($perms & 0x2000) == 0x2000) {
248                         // Character special
249                         $info = 'c';
250                 } elseif (($perms & 0x1000) == 0x1000) {
251                         // FIFO pipe
252                         $info = 'p';
253                 } else {
254                         // Unknown
255                         $info = 'u';
256                 }
257
258                 // Owner
259                 $info .= (($perms & 0x0100) ? 'r' : '-');
260                 $info .= (($perms & 0x0080) ? 'w' : '-');
261                 $info .= (($perms & 0x0040) ?
262                                         (($perms & 0x0800) ? 's' : 'x' ) :
263                                         (($perms & 0x0800) ? 'S' : '-'));
264
265                 // Group
266                 $info .= (($perms & 0x0020) ? 'r' : '-');
267                 $info .= (($perms & 0x0010) ? 'w' : '-');
268                 $info .= (($perms & 0x0008) ?
269                                         (($perms & 0x0400) ? 's' : 'x' ) :
270                                         (($perms & 0x0400) ? 'S' : '-'));
271
272                 // World
273                 $info .= (($perms & 0x0004) ? 'r' : '-');
274                 $info .= (($perms & 0x0002) ? 'w' : '-');
275                 $info .= (($perms & 0x0001) ?
276                                         (($perms & 0x0200) ? 't' : 'x' ) :
277                                         (($perms & 0x0200) ? 'T' : '-'));
278                 return $info;
279         }
280         function getnumchmodfromh($mode) {
281                 $realmode = "";
282                 $legal =  array("","w","r","x","-");
283                 $attarray = preg_split("//",$mode);
284                 for($i=0;$i<count($attarray);$i++){
285                    if($key = array_search($attarray[$i],$legal)){
286                            $realmode .= $legal[$key];
287                    }
288                 }
289                 $mode = str_pad($realmode,9,'-');
290                 $trans = array('-'=>'0','r'=>'4','w'=>'2','x'=>'1');
291                 $mode = strtr($mode,$trans);
292                 $newmode = '';
293                 $newmode .= $mode[0]+$mode[1]+$mode[2];
294                 $newmode .= $mode[3]+$mode[4]+$mode[5];
295                 $newmode .= $mode[6]+$mode[7]+$mode[8];
296                 return $newmode;
297         }
298         function group($file){
299                 $dir = $this->dirlist($file);
300                 return $dir[$file]['group'];
301         }
302         function copy($source,$destination,$overwrite=false){
303                 if( ! $overwrite && $this->exists($destination) )
304                         return false;
305                 $content = $this->get_contents($source);
306                 if( false === $content)
307                         return false;
308                 return $this->put_contents($destination,$content);
309         }
310         function move($source,$destination,$overwrite=false){
311                 return ftp_rename($this->link,$source,$destination);
312         }
313
314         function delete($file,$recursive=false) {
315                 if ( $this->is_file($file) )
316                         return @ftp_delete($this->link,$file);
317                 if ( !$recursive )
318                         return @ftp_rmdir($this->link,$file);
319                 $filelist = $this->dirlist($file);
320                 foreach ((array) $filelist as $filename => $fileinfo) {
321                         $this->delete($file.'/'.$filename,$recursive);
322                 }
323                 return @ftp_rmdir($this->link,$file);
324         }
325
326         function exists($file){
327                 $list = ftp_rawlist($this->link,$file,false);
328                 if( ! $list )
329                         return false;
330                 return count($list) == 1 ? true : false;
331         }
332         function is_file($file){
333                 return $this->is_dir($file) ? false : true;
334         }
335         function is_dir($path){
336                 $cwd = $this->cwd();
337                 $result = @ftp_chdir($this->link, $path);
338                 if( $result && $path == $this->cwd() ||
339                         $this->cwd() != $cwd ) {
340                         @ftp_chdir($this->link, $cwd);
341                         return true;
342                 }
343                 return false;
344         }
345         function is_readable($file){
346                 //Get dir list, Check if the file is writable by the current user??
347                 return true;
348         }
349         function is_writable($file){
350                 //Get dir list, Check if the file is writable by the current user??
351                 return true;
352         }
353         function atime($file){
354                 return false;
355         }
356         function mtime($file){
357                 return ftp_mdtm($this->link, $file);
358         }
359         function size($file){
360                 return ftp_size($this->link, $file);
361         }
362         function touch($file,$time=0,$atime=0){
363                 return false;
364         }
365         function mkdir($path,$chmod=false,$chown=false,$chgrp=false){
366                 if( !@ftp_mkdir($this->link, $path) )
367                         return false;
368                 if( $chmod )
369                         $this->chmod($path, $chmod);
370                 if( $chown )
371                         $this->chown($path, $chown);
372                 if( $chgrp )
373                         $this->chgrp($path, $chgrp);
374                 return true;
375         }
376         function rmdir($path,$recursive=false){
377                 if( ! $recursive )
378                         return @ftp_rmdir($this->link, $path);
379
380                 //TODO: Recursive Directory delete, Have to delete files from the folder first.
381                 //$dir = $this->dirlist($path);
382                 //foreach($dir as $file)
383
384         }
385
386         function parselisting($line) {
387                 $is_windows = ($this->OS_remote == FTP_OS_Windows);
388                 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)) {
389                         $b = array();
390                         if ($lucifer[3]<70) { $lucifer[3]+=2000; } else { $lucifer[3]+=1900; } // 4digit year fix
391                         $b['isdir'] = ($lucifer[7]=="<DIR>");
392                         if ( $b['isdir'] )
393                                 $b['type'] = 'd';
394                         else
395                                 $b['type'] = 'f';
396                         $b['size'] = $lucifer[7];
397                         $b['month'] = $lucifer[1];
398                         $b['day'] = $lucifer[2];
399                         $b['year'] = $lucifer[3];
400                         $b['hour'] = $lucifer[4];
401                         $b['minute'] = $lucifer[5];
402                         $b['time'] = @mktime($lucifer[4]+(strcasecmp($lucifer[6],"PM")==0?12:0),$lucifer[5],0,$lucifer[1],$lucifer[2],$lucifer[3]);
403                         $b['am/pm'] = $lucifer[6];
404                         $b['name'] = $lucifer[8];
405                 } else if (!$is_windows && $lucifer=preg_split("/[ ]/",$line,9,PREG_SPLIT_NO_EMPTY)) {
406                         //echo $line."\n";
407                         $lcount=count($lucifer);
408                         if ($lcount<8) return '';
409                         $b = array();
410                         $b['isdir'] = $lucifer[0]{0} === "d";
411                         $b['islink'] = $lucifer[0]{0} === "l";
412                         if ( $b['isdir'] )
413                                 $b['type'] = 'd';
414                         elseif ( $b['islink'] )
415                                 $b['type'] = 'l';
416                         else
417                                 $b['type'] = 'f';
418                         $b['perms'] = $lucifer[0];
419                         $b['number'] = $lucifer[1];
420                         $b['owner'] = $lucifer[2];
421                         $b['group'] = $lucifer[3];
422                         $b['size'] = $lucifer[4];
423                         if ($lcount==8) {
424                                 sscanf($lucifer[5],"%d-%d-%d",$b['year'],$b['month'],$b['day']);
425                                 sscanf($lucifer[6],"%d:%d",$b['hour'],$b['minute']);
426                                 $b['time'] = @mktime($b['hour'],$b['minute'],0,$b['month'],$b['day'],$b['year']);
427                                 $b['name'] = $lucifer[7];
428                         } else {
429                                 $b['month'] = $lucifer[5];
430                                 $b['day'] = $lucifer[6];
431                                 if (preg_match("/([0-9]{2}):([0-9]{2})/",$lucifer[7],$l2)) {
432                                         $b['year'] = date("Y");
433                                         $b['hour'] = $l2[1];
434                                         $b['minute'] = $l2[2];
435                                 } else {
436                                         $b['year'] = $lucifer[7];
437                                         $b['hour'] = 0;
438                                         $b['minute'] = 0;
439                                 }
440                                 $b['time'] = strtotime(sprintf("%d %s %d %02d:%02d",$b['day'],$b['month'],$b['year'],$b['hour'],$b['minute']));
441                                 $b['name'] = $lucifer[8];
442                         }
443                 }
444
445                 return $b;
446         }
447
448         function dirlist($path='.',$incdot=false,$recursive=false){
449                 if( $this->is_file($path) ){
450                         $limitFile = basename($path);
451                         $path = dirname($path) . '/';
452                 } else {
453                         $limitFile = false;
454                 }
455
456                 $list = @ftp_rawlist($this->link , '-a ' . $path, false);
457
458                 if ( $list === false )
459                         return false;
460
461                 $dirlist = array();
462                 foreach ( $list as $k => $v ) {
463                         $entry = $this->parselisting($v);
464                         if ( empty($entry) )
465                                 continue;
466
467                         if ( $entry["name"]=="." or $entry["name"]==".." )
468                                 continue;
469
470                         $dirlist[$entry['name']] = $entry;
471                 }
472
473                 if ( ! $dirlist )
474                         return false;
475                 if ( empty($dirlist) )
476                         return array();
477
478                 $ret = array();
479                 foreach ( $dirlist as $struc ) {
480
481                         if ( 'd' == $struc['type'] ) {
482                                 $struc['files'] = array();
483
484                                 if ( $incdot ){
485                                         //We're including the doted starts
486                                         if( '.' != $struc['name'] && '..' != $struc['name'] ){ //Ok, It isnt a special folder
487                                                 if ($recursive)
488                                                         $struc['files'] = $this->dirlist($path.'/'.$struc['name'],$incdot,$recursive);
489                                         }
490                                 } else { //No dots
491                                         if ($recursive)
492                                                 $struc['files'] = $this->dirlist($path.'/'.$struc['name'],$incdot,$recursive);
493                                 }
494                         }
495                         //File
496                         $ret[$struc['name']] = $struc;
497                 }
498                 return $ret;
499         }
500
501         function __destruct(){
502                 if( $this->link )
503                         ftp_close($this->link);
504         }
505 }
506
507 ?>