WordPress.org

Make WordPress Core

Ticket #41560: 41560.2.patch

File 41560.2.patch, 25.3 KB (added by ayeshrajans, 4 years ago)
  • src/wp-admin/includes/class-wp-filesystem-ftpext.php

    diff --git src/wp-admin/includes/class-wp-filesystem-ftpext.php src/wp-admin/includes/class-wp-filesystem-ftpext.php
    index 52e4357394..1d12a889e4 100644
     
    1414 * @see WP_Filesystem_Base
    1515 */
    1616class WP_Filesystem_FTPext extends WP_Filesystem_Base {
     17
    1718        public $link;
    1819
     20
    1921        /**
     22         * WP_Filesystem_FTPext constructor.
    2023         *
    21          * @param array $opt
     24         * @param array|string $opt
    2225         */
    2326        public function __construct( $opt = '' ) {
    2427                $this->method = 'ftpext';
    2528                $this->errors = new WP_Error();
    2629
    2730                // Check if possible to use ftp functions.
    28                 if ( ! extension_loaded('ftp') ) {
    29                         $this->errors->add('no_ftp_ext', __('The ftp PHP extension is not available'));
     31                if ( ! extension_loaded( 'ftp' ) ) {
     32                        $this->errors->add( 'no_ftp_ext', __( 'The ftp PHP extension is not available' ) );
     33
    3034                        return;
    3135                }
    3236
    3337                // This Class uses the timeout on a per-connection basis, Others use it on a per-action basis.
    3438
    35                 if ( ! defined('FS_TIMEOUT') )
    36                         define('FS_TIMEOUT', 240);
     39                if ( ! defined( 'FS_TIMEOUT' ) ) {
     40                        define( 'FS_TIMEOUT', 240 );
     41                }
    3742
    38                 if ( empty($opt['port']) )
     43                if ( empty( $opt['port'] ) ) {
    3944                        $this->options['port'] = 21;
    40                 else
     45                } else {
    4146                        $this->options['port'] = $opt['port'];
     47                }
    4248
    43                 if ( empty($opt['hostname']) )
    44                         $this->errors->add('empty_hostname', __('FTP hostname is required'));
    45                 else
     49                if ( empty( $opt['hostname'] ) ) {
     50                        $this->errors->add( 'empty_hostname', __( 'FTP hostname is required' ) );
     51                } else {
    4652                        $this->options['hostname'] = $opt['hostname'];
     53                }
    4754
    4855                // Check if the options provided are OK.
    49                 if ( empty($opt['username']) )
    50                         $this->errors->add('empty_username', __('FTP username is required'));
    51                 else
     56                if ( empty( $opt['username'] ) ) {
     57                        $this->errors->add( 'empty_username', __( 'FTP username is required' ) );
     58                } else {
    5259                        $this->options['username'] = $opt['username'];
     60                }
    5361
    54                 if ( empty($opt['password']) )
    55                         $this->errors->add('empty_password', __('FTP password is required'));
    56                 else
     62                if ( empty( $opt['password'] ) ) {
     63                        $this->errors->add( 'empty_password', __( 'FTP password is required' ) );
     64                } else {
    5765                        $this->options['password'] = $opt['password'];
     66                }
    5867
    59                 $this->options['ssl'] = false;
    60                 if ( isset($opt['connection_type']) && 'ftps' == $opt['connection_type'] )
    61                         $this->options['ssl'] = true;
     68                $this->options['ssl'] = FALSE;
     69                if ( isset( $opt['connection_type'] ) && 'ftps' == $opt['connection_type'] ) {
     70                        $this->options['ssl'] = TRUE;
     71                }
    6272        }
    6373
    6474        /**
    public function __construct( $opt = '' ) { 
    6676         * @return bool
    6777         */
    6878        public function connect() {
    69                 if ( isset($this->options['ssl']) && $this->options['ssl'] && function_exists('ftp_ssl_connect') )
    70                         $this->link = @ftp_ssl_connect($this->options['hostname'], $this->options['port'], FS_CONNECT_TIMEOUT);
    71                 else
    72                         $this->link = @ftp_connect($this->options['hostname'], $this->options['port'], FS_CONNECT_TIMEOUT);
     79                if ( isset( $this->options['ssl'] ) && $this->options['ssl'] && function_exists( 'ftp_ssl_connect' ) ) {
     80                        $this->link = @ftp_ssl_connect( $this->options['hostname'], $this->options['port'], FS_CONNECT_TIMEOUT );
     81                } else {
     82                        $this->link = @ftp_connect( $this->options['hostname'], $this->options['port'], FS_CONNECT_TIMEOUT );
     83                }
    7384
    7485                if ( ! $this->link ) {
    7586                        $this->errors->add( 'connect',
    public function connect() { 
    7889                                        $this->options['hostname'] . ':' . $this->options['port']
    7990                                )
    8091                        );
    81                         return false;
     92
     93                        return FALSE;
    8294                }
    8395
    84                 if ( ! @ftp_login( $this->link,$this->options['username'], $this->options['password'] ) ) {
     96                if ( ! @ftp_login( $this->link, $this->options['username'], $this->options['password'] ) ) {
    8597                        $this->errors->add( 'auth',
    8698                                /* translators: %s: username */
    8799                                sprintf( __( 'Username/Password incorrect for %s' ),
    88100                                        $this->options['username']
    89101                                )
    90102                        );
    91                         return false;
     103
     104                        return FALSE;
    92105                }
    93106
    94107                // Set the Connection to use Passive FTP
    95                 @ftp_pasv( $this->link, true );
    96                 if ( @ftp_get_option($this->link, FTP_TIMEOUT_SEC) < FS_TIMEOUT )
    97                         @ftp_set_option($this->link, FTP_TIMEOUT_SEC, FS_TIMEOUT);
     108                @ftp_pasv( $this->link, TRUE );
     109                if ( @ftp_get_option( $this->link, FTP_TIMEOUT_SEC ) < FS_TIMEOUT ) {
     110                        @ftp_set_option( $this->link, FTP_TIMEOUT_SEC, FS_TIMEOUT );
     111                }
    98112
    99                 return true;
     113                return TRUE;
     114        }
     115
     116        /**
     117         *
     118         * @param string $file
     119         *
     120         * @return array
     121         */
     122        public function get_contents_array( $file ) {
     123                return explode( "\n", $this->get_contents( $file ) );
    100124        }
    101125
    102126        /**
    public function connect() { 
    105129         * @since 2.5.0
    106130         *
    107131         * @param string $file Filename.
    108          * @return string|false File contents on success, false if no temp file could be opened,
    109          *                      or if the file couldn't be retrieved.
     132         *
     133         * @return string|false File contents on success, false if no temp file
     134         *     could be opened, or if the file couldn't be retrieved.
    110135         */
    111136        public function get_contents( $file ) {
    112                 $tempfile = wp_tempnam($file);
    113                 $temp = fopen($tempfile, 'w+');
     137                $tempfile = wp_tempnam( $file );
     138                $temp     = fopen( $tempfile, 'w+' );
    114139
    115140                if ( ! $temp ) {
    116141                        unlink( $tempfile );
    117                         return false;
     142
     143                        return FALSE;
    118144                }
    119145
    120146                if ( ! @ftp_fget( $this->link, $temp, $file, FTP_BINARY ) ) {
    121147                        fclose( $temp );
    122148                        unlink( $tempfile );
    123                         return false;
     149
     150                        return FALSE;
    124151                }
    125152
    126153                fseek( $temp, 0 ); // Skip back to the start of the file being written to
    127154                $contents = '';
    128155
    129                 while ( ! feof($temp) )
    130                         $contents .= fread($temp, 8192);
     156                while ( ! feof( $temp ) ) {
     157                        $contents .= fread( $temp, 8192 );
     158                }
     159
     160                fclose( $temp );
     161                unlink( $tempfile );
    131162
    132                 fclose($temp);
    133                 unlink($tempfile);
    134163                return $contents;
    135164        }
    136165
    137166        /**
    138167         *
    139          * @param string $file
    140          * @return array
     168         * @param string $dir
     169         *
     170         * @return bool
    141171         */
    142         public function get_contents_array($file) {
    143                 return explode("\n", $this->get_contents($file));
     172        public function chdir( $dir ) {
     173                return @ftp_chdir( $this->link, $dir );
    144174        }
    145175
    146176        /**
    147177         *
    148178         * @param string $file
    149          * @param string $contents
    150          * @param bool|int $mode
    151          * @return bool
     179         *
     180         * @return string
    152181         */
    153         public function put_contents($file, $contents, $mode = false ) {
    154                 $tempfile = wp_tempnam($file);
    155                 $temp = fopen( $tempfile, 'wb+' );
     182        public function owner( $file ) {
     183                $dir = $this->dirlist( $file );
    156184
    157                 if ( ! $temp ) {
    158                         unlink( $tempfile );
    159                         return false;
    160                 }
     185                return $dir[ $file ]['owner'];
     186        }
    161187
    162                 mbstring_binary_safe_encoding();
     188        /**
     189         *
     190         * @param string $path
     191         * @param bool $include_hidden
     192         * @param bool $recursive
     193         *
     194         * @return bool|array
     195         */
     196        public function dirlist( $path = '.', $include_hidden = TRUE, $recursive = FALSE ) {
     197                if ( $this->is_file( $path ) ) {
     198                        $limit_file = basename( $path );
     199                        $path       = dirname( $path ) . '/';
     200                } else {
     201                        $limit_file = FALSE;
     202                }
    163203
    164                 $data_length = strlen( $contents );
    165                 $bytes_written = fwrite( $temp, $contents );
     204                $pwd = @ftp_pwd( $this->link );
     205                if ( ! @ftp_chdir( $this->link, $path ) ) { // Cant change to folder = folder doesn't exist
     206                        return FALSE;
     207                }
    166208
    167                 reset_mbstring_encoding();
     209                $list = @ftp_rawlist( $this->link, '-a', FALSE );
     210                @ftp_chdir( $this->link, $pwd );
    168211
    169                 if ( $data_length !== $bytes_written ) {
    170                         fclose( $temp );
    171                         unlink( $tempfile );
    172                         return false;
     212                if ( empty( $list ) ) { // Empty array = non-existent folder (real folder will show . at least)
     213                        return FALSE;
    173214                }
    174215
    175                 fseek( $temp, 0 ); // Skip back to the start of the file being written to
     216                $dirlist = [];
     217                foreach ( $list as $k => $v ) {
     218                        $entry = $this->parselisting( $v );
     219                        if ( empty( $entry ) ) {
     220                                continue;
     221                        }
    176222
    177                 $ret = @ftp_fput( $this->link, $file, $temp, FTP_BINARY );
     223                        if ( '.' == $entry['name'] || '..' == $entry['name'] ) {
     224                                continue;
     225                        }
    178226
    179                 fclose($temp);
    180                 unlink($tempfile);
    181227
    182                 $this->chmod($file, $mode);
     228                        if ( ! $include_hidden && '.' == $entry['name'][0] ) {
     229                                continue;
     230                        }
     231
     232                        if ( $limit_file && $entry['name'] != $limit_file ) {
     233                                continue;
     234                        }
     235
     236                        $dirlist[ $entry['name'] ] = $entry;
     237                }
     238
     239                $ret = [];
     240                foreach ( (array) $dirlist as $struc ) {
     241                        if ( 'd' == $struc['type'] ) {
     242                                if ( $recursive ) {
     243                                        $struc['files'] = $this->dirlist( $path . '/' . $struc['name'], $include_hidden, $recursive );
     244                                } else {
     245                                        $struc['files'] = [];
     246                                }
     247                        }
     248
     249                        $ret[ $struc['name'] ] = $struc;
     250                }
    183251
    184252                return $ret;
    185253        }
    186254
    187255        /**
    188256         *
    189          * @return string
     257         * @param string $file
     258         *
     259         * @return bool
    190260         */
    191         public function cwd() {
    192                 $cwd = @ftp_pwd($this->link);
    193                 if ( $cwd )
    194                         $cwd = trailingslashit($cwd);
    195                 return $cwd;
     261        public function is_file( $file ) {
     262                return $this->exists( $file ) && ! $this->is_dir( $file );
    196263        }
    197264
    198265        /**
    199266         *
    200          * @param string $dir
     267         * @param string $file
     268         *
    201269         * @return bool
    202270         */
    203         public function chdir($dir) {
    204                 return @ftp_chdir($this->link, $dir);
     271        public function exists( $file ) {
     272                $list = @ftp_nlist( $this->link, $file );
     273
     274                if ( empty( $list ) && $this->is_dir( $file ) ) {
     275                        return TRUE; // File is an empty directory.
     276                }
     277
     278                return ! empty( $list ); //empty list = no file, so invert.
    205279        }
    206280
    207281        /**
    208282         *
    209          * @param string $file
    210          * @param int $mode
    211          * @param bool $recursive
     283         * @param string $path
     284         *
    212285         * @return bool
    213286         */
    214         public function chmod($file, $mode = false, $recursive = false) {
    215                 if ( ! $mode ) {
    216                         if ( $this->is_file($file) )
    217                                 $mode = FS_CHMOD_FILE;
    218                         elseif ( $this->is_dir($file) )
    219                                 $mode = FS_CHMOD_DIR;
    220                         else
    221                                 return false;
    222                 }
     287        public function is_dir( $path ) {
     288                $cwd    = $this->cwd();
     289                $result = @ftp_chdir( $this->link, trailingslashit( $path ) );
     290                if ( $result && $path == $this->cwd() || $this->cwd() != $cwd ) {
     291                        @ftp_chdir( $this->link, $cwd );
    223292
    224                 // chmod any sub-objects if recursive.
    225                 if ( $recursive && $this->is_dir($file) ) {
    226                         $filelist = $this->dirlist($file);
    227                         foreach ( (array)$filelist as $filename => $filemeta )
    228                                 $this->chmod($file . '/' . $filename, $mode, $recursive);
     293                        return TRUE;
    229294                }
    230295
    231                 // chmod the file or directory
    232                 if ( ! function_exists('ftp_chmod') )
    233                         return (bool)@ftp_site($this->link, sprintf('CHMOD %o %s', $mode, $file));
    234                 return (bool)@ftp_chmod($this->link, $mode, $file);
     296                return FALSE;
    235297        }
    236298
    237299        /**
    238300         *
    239          * @param string $file
    240301         * @return string
    241302         */
    242         public function owner($file) {
    243                 $dir = $this->dirlist($file);
    244                 return $dir[$file]['owner'];
     303        public function cwd() {
     304                $cwd = @ftp_pwd( $this->link );
     305                if ( $cwd ) {
     306                        $cwd = trailingslashit( $cwd );
     307                }
     308
     309                return $cwd;
    245310        }
     311
    246312        /**
    247313         *
    248          * @param string $file
    249          * @return string
     314         * @var static bool|null $is_windows
     315         *
     316         * @param string $line
     317         *
     318         * @return array|string
    250319         */
    251         public function getchmod($file) {
    252                 $dir = $this->dirlist($file);
    253                 return $dir[$file]['permsn'];
     320        public function parselisting( $line ) {
     321                static $is_windows = NULL;
     322                if ( is_null( $is_windows ) ) {
     323                        $is_windows = stripos( ftp_systype( $this->link ), 'win' ) !== FALSE;
     324                }
     325
     326                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 ) ) {
     327                        $b = [];
     328                        if ( $lucifer[3] < 70 ) {
     329                                $lucifer[3] += 2000;
     330                        } else {
     331                                $lucifer[3] += 1900; // 4digit year fix
     332                        }
     333
     334                        $b['isdir']  = ( $lucifer[7] == '<DIR>' );
     335                        $b['type']   = $b['isdir'] ? 'd' : 'f';
     336                        $b['size']   = $lucifer[7];
     337                        $b['month']  = $lucifer[1];
     338                        $b['day']    = $lucifer[2];
     339                        $b['year']   = $lucifer[3];
     340                        $b['hour']   = $lucifer[4];
     341                        $b['minute'] = $lucifer[5];
     342                        $b['time']   = @mktime( $lucifer[4] + ( strcasecmp( $lucifer[6], "PM" ) == 0 ? 12 : 0 ), $lucifer[5], 0, $lucifer[1], $lucifer[2], $lucifer[3] );
     343                        $b['am/pm']  = $lucifer[6];
     344                        $b['name']   = $lucifer[8];
     345                } elseif ( ! $is_windows && $lucifer = preg_split( '/[ ]/', $line, 9, PREG_SPLIT_NO_EMPTY ) ) {
     346                        //echo $line."\n";
     347                        $lcount = count( $lucifer );
     348                        if ( $lcount < 8 ) {
     349                                return '';
     350                        }
     351
     352                        $b           = [];
     353                        $b['isdir']  = $lucifer[0]{0} === 'd';
     354                        $b['islink'] = $lucifer[0]{0} === 'l';
     355                        $b['type']   = $b['isdir'] ? 'd' : 'l';
     356                        if ( $b['isdir'] ) {
     357                                $b['type'] = 'd';
     358                        } elseif ( $b['islink'] ) {
     359                                $b['type'] = 'l';
     360                        } else {
     361                                $b['type'] = 'f';
     362                        }
     363
     364                        $b['perms']  = $lucifer[0];
     365                        $b['permsn'] = $this->getnumchmodfromh( $b['perms'] );
     366                        $b['number'] = $lucifer[1];
     367                        $b['owner']  = $lucifer[2];
     368                        $b['group']  = $lucifer[3];
     369                        $b['size']   = $lucifer[4];
     370                        if ( $lcount == 8 ) {
     371                                sscanf( $lucifer[5], '%d-%d-%d', $b['year'], $b['month'], $b['day'] );
     372                                sscanf( $lucifer[6], '%d:%d', $b['hour'], $b['minute'] );
     373                                $b['time'] = @mktime( $b['hour'], $b['minute'], 0, $b['month'], $b['day'], $b['year'] );
     374                                $b['name'] = $lucifer[7];
     375                        } else {
     376                                $b['month'] = $lucifer[5];
     377                                $b['day']   = $lucifer[6];
     378                                if ( preg_match( '/([0-9]{2}):([0-9]{2})/', $lucifer[7], $l2 ) ) {
     379                                        $b['year']   = date( "Y" );
     380                                        $b['hour']   = $l2[1];
     381                                        $b['minute'] = $l2[2];
     382                                } else {
     383                                        $b['year']   = $lucifer[7];
     384                                        $b['hour']   = 0;
     385                                        $b['minute'] = 0;
     386                                }
     387                                $b['time'] = strtotime( sprintf( '%d %s %d %02d:%02d', $b['day'], $b['month'], $b['year'], $b['hour'], $b['minute'] ) );
     388                                $b['name'] = $lucifer[8];
     389                        }
     390                }
     391
     392                // Replace symlinks formatted as "source -> target" with just the source name
     393                if ( isset( $b['islink'] ) && $b['islink'] ) {
     394                        $b['name'] = preg_replace( '/(\s*->\s*.*)$/', '', $b['name'] );
     395                }
     396
     397                return $b;
    254398        }
    255399
    256400        /**
    257401         *
    258402         * @param string $file
     403         *
    259404         * @return string
    260405         */
    261         public function group($file) {
    262                 $dir = $this->dirlist($file);
    263                 return $dir[$file]['group'];
     406        public function getchmod( $file ) {
     407                $dir = $this->dirlist( $file );
     408
     409                return $dir[ $file ]['permsn'];
    264410        }
    265411
    266412        /**
    267413         *
    268          * @param string $source
    269          * @param string $destination
    270          * @param bool   $overwrite
    271          * @param string|bool $mode
    272          * @return bool
     414         * @param string $file
     415         *
     416         * @return string
    273417         */
    274         public function copy($source, $destination, $overwrite = false, $mode = false) {
    275                 if ( ! $overwrite && $this->exists($destination) )
    276                         return false;
    277                 $content = $this->get_contents($source);
    278                 if ( false === $content )
    279                         return false;
    280                 return $this->put_contents($destination, $content, $mode);
     418        public function group( $file ) {
     419                $dir = $this->dirlist( $file );
     420
     421                return $dir[ $file ]['group'];
    281422        }
    282423
    283424        /**
    public function copy($source, $destination, $overwrite = false, $mode = false) { 
    285426         * @param string $source
    286427         * @param string $destination
    287428         * @param bool $overwrite
    288          * @return bool
    289          */
    290         public function move($source, $destination, $overwrite = false) {
    291                 return ftp_rename($this->link, $source, $destination);
    292         }
    293 
    294         /**
     429         * @param string|bool $mode
    295430         *
    296          * @param string $file
    297          * @param bool $recursive
    298          * @param string $type
    299431         * @return bool
    300432         */
    301         public function delete($file, $recursive = false, $type = false) {
    302                 if ( empty($file) )
    303                         return false;
    304                 if ( 'f' == $type || $this->is_file($file) )
    305                         return @ftp_delete($this->link, $file);
    306                 if ( !$recursive )
    307                         return @ftp_rmdir($this->link, $file);
    308 
    309                 $filelist = $this->dirlist( trailingslashit($file) );
    310                 if ( !empty($filelist) )
    311                         foreach ( $filelist as $delete_file )
    312                                 $this->delete( trailingslashit($file) . $delete_file['name'], $recursive, $delete_file['type'] );
    313                 return @ftp_rmdir($this->link, $file);
     433        public function copy( $source, $destination, $overwrite = FALSE, $mode = FALSE ) {
     434                if ( ! $overwrite && $this->exists( $destination ) ) {
     435                        return FALSE;
     436                }
     437
     438                $content = $this->get_contents( $source );
     439                if ( FALSE === $content ) {
     440                        return FALSE;
     441                }
     442
     443                return $this->put_contents( $destination, $content, $mode );
    314444        }
    315445
    316446        /**
    317447         *
    318448         * @param string $file
     449         * @param string $contents
     450         * @param bool|int $mode
     451         *
    319452         * @return bool
    320453         */
    321         public function exists($file) {
    322                 $list = @ftp_nlist($this->link, $file);
     454        public function put_contents( $file, $contents, $mode = FALSE ) {
     455                $tempfile = wp_tempnam( $file );
     456                $temp     = fopen( $tempfile, 'wb+' );
    323457
    324                 if ( empty( $list ) && $this->is_dir( $file ) ) {
    325                         return true; // File is an empty directory.
     458                if ( ! $temp ) {
     459                        unlink( $tempfile );
     460
     461                        return FALSE;
    326462                }
    327463
    328                 return !empty($list); //empty list = no file, so invert.
     464                mbstring_binary_safe_encoding();
     465
     466                $data_length   = strlen( $contents );
     467                $bytes_written = fwrite( $temp, $contents );
     468
     469                reset_mbstring_encoding();
     470
     471                if ( $data_length !== $bytes_written ) {
     472                        fclose( $temp );
     473                        unlink( $tempfile );
     474
     475                        return FALSE;
     476                }
     477
     478                fseek( $temp, 0 ); // Skip back to the start of the file being written to
     479
     480                $ret = @ftp_fput( $this->link, $file, $temp, FTP_BINARY );
     481
     482                fclose( $temp );
     483                unlink( $tempfile );
     484
     485                $this->chmod( $file, $mode );
     486
     487                return $ret;
    329488        }
    330489
    331490        /**
    332491         *
    333492         * @param string $file
     493         * @param int|bool $mode
     494         * @param bool $recursive
     495         *
    334496         * @return bool
    335497         */
    336         public function is_file($file) {
    337                 return $this->exists($file) && !$this->is_dir($file);
     498        public function chmod( $file, $mode = FALSE, $recursive = FALSE ) {
     499                if ( ! $mode ) {
     500                        if ( $this->is_file( $file ) ) {
     501                                $mode = FS_CHMOD_FILE;
     502                        } elseif ( $this->is_dir( $file ) ) {
     503                                $mode = FS_CHMOD_DIR;
     504                        } else {
     505                                return FALSE;
     506                        }
     507                }
     508
     509                // chmod any sub-objects if recursive.
     510                if ( $recursive && $this->is_dir( $file ) ) {
     511                        $filelist = $this->dirlist( $file );
     512                        foreach ( (array) $filelist as $filename => $filemeta ) {
     513                                $this->chmod( $file . '/' . $filename, $mode, $recursive );
     514                        }
     515                }
     516
     517                // chmod the file or directory
     518                if ( ! function_exists( 'ftp_chmod' ) ) {
     519                        return (bool) @ftp_site( $this->link, sprintf( 'CHMOD %o %s', $mode, $file ) );
     520                }
     521
     522                return (bool) @ftp_chmod( $this->link, $mode, $file );
    338523        }
    339524
    340525        /**
    341526         *
    342          * @param string $path
     527         * @param string $source
     528         * @param string $destination
     529         * @param bool $overwrite
     530         *
    343531         * @return bool
    344532         */
    345         public function is_dir($path) {
    346                 $cwd = $this->cwd();
    347                 $result = @ftp_chdir($this->link, trailingslashit($path) );
    348                 if ( $result && $path == $this->cwd() || $this->cwd() != $cwd ) {
    349                         @ftp_chdir($this->link, $cwd);
    350                         return true;
    351                 }
    352                 return false;
     533        public function move( $source, $destination, $overwrite = FALSE ) {
     534                return ftp_rename( $this->link, $source, $destination );
    353535        }
    354536
    355537        /**
    356538         *
    357539         * @param string $file
     540         *
    358541         * @return bool
    359542         */
    360         public function is_readable($file) {
    361                 return true;
     543        public function is_readable( $file ) {
     544                return TRUE;
    362545        }
    363546
    364547        /**
    365548         *
    366549         * @param string $file
     550         *
    367551         * @return bool
    368552         */
    369         public function is_writable($file) {
    370                 return true;
     553        public function is_writable( $file ) {
     554                return TRUE;
    371555        }
    372556
    373557        /**
    374558         *
    375559         * @param string $file
     560         *
    376561         * @return bool
    377562         */
    378         public function atime($file) {
    379                 return false;
     563        public function atime( $file ) {
     564                return FALSE;
    380565        }
    381566
    382567        /**
    383568         *
    384569         * @param string $file
     570         *
    385571         * @return int
    386572         */
    387         public function mtime($file) {
    388                 return ftp_mdtm($this->link, $file);
     573        public function mtime( $file ) {
     574                return ftp_mdtm( $this->link, $file );
    389575        }
    390576
    391577        /**
    392578         *
    393579         * @param string $file
     580         *
    394581         * @return int
    395582         */
    396         public function size($file) {
    397                 return ftp_size($this->link, $file);
     583        public function size( $file ) {
     584                return ftp_size( $this->link, $file );
    398585        }
    399586
    400587        /**
    401588         *
    402589         * @param string $file
     590         * @param int $time
     591         * @param int $atime
     592         *
    403593         * @return bool
    404594         */
    405         public function touch($file, $time = 0, $atime = 0) {
    406                 return false;
     595        public function touch( $file, $time = 0, $atime = 0 ) {
     596                return FALSE;
    407597        }
    408598
    409599        /**
    public function touch($file, $time = 0, $atime = 0) { 
    412602         * @param mixed $chmod
    413603         * @param mixed $chown
    414604         * @param mixed $chgrp
     605         *
    415606         * @return bool
    416607         */
    417         public function mkdir($path, $chmod = false, $chown = false, $chgrp = false) {
    418                 $path = untrailingslashit($path);
    419                 if ( empty($path) )
    420                         return false;
    421 
    422                 if ( !@ftp_mkdir($this->link, $path) )
    423                         return false;
    424                 $this->chmod($path, $chmod);
    425                 return true;
     608        public function mkdir( $path, $chmod = FALSE, $chown = FALSE, $chgrp = FALSE ) {
     609                $path = untrailingslashit( $path );
     610                if ( empty( $path ) ) {
     611                        return FALSE;
     612                }
     613
     614
     615                if ( ! @ftp_mkdir( $this->link, $path ) ) {
     616                        return FALSE;
     617                }
     618
     619                $this->chmod( $path, $chmod );
     620
     621                return TRUE;
    426622        }
    427623
    428624        /**
    429625         *
    430626         * @param string $path
    431627         * @param bool $recursive
    432          * @return bool
    433          */
    434         public function rmdir($path, $recursive = false) {
    435                 return $this->delete($path, $recursive);
    436         }
    437 
    438         /**
    439628         *
    440          * @staticvar bool $is_windows
    441          * @param string $line
    442          * @return array
     629         * @return bool
    443630         */
    444         public function parselisting($line) {
    445                 static $is_windows = null;
    446                 if ( is_null($is_windows) )
    447                         $is_windows = stripos( ftp_systype($this->link), 'win') !== false;
    448 
    449                 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) ) {
    450                         $b = array();
    451                         if ( $lucifer[3] < 70 )
    452                                 $lucifer[3] +=2000;
    453                         else
    454                                 $lucifer[3] += 1900; // 4digit year fix
    455                         $b['isdir'] = ( $lucifer[7] == '<DIR>');
    456                         if ( $b['isdir'] )
    457                                 $b['type'] = 'd';
    458                         else
    459                                 $b['type'] = 'f';
    460                         $b['size'] = $lucifer[7];
    461                         $b['month'] = $lucifer[1];
    462                         $b['day'] = $lucifer[2];
    463                         $b['year'] = $lucifer[3];
    464                         $b['hour'] = $lucifer[4];
    465                         $b['minute'] = $lucifer[5];
    466                         $b['time'] = @mktime($lucifer[4] + (strcasecmp($lucifer[6], "PM") == 0 ? 12 : 0), $lucifer[5], 0, $lucifer[1], $lucifer[2], $lucifer[3]);
    467                         $b['am/pm'] = $lucifer[6];
    468                         $b['name'] = $lucifer[8];
    469                 } elseif ( !$is_windows && $lucifer = preg_split('/[ ]/', $line, 9, PREG_SPLIT_NO_EMPTY)) {
    470                         //echo $line."\n";
    471                         $lcount = count($lucifer);
    472                         if ( $lcount < 8 )
    473                                 return '';
    474                         $b = array();
    475                         $b['isdir'] = $lucifer[0]{0} === 'd';
    476                         $b['islink'] = $lucifer[0]{0} === 'l';
    477                         if ( $b['isdir'] )
    478                                 $b['type'] = 'd';
    479                         elseif ( $b['islink'] )
    480                                 $b['type'] = 'l';
    481                         else
    482                                 $b['type'] = 'f';
    483                         $b['perms'] = $lucifer[0];
    484                         $b['permsn'] = $this->getnumchmodfromh( $b['perms'] );
    485                         $b['number'] = $lucifer[1];
    486                         $b['owner'] = $lucifer[2];
    487                         $b['group'] = $lucifer[3];
    488                         $b['size'] = $lucifer[4];
    489                         if ( $lcount == 8 ) {
    490                                 sscanf($lucifer[5], '%d-%d-%d', $b['year'], $b['month'], $b['day']);
    491                                 sscanf($lucifer[6], '%d:%d', $b['hour'], $b['minute']);
    492                                 $b['time'] = @mktime($b['hour'], $b['minute'], 0, $b['month'], $b['day'], $b['year']);
    493                                 $b['name'] = $lucifer[7];
    494                         } else {
    495                                 $b['month'] = $lucifer[5];
    496                                 $b['day'] = $lucifer[6];
    497                                 if ( preg_match('/([0-9]{2}):([0-9]{2})/', $lucifer[7], $l2) ) {
    498                                         $b['year'] = date("Y");
    499                                         $b['hour'] = $l2[1];
    500                                         $b['minute'] = $l2[2];
    501                                 } else {
    502                                         $b['year'] = $lucifer[7];
    503                                         $b['hour'] = 0;
    504                                         $b['minute'] = 0;
    505                                 }
    506                                 $b['time'] = strtotime( sprintf('%d %s %d %02d:%02d', $b['day'], $b['month'], $b['year'], $b['hour'], $b['minute']) );
    507                                 $b['name'] = $lucifer[8];
    508                         }
    509                 }
    510 
    511                 // Replace symlinks formatted as "source -> target" with just the source name
    512                 if ( isset( $b['islink'] ) && $b['islink'] ) {
    513                         $b['name'] = preg_replace( '/(\s*->\s*.*)$/', '', $b['name'] );
    514                 }
    515 
    516                 return $b;
     631        public function rmdir( $path, $recursive = FALSE ) {
     632                return $this->delete( $path, $recursive );
    517633        }
    518634
    519635        /**
    520636         *
    521          * @param string $path
    522          * @param bool $include_hidden
     637         * @param string $file
    523638         * @param bool $recursive
    524          * @return bool|array
     639         * @param bool|string $type
     640         *
     641         * @return bool
    525642         */
    526         public function dirlist($path = '.', $include_hidden = true, $recursive = false) {
    527                 if ( $this->is_file($path) ) {
    528                         $limit_file = basename($path);
    529                         $path = dirname($path) . '/';
    530                 } else {
    531                         $limit_file = false;
     643        public function delete( $file, $recursive = FALSE, $type = FALSE ) {
     644                if ( empty( $file ) ) {
     645                        return FALSE;
     646                }
     647                if ( 'f' == $type || $this->is_file( $file ) ) {
     648                        return @ftp_delete( $this->link, $file );
    532649                }
    533650
    534                 $pwd = @ftp_pwd($this->link);
    535                 if ( ! @ftp_chdir($this->link, $path) ) // Cant change to folder = folder doesn't exist
    536                         return false;
    537                 $list = @ftp_rawlist($this->link, '-a', false);
    538                 @ftp_chdir($this->link, $pwd);
    539 
    540                 if ( empty($list) ) // Empty array = non-existent folder (real folder will show . at least)
    541                         return false;
    542 
    543                 $dirlist = array();
    544                 foreach ( $list as $k => $v ) {
    545                         $entry = $this->parselisting($v);
    546                         if ( empty($entry) )
    547                                 continue;
    548 
    549                         if ( '.' == $entry['name'] || '..' == $entry['name'] )
    550                                 continue;
    551 
    552                         if ( ! $include_hidden && '.' == $entry['name'][0] )
    553                                 continue;
    554 
    555                         if ( $limit_file && $entry['name'] != $limit_file)
    556                                 continue;
    557 
    558                         $dirlist[ $entry['name'] ] = $entry;
     651                if ( ! $recursive ) {
     652                        return @ftp_rmdir( $this->link, $file );
    559653                }
    560654
    561                 $ret = array();
    562                 foreach ( (array)$dirlist as $struc ) {
    563                         if ( 'd' == $struc['type'] ) {
    564                                 if ( $recursive )
    565                                         $struc['files'] = $this->dirlist($path . '/' . $struc['name'], $include_hidden, $recursive);
    566                                 else
    567                                         $struc['files'] = array();
     655                $filelist = $this->dirlist( trailingslashit( $file ) );
     656                if ( ! empty( $filelist ) ) {
     657                        foreach ( $filelist as $delete_file ) {
     658                                $this->delete( trailingslashit( $file ) . $delete_file['name'], $recursive, $delete_file['type'] );
    568659                        }
    569 
    570                         $ret[ $struc['name'] ] = $struc;
    571660                }
    572                 return $ret;
     661
     662                return @ftp_rmdir( $this->link, $file );
    573663        }
    574664
    575665        /**
    576666         */
    577667        public function __destruct() {
    578                 if ( $this->link )
    579                         ftp_close($this->link);
     668                if ( $this->link ) {
     669                        ftp_close( $this->link );
     670                }
    580671        }
    581672}