wp/wp-admin/includes/class-wp-filesystem-ftpsockets.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
    12  * @since 2.5.0
    12  * @since 2.5.0
    13  *
    13  *
    14  * @see WP_Filesystem_Base
    14  * @see WP_Filesystem_Base
    15  */
    15  */
    16 class WP_Filesystem_ftpsockets extends WP_Filesystem_Base {
    16 class WP_Filesystem_ftpsockets extends WP_Filesystem_Base {
    17 	/**
    17 
       
    18 	/**
       
    19 	 * @since 2.5.0
    18 	 * @var ftp
    20 	 * @var ftp
    19 	 */
    21 	 */
    20 	public $ftp;
    22 	public $ftp;
    21 
    23 
    22 	/**
    24 	/**
       
    25 	 * Constructor.
       
    26 	 *
       
    27 	 * @since 2.5.0
    23 	 *
    28 	 *
    24 	 * @param array $opt
    29 	 * @param array $opt
    25 	 */
    30 	 */
    26 	public function __construct( $opt  = '' ) {
    31 	public function __construct( $opt = '' ) {
    27 		$this->method = 'ftpsockets';
    32 		$this->method = 'ftpsockets';
    28 		$this->errors = new WP_Error();
    33 		$this->errors = new WP_Error();
    29 
    34 
    30 		// Check if possible to use ftp functions.
    35 		// Check if possible to use ftp functions.
    31 		if ( ! @include_once( ABSPATH . 'wp-admin/includes/class-ftp.php' ) ) {
    36 		if ( ! @include_once( ABSPATH . 'wp-admin/includes/class-ftp.php' ) ) {
    32 			return;
    37 			return;
    33 		}
    38 		}
    34 		$this->ftp = new ftp();
    39 		$this->ftp = new ftp();
    35 
    40 
    36 		if ( empty($opt['port']) )
    41 		if ( empty( $opt['port'] ) ) {
    37 			$this->options['port'] = 21;
    42 			$this->options['port'] = 21;
    38 		else
    43 		} else {
    39 			$this->options['port'] = (int) $opt['port'];
    44 			$this->options['port'] = (int) $opt['port'];
    40 
    45 		}
    41 		if ( empty($opt['hostname']) )
    46 
    42 			$this->errors->add('empty_hostname', __('FTP hostname is required'));
    47 		if ( empty( $opt['hostname'] ) ) {
    43 		else
    48 			$this->errors->add( 'empty_hostname', __( 'FTP hostname is required' ) );
       
    49 		} else {
    44 			$this->options['hostname'] = $opt['hostname'];
    50 			$this->options['hostname'] = $opt['hostname'];
       
    51 		}
    45 
    52 
    46 		// Check if the options provided are OK.
    53 		// Check if the options provided are OK.
    47 		if ( empty ($opt['username']) )
    54 		if ( empty( $opt['username'] ) ) {
    48 			$this->errors->add('empty_username', __('FTP username is required'));
    55 			$this->errors->add( 'empty_username', __( 'FTP username is required' ) );
    49 		else
    56 		} else {
    50 			$this->options['username'] = $opt['username'];
    57 			$this->options['username'] = $opt['username'];
    51 
    58 		}
    52 		if ( empty ($opt['password']) )
    59 
    53 			$this->errors->add('empty_password', __('FTP password is required'));
    60 		if ( empty( $opt['password'] ) ) {
    54 		else
    61 			$this->errors->add( 'empty_password', __( 'FTP password is required' ) );
       
    62 		} else {
    55 			$this->options['password'] = $opt['password'];
    63 			$this->options['password'] = $opt['password'];
    56 	}
    64 		}
    57 
    65 	}
    58 	/**
    66 
    59 	 *
    67 	/**
    60 	 * @return bool
    68 	 * Connects filesystem.
       
    69 	 *
       
    70 	 * @since 2.5.0
       
    71 	 *
       
    72 	 * @return bool True on success, false on failure.
    61 	 */
    73 	 */
    62 	public function connect() {
    74 	public function connect() {
    63 		if ( ! $this->ftp )
    75 		if ( ! $this->ftp ) {
    64 			return false;
    76 			return false;
    65 
    77 		}
    66 		$this->ftp->setTimeout(FS_CONNECT_TIMEOUT);
    78 
       
    79 		$this->ftp->setTimeout( FS_CONNECT_TIMEOUT );
    67 
    80 
    68 		if ( ! $this->ftp->SetServer( $this->options['hostname'], $this->options['port'] ) ) {
    81 		if ( ! $this->ftp->SetServer( $this->options['hostname'], $this->options['port'] ) ) {
    69 			$this->errors->add( 'connect',
    82 			$this->errors->add(
       
    83 				'connect',
    70 				/* translators: %s: hostname:port */
    84 				/* translators: %s: hostname:port */
    71 				sprintf( __( 'Failed to connect to FTP Server %s' ),
    85 				sprintf(
       
    86 					__( 'Failed to connect to FTP Server %s' ),
    72 					$this->options['hostname'] . ':' . $this->options['port']
    87 					$this->options['hostname'] . ':' . $this->options['port']
    73 				)
    88 				)
    74 			);
    89 			);
    75 			return false;
    90 			return false;
    76 		}
    91 		}
    77 
    92 
    78 		if ( ! $this->ftp->connect() ) {
    93 		if ( ! $this->ftp->connect() ) {
    79 			$this->errors->add( 'connect',
    94 			$this->errors->add(
       
    95 				'connect',
    80 				/* translators: %s: hostname:port */
    96 				/* translators: %s: hostname:port */
    81 				sprintf( __( 'Failed to connect to FTP Server %s' ),
    97 				sprintf(
       
    98 					__( 'Failed to connect to FTP Server %s' ),
    82 					$this->options['hostname'] . ':' . $this->options['port']
    99 					$this->options['hostname'] . ':' . $this->options['port']
    83 				)
   100 				)
    84 			);
   101 			);
    85 			return false;
   102 			return false;
    86 		}
   103 		}
    87 
   104 
    88 		if ( ! $this->ftp->login( $this->options['username'], $this->options['password'] ) ) {
   105 		if ( ! $this->ftp->login( $this->options['username'], $this->options['password'] ) ) {
    89 			$this->errors->add( 'auth',
   106 			$this->errors->add(
       
   107 				'auth',
    90 				/* translators: %s: username */
   108 				/* translators: %s: username */
    91 				sprintf( __( 'Username/Password incorrect for %s' ),
   109 				sprintf(
       
   110 					__( 'Username/Password incorrect for %s' ),
    92 					$this->options['username']
   111 					$this->options['username']
    93 				)
   112 				)
    94 			);
   113 			);
    95 			return false;
   114 			return false;
    96 		}
   115 		}
   100 		$this->ftp->setTimeout( FS_TIMEOUT );
   119 		$this->ftp->setTimeout( FS_TIMEOUT );
   101 		return true;
   120 		return true;
   102 	}
   121 	}
   103 
   122 
   104 	/**
   123 	/**
   105 	 * Retrieves the file contents.
   124 	 * Reads entire file into a string.
   106 	 *
   125 	 *
   107 	 * @since 2.5.0
   126 	 * @since 2.5.0
   108 	 *
   127 	 *
   109 	 * @param string $file Filename.
   128 	 * @param string $file Name of the file to read.
   110 	 * @return string|false File contents on success, false if no temp file could be opened,
   129 	 * @return string|false Read data on success, false if no temporary file could be opened,
   111 	 *                      or if the file doesn't exist.
   130 	 *                      or if the file couldn't be retrieved.
   112 	 */
   131 	 */
   113 	public function get_contents( $file ) {
   132 	public function get_contents( $file ) {
   114 		if ( ! $this->exists($file) )
   133 		if ( ! $this->exists( $file ) ) {
   115 			return false;
   134 			return false;
       
   135 		}
   116 
   136 
   117 		$temp = wp_tempnam( $file );
   137 		$temp = wp_tempnam( $file );
   118 
   138 
   119 		if ( ! $temphandle = fopen( $temp, 'w+' ) ) {
   139 		if ( ! $temphandle = fopen( $temp, 'w+' ) ) {
   120 			unlink( $temp );
   140 			unlink( $temp );
   121 			return false;
   141 			return false;
   122 		}
   142 		}
   123 
   143 
   124 		mbstring_binary_safe_encoding();
   144 		mbstring_binary_safe_encoding();
   125 
   145 
   126 		if ( ! $this->ftp->fget($temphandle, $file) ) {
   146 		if ( ! $this->ftp->fget( $temphandle, $file ) ) {
   127 			fclose($temphandle);
   147 			fclose( $temphandle );
   128 			unlink($temp);
   148 			unlink( $temp );
   129 
   149 
   130 			reset_mbstring_encoding();
   150 			reset_mbstring_encoding();
   131 
   151 
   132 			return ''; // Blank document, File does exist, It's just blank.
   152 			return ''; // Blank document, File does exist, It's just blank.
   133 		}
   153 		}
   135 		reset_mbstring_encoding();
   155 		reset_mbstring_encoding();
   136 
   156 
   137 		fseek( $temphandle, 0 ); // Skip back to the start of the file being written to
   157 		fseek( $temphandle, 0 ); // Skip back to the start of the file being written to
   138 		$contents = '';
   158 		$contents = '';
   139 
   159 
   140 		while ( ! feof($temphandle) )
   160 		while ( ! feof( $temphandle ) ) {
   141 			$contents .= fread($temphandle, 8192);
   161 			$contents .= fread( $temphandle, 8 * KB_IN_BYTES );
   142 
   162 		}
   143 		fclose($temphandle);
   163 
   144 		unlink($temp);
   164 		fclose( $temphandle );
       
   165 		unlink( $temp );
   145 		return $contents;
   166 		return $contents;
   146 	}
   167 	}
   147 
   168 
   148 	/**
   169 	/**
   149 	 *
   170 	 * Reads entire file into an array.
   150 	 * @param string $file
   171 	 *
   151 	 * @return array
   172 	 * @since 2.5.0
   152 	 */
   173 	 *
   153 	public function get_contents_array($file) {
   174 	 * @param string $file Path to the file.
   154 		return explode("\n", $this->get_contents($file) );
   175 	 * @return array|false File contents in an array on success, false on failure.
   155 	}
   176 	 */
   156 
   177 	public function get_contents_array( $file ) {
   157 	/**
   178 		return explode( "\n", $this->get_contents( $file ) );
   158 	 *
   179 	}
   159 	 * @param string $file
   180 
   160 	 * @param string $contents
   181 	/**
   161 	 * @param int|bool $mode
   182 	 * Writes a string to a file.
   162 	 * @return bool
   183 	 *
   163 	 */
   184 	 * @since 2.5.0
   164 	public function put_contents($file, $contents, $mode = false ) {
   185 	 *
       
   186 	 * @param string    $file     Remote path to the file where to write the data.
       
   187 	 * @param string    $contents The data to write.
       
   188 	 * @param int|false $mode     Optional. The file permissions as octal number, usually 0644.
       
   189 	 *                            Default false.
       
   190 	 * @return bool True on success, false on failure.
       
   191 	 */
       
   192 	public function put_contents( $file, $contents, $mode = false ) {
   165 		$temp = wp_tempnam( $file );
   193 		$temp = wp_tempnam( $file );
   166 		if ( ! $temphandle = @fopen($temp, 'w+') ) {
   194 		if ( ! $temphandle = @fopen( $temp, 'w+' ) ) {
   167 			unlink($temp);
   195 			unlink( $temp );
   168 			return false;
   196 			return false;
   169 		}
   197 		}
   170 
   198 
   171 		// The FTP class uses string functions internally during file download/upload
   199 		// The FTP class uses string functions internally during file download/upload
   172 		mbstring_binary_safe_encoding();
   200 		mbstring_binary_safe_encoding();
   181 			return false;
   209 			return false;
   182 		}
   210 		}
   183 
   211 
   184 		fseek( $temphandle, 0 ); // Skip back to the start of the file being written to
   212 		fseek( $temphandle, 0 ); // Skip back to the start of the file being written to
   185 
   213 
   186 		$ret = $this->ftp->fput($file, $temphandle);
   214 		$ret = $this->ftp->fput( $file, $temphandle );
   187 
   215 
   188 		reset_mbstring_encoding();
   216 		reset_mbstring_encoding();
   189 
   217 
   190 		fclose($temphandle);
   218 		fclose( $temphandle );
   191 		unlink($temp);
   219 		unlink( $temp );
   192 
   220 
   193 		$this->chmod($file, $mode);
   221 		$this->chmod( $file, $mode );
   194 
   222 
   195 		return $ret;
   223 		return $ret;
   196 	}
   224 	}
   197 
   225 
   198 	/**
   226 	/**
   199 	 *
   227 	 * Gets the current working directory.
   200 	 * @return string
   228 	 *
       
   229 	 * @since 2.5.0
       
   230 	 *
       
   231 	 * @return string|false The current working directory on success, false on failure.
   201 	 */
   232 	 */
   202 	public function cwd() {
   233 	public function cwd() {
   203 		$cwd = $this->ftp->pwd();
   234 		$cwd = $this->ftp->pwd();
   204 		if ( $cwd )
   235 		if ( $cwd ) {
   205 			$cwd = trailingslashit($cwd);
   236 			$cwd = trailingslashit( $cwd );
       
   237 		}
   206 		return $cwd;
   238 		return $cwd;
   207 	}
   239 	}
   208 
   240 
   209 	/**
   241 	/**
   210 	 *
   242 	 * Changes current directory.
   211 	 * @param string $file
   243 	 *
   212 	 * @return bool
   244 	 * @since 2.5.0
   213 	 */
   245 	 *
   214 	public function chdir($file) {
   246 	 * @param string $dir The new current directory.
   215 		return $this->ftp->chdir($file);
   247 	 * @return bool True on success, false on failure.
   216 	}
   248 	 */
   217 
   249 	public function chdir( $dir ) {
   218 	/**
   250 		return $this->ftp->chdir( $dir );
   219 	 *
   251 	}
   220 	 * @param string $file
   252 
   221 	 * @param int|bool $mode
   253 	/**
   222 	 * @param bool $recursive
   254 	 * Changes filesystem permissions.
   223 	 * @return bool
   255 	 *
   224 	 */
   256 	 * @since 2.5.0
   225 	public function chmod($file, $mode = false, $recursive = false ) {
   257 	 *
       
   258 	 * @param string    $file      Path to the file.
       
   259 	 * @param int|false $mode      Optional. The permissions as octal number, usually 0644 for files,
       
   260 	 *                             0755 for directories. Default false.
       
   261 	 * @param bool      $recursive Optional. If set to true, changes file group recursively.
       
   262 	 *                             Default false.
       
   263 	 * @return bool True on success, false on failure.
       
   264 	 */
       
   265 	public function chmod( $file, $mode = false, $recursive = false ) {
   226 		if ( ! $mode ) {
   266 		if ( ! $mode ) {
   227 			if ( $this->is_file($file) )
   267 			if ( $this->is_file( $file ) ) {
   228 				$mode = FS_CHMOD_FILE;
   268 				$mode = FS_CHMOD_FILE;
   229 			elseif ( $this->is_dir($file) )
   269 			} elseif ( $this->is_dir( $file ) ) {
   230 				$mode = FS_CHMOD_DIR;
   270 				$mode = FS_CHMOD_DIR;
   231 			else
   271 			} else {
   232 				return false;
   272 				return false;
       
   273 			}
   233 		}
   274 		}
   234 
   275 
   235 		// chmod any sub-objects if recursive.
   276 		// chmod any sub-objects if recursive.
   236 		if ( $recursive && $this->is_dir($file) ) {
   277 		if ( $recursive && $this->is_dir( $file ) ) {
   237 			$filelist = $this->dirlist($file);
   278 			$filelist = $this->dirlist( $file );
   238 			foreach ( (array)$filelist as $filename => $filemeta )
   279 			foreach ( (array) $filelist as $filename => $filemeta ) {
   239 				$this->chmod($file . '/' . $filename, $mode, $recursive);
   280 				$this->chmod( $file . '/' . $filename, $mode, $recursive );
       
   281 			}
   240 		}
   282 		}
   241 
   283 
   242 		// chmod the file or directory
   284 		// chmod the file or directory
   243 		return $this->ftp->chmod($file, $mode);
   285 		return $this->ftp->chmod( $file, $mode );
   244 	}
   286 	}
   245 
   287 
   246 	/**
   288 	/**
   247 	 *
   289 	 * Gets the file owner.
   248 	 * @param string $file
   290 	 *
   249 	 * @return string
   291 	 * @since 2.5.0
   250 	 */
   292 	 *
   251 	public function owner($file) {
   293 	 * @param string $file Path to the file.
   252 		$dir = $this->dirlist($file);
   294 	 * @return string|false Username of the owner on success, false on failure.
   253 		return $dir[$file]['owner'];
   295 	 */
   254 	}
   296 	public function owner( $file ) {
   255 
   297 		$dir = $this->dirlist( $file );
   256 	/**
   298 		return $dir[ $file ]['owner'];
   257 	 *
   299 	}
   258 	 * @param string $file
   300 
   259 	 * @return string
   301 	/**
   260 	 */
   302 	 * Gets the permissions of the specified file or filepath in their octal format.
   261 	public function getchmod($file) {
   303 	 *
   262 		$dir = $this->dirlist($file);
   304 	 * @since 2.5.0
   263 		return $dir[$file]['permsn'];
   305 	 *
   264 	}
   306 	 * @param string $file Path to the file.
   265 
   307 	 * @return string Mode of the file (the last 3 digits).
   266 	/**
   308 	 */
   267 	 *
   309 	public function getchmod( $file ) {
   268 	 * @param string $file
   310 		$dir = $this->dirlist( $file );
   269 	 * @return string
   311 		return $dir[ $file ]['permsn'];
   270 	 */
   312 	}
   271 	public function group($file) {
   313 
   272 		$dir = $this->dirlist($file);
   314 	/**
   273 		return $dir[$file]['group'];
   315 	 * Gets the file's group.
   274 	}
   316 	 *
   275 
   317 	 * @since 2.5.0
   276 	/**
   318 	 *
   277 	 *
   319 	 * @param string $file Path to the file.
   278 	 * @param string   $source
   320 	 * @return string|false The group on success, false on failure.
   279 	 * @param string   $destination
   321 	 */
   280 	 * @param bool     $overwrite
   322 	public function group( $file ) {
   281 	 * @param int|bool $mode
   323 		$dir = $this->dirlist( $file );
   282 	 * @return bool
   324 		return $dir[ $file ]['group'];
   283 	 */
   325 	}
   284 	public function copy($source, $destination, $overwrite = false, $mode = false) {
   326 
   285 		if ( ! $overwrite && $this->exists($destination) )
   327 	/**
   286 			return false;
   328 	 * Copies a file.
   287 
   329 	 *
   288 		$content = $this->get_contents($source);
   330 	 * @since 2.5.0
   289 		if ( false === $content )
   331 	 *
   290 			return false;
   332 	 * @param string    $source      Path to the source file.
   291 
   333 	 * @param string    $destination Path to the destination file.
   292 		return $this->put_contents($destination, $content, $mode);
   334 	 * @param bool      $overwrite   Optional. Whether to overwrite the destination file if it exists.
   293 	}
   335 	 *                               Default false.
   294 
   336 	 * @param int|false $mode        Optional. The permissions as octal number, usually 0644 for files,
   295 	/**
   337 	 *                               0755 for dirs. Default false.
   296 	 *
   338 	 * @return bool True on success, false on failure.
   297 	 * @param string $source
   339 	 */
   298 	 * @param string $destination
   340 	public function copy( $source, $destination, $overwrite = false, $mode = false ) {
   299 	 * @param bool   $overwrite
   341 		if ( ! $overwrite && $this->exists( $destination ) ) {
   300 	 * @return bool
   342 			return false;
   301 	 */
   343 		}
   302 	public function move($source, $destination, $overwrite = false ) {
   344 
   303 		return $this->ftp->rename($source, $destination);
   345 		$content = $this->get_contents( $source );
   304 	}
   346 		if ( false === $content ) {
   305 
   347 			return false;
   306 	/**
   348 		}
   307 	 *
   349 
   308 	 * @param string $file
   350 		return $this->put_contents( $destination, $content, $mode );
   309 	 * @param bool   $recursive
   351 	}
   310 	 * @param string $type
   352 
   311 	 * @return bool
   353 	/**
   312 	 */
   354 	 * Moves a file.
   313 	public function delete($file, $recursive = false, $type = false) {
   355 	 *
   314 		if ( empty($file) )
   356 	 * @since 2.5.0
   315 			return false;
   357 	 *
   316 		if ( 'f' == $type || $this->is_file($file) )
   358 	 * @param string $source      Path to the source file.
   317 			return $this->ftp->delete($file);
   359 	 * @param string $destination Path to the destination file.
   318 		if ( !$recursive )
   360 	 * @param bool   $overwrite   Optional. Whether to overwrite the destination file if it exists.
   319 			return $this->ftp->rmdir($file);
   361 	 *                            Default false.
   320 
   362 	 * @return bool True on success, false on failure.
   321 		return $this->ftp->mdel($file);
   363 	 */
   322 	}
   364 	public function move( $source, $destination, $overwrite = false ) {
   323 
   365 		return $this->ftp->rename( $source, $destination );
   324 	/**
   366 	}
   325 	 *
   367 
   326 	 * @param string $file
   368 	/**
   327 	 * @return bool
   369 	 * Deletes a file or directory.
       
   370 	 *
       
   371 	 * @since 2.5.0
       
   372 	 *
       
   373 	 * @param string       $file      Path to the file or directory.
       
   374 	 * @param bool         $recursive Optional. If set to true, changes file group recursively.
       
   375 	 *                                Default false.
       
   376 	 * @param string|false $type      Type of resource. 'f' for file, 'd' for directory.
       
   377 	 *                                Default false.
       
   378 	 * @return bool True on success, false on failure.
       
   379 	 */
       
   380 	public function delete( $file, $recursive = false, $type = false ) {
       
   381 		if ( empty( $file ) ) {
       
   382 			return false;
       
   383 		}
       
   384 		if ( 'f' == $type || $this->is_file( $file ) ) {
       
   385 			return $this->ftp->delete( $file );
       
   386 		}
       
   387 		if ( ! $recursive ) {
       
   388 			return $this->ftp->rmdir( $file );
       
   389 		}
       
   390 
       
   391 		return $this->ftp->mdel( $file );
       
   392 	}
       
   393 
       
   394 	/**
       
   395 	 * Checks if a file or directory exists.
       
   396 	 *
       
   397 	 * @since 2.5.0
       
   398 	 *
       
   399 	 * @param string $file Path to file or directory.
       
   400 	 * @return bool Whether $file exists or not.
   328 	 */
   401 	 */
   329 	public function exists( $file ) {
   402 	public function exists( $file ) {
   330 		$list = $this->ftp->nlist( $file );
   403 		$list = $this->ftp->nlist( $file );
   331 
   404 
   332 		if ( empty( $list ) && $this->is_dir( $file ) ) {
   405 		if ( empty( $list ) && $this->is_dir( $file ) ) {
   333 			return true; // File is an empty directory.
   406 			return true; // File is an empty directory.
   334 		}
   407 		}
   335 
   408 
   336 		return !empty( $list ); //empty list = no file, so invert.
   409 		return ! empty( $list ); //empty list = no file, so invert.
   337 		// Return $this->ftp->is_exists($file); has issues with ABOR+426 responses on the ncFTPd server.
   410 		// Return $this->ftp->is_exists($file); has issues with ABOR+426 responses on the ncFTPd server.
   338 	}
   411 	}
   339 
   412 
   340 	/**
   413 	/**
   341 	 *
   414 	 * Checks if resource is a file.
   342 	 * @param string $file
   415 	 *
   343 	 * @return bool
   416 	 * @since 2.5.0
   344 	 */
   417 	 *
   345 	public function is_file($file) {
   418 	 * @param string $file File path.
   346 		if ( $this->is_dir($file) )
   419 	 * @return bool Whether $file is a file.
   347 			return false;
   420 	 */
   348 		if ( $this->exists($file) )
   421 	public function is_file( $file ) {
       
   422 		if ( $this->is_dir( $file ) ) {
       
   423 			return false;
       
   424 		}
       
   425 		if ( $this->exists( $file ) ) {
   349 			return true;
   426 			return true;
       
   427 		}
   350 		return false;
   428 		return false;
   351 	}
   429 	}
   352 
   430 
   353 	/**
   431 	/**
   354 	 *
   432 	 * Checks if resource is a directory.
   355 	 * @param string $path
   433 	 *
   356 	 * @return bool
   434 	 * @since 2.5.0
   357 	 */
   435 	 *
   358 	public function is_dir($path) {
   436 	 * @param string $path Directory path.
       
   437 	 * @return bool Whether $path is a directory.
       
   438 	 */
       
   439 	public function is_dir( $path ) {
   359 		$cwd = $this->cwd();
   440 		$cwd = $this->cwd();
   360 		if ( $this->chdir($path) ) {
   441 		if ( $this->chdir( $path ) ) {
   361 			$this->chdir($cwd);
   442 			$this->chdir( $cwd );
   362 			return true;
   443 			return true;
   363 		}
   444 		}
   364 		return false;
   445 		return false;
   365 	}
   446 	}
   366 
   447 
   367 	/**
   448 	/**
   368 	 *
   449 	 * Checks if a file is readable.
   369 	 * @param string $file
   450 	 *
   370 	 * @return bool
   451 	 * @since 2.5.0
   371 	 */
   452 	 *
   372 	public function is_readable($file) {
   453 	 * @param string $file Path to file.
       
   454 	 * @return bool Whether $file is readable.
       
   455 	 */
       
   456 	public function is_readable( $file ) {
   373 		return true;
   457 		return true;
   374 	}
   458 	}
   375 
   459 
   376 	/**
   460 	/**
   377 	 *
   461 	 * Checks if a file or directory is writable.
   378 	 * @param string $file
   462 	 *
   379 	 * @return bool
   463 	 * @since 2.5.0
   380 	 */
   464 	 *
   381 	public function is_writable($file) {
   465 	 * @param string $file Path to file or directory.
       
   466 	 * @return bool Whether $file is writable.
       
   467 	 */
       
   468 	public function is_writable( $file ) {
   382 		return true;
   469 		return true;
   383 	}
   470 	}
   384 
   471 
   385 	/**
   472 	/**
   386 	 *
   473 	 * Gets the file's last access time.
   387 	 * @param string $file
   474 	 *
   388 	 * @return bool
   475 	 * @since 2.5.0
   389 	 */
   476 	 *
   390 	public function atime($file) {
   477 	 * @param string $file Path to file.
       
   478 	 * @return int|false Unix timestamp representing last access time, false on failure.
       
   479 	 */
       
   480 	public function atime( $file ) {
   391 		return false;
   481 		return false;
   392 	}
   482 	}
   393 
   483 
   394 	/**
   484 	/**
   395 	 *
   485 	 * Gets the file modification time.
   396 	 * @param string $file
   486 	 *
   397 	 * @return int
   487 	 * @since 2.5.0
   398 	 */
   488 	 *
   399 	public function mtime($file) {
   489 	 * @param string $file Path to file.
   400 		return $this->ftp->mdtm($file);
   490 	 * @return int|false Unix timestamp representing modification time, false on failure.
   401 	}
   491 	 */
   402 
   492 	public function mtime( $file ) {
   403 	/**
   493 		return $this->ftp->mdtm( $file );
   404 	 * @param string $file
   494 	}
   405 	 * @return int
   495 
   406 	 */
   496 	/**
   407 	public function size($file) {
   497 	 * Gets the file size (in bytes).
   408 		return $this->ftp->filesize($file);
   498 	 *
   409 	}
   499 	 * @since 2.5.0
   410 
   500 	 *
   411 	/**
   501 	 * @param string $file Path to file.
   412 	 *
   502 	 * @return int|false Size of the file in bytes on success, false on failure.
   413 	 * @param string $file
   503 	 */
   414 	 * @param int $time
   504 	public function size( $file ) {
   415 	 * @param int $atime
   505 		return $this->ftp->filesize( $file );
   416 	 * @return bool
   506 	}
   417 	 */
   507 
   418 	public function touch($file, $time = 0, $atime = 0 ) {
   508 	/**
       
   509 	 * Sets the access and modification times of a file.
       
   510 	 *
       
   511 	 * Note: If $file doesn't exist, it will be created.
       
   512 	 *
       
   513 	 * @since 2.5.0
       
   514 	 *
       
   515 	 * @param string $file  Path to file.
       
   516 	 * @param int    $time  Optional. Modified time to set for file.
       
   517 	 *                      Default 0.
       
   518 	 * @param int    $atime Optional. Access time to set for file.
       
   519 	 *                      Default 0.
       
   520 	 * @return bool True on success, false on failure.
       
   521 	 */
       
   522 	public function touch( $file, $time = 0, $atime = 0 ) {
   419 		return false;
   523 		return false;
   420 	}
   524 	}
   421 
   525 
   422 	/**
   526 	/**
   423 	 *
   527 	 * Creates a directory.
   424 	 * @param string $path
   528 	 *
   425 	 * @param mixed  $chmod
   529 	 * @since 2.5.0
   426 	 * @param mixed  $chown
   530 	 *
   427 	 * @param mixed  $chgrp
   531 	 * @param string     $path  Path for new directory.
   428 	 * @return bool
   532 	 * @param int|false  $chmod Optional. The permissions as octal number (or false to skip chmod).
   429 	 */
   533 	 *                          Default false.
   430 	public function mkdir($path, $chmod = false, $chown = false, $chgrp = false ) {
   534 	 * @param string|int $chown Optional. A user name or number (or false to skip chown).
   431 		$path = untrailingslashit($path);
   535 	 *                          Default false.
   432 		if ( empty($path) )
   536 	 * @param string|int $chgrp Optional. A group name or number (or false to skip chgrp).
   433 			return false;
   537 	 *                          Default false.
   434 
   538 	 * @return bool True on success, false on failure.
   435 		if ( ! $this->ftp->mkdir($path) )
   539 	 */
   436 			return false;
   540 	public function mkdir( $path, $chmod = false, $chown = false, $chgrp = false ) {
   437 		if ( ! $chmod )
   541 		$path = untrailingslashit( $path );
       
   542 		if ( empty( $path ) ) {
       
   543 			return false;
       
   544 		}
       
   545 
       
   546 		if ( ! $this->ftp->mkdir( $path ) ) {
       
   547 			return false;
       
   548 		}
       
   549 		if ( ! $chmod ) {
   438 			$chmod = FS_CHMOD_DIR;
   550 			$chmod = FS_CHMOD_DIR;
   439 		$this->chmod($path, $chmod);
   551 		}
       
   552 		$this->chmod( $path, $chmod );
   440 		return true;
   553 		return true;
   441 	}
   554 	}
   442 
   555 
   443 	/**
   556 	/**
   444 	 *
   557 	 * Deletes a directory.
   445 	 * @param string $path
   558 	 *
   446 	 * @param bool $recursive
   559 	 * @since 2.5.0
   447 	 * @return bool
   560 	 *
   448 	 */
   561 	 * @param string $path      Path to directory.
   449 	public function rmdir($path, $recursive = false ) {
   562 	 * @param bool   $recursive Optional. Whether to recursively remove files/directories.
   450 		return $this->delete($path, $recursive);
   563 	 *                          Default false.
   451 	}
   564 	 * @return bool True on success, false on failure.
   452 
   565 	 */
   453 	/**
   566 	public function rmdir( $path, $recursive = false ) {
   454 	 *
   567 		return $this->delete( $path, $recursive );
   455 	 * @param string $path
   568 	}
   456 	 * @param bool   $include_hidden
   569 
   457 	 * @param bool   $recursive
   570 	/**
   458 	 * @return bool|array
   571 	 * Gets details for files in a directory or a specific file.
   459 	 */
   572 	 *
   460 	public function dirlist($path = '.', $include_hidden = true, $recursive = false ) {
   573 	 * @since 2.5.0
   461 		if ( $this->is_file($path) ) {
   574 	 *
   462 			$limit_file = basename($path);
   575 	 * @param string $path           Path to directory or file.
   463 			$path = dirname($path) . '/';
   576 	 * @param bool   $include_hidden Optional. Whether to include details of hidden ("." prefixed) files.
       
   577 	 *                               Default true.
       
   578 	 * @param bool   $recursive      Optional. Whether to recursively include file details in nested directories.
       
   579 	 *                               Default false.
       
   580 	 * @return array|false {
       
   581 	 *     Array of files. False if unable to list directory contents.
       
   582 	 *
       
   583 	 *     @type string $name        Name of the file or directory.
       
   584 	 *     @type string $perms       *nix representation of permissions.
       
   585 	 *     @type int    $permsn      Octal representation of permissions.
       
   586 	 *     @type string $owner       Owner name or ID.
       
   587 	 *     @type int    $size        Size of file in bytes.
       
   588 	 *     @type int    $lastmodunix Last modified unix timestamp.
       
   589 	 *     @type mixed  $lastmod     Last modified month (3 letter) and day (without leading 0).
       
   590 	 *     @type int    $time        Last modified time.
       
   591 	 *     @type string $type        Type of resource. 'f' for file, 'd' for directory.
       
   592 	 *     @type mixed  $files       If a directory and $recursive is true, contains another array of files.
       
   593 	 * }
       
   594 	 */
       
   595 	public function dirlist( $path = '.', $include_hidden = true, $recursive = false ) {
       
   596 		if ( $this->is_file( $path ) ) {
       
   597 			$limit_file = basename( $path );
       
   598 			$path       = dirname( $path ) . '/';
   464 		} else {
   599 		} else {
   465 			$limit_file = false;
   600 			$limit_file = false;
   466 		}
   601 		}
   467 
   602 
   468 		mbstring_binary_safe_encoding();
   603 		mbstring_binary_safe_encoding();
   469 
   604 
   470 		$list = $this->ftp->dirlist($path);
   605 		$list = $this->ftp->dirlist( $path );
   471 		if ( empty( $list ) && ! $this->exists( $path ) ) {
   606 		if ( empty( $list ) && ! $this->exists( $path ) ) {
   472 
   607 
   473 			reset_mbstring_encoding();
   608 			reset_mbstring_encoding();
   474 
   609 
   475 			return false;
   610 			return false;
   476 		}
   611 		}
   477 
   612 
   478 		$ret = array();
   613 		$ret = array();
   479 		foreach ( $list as $struc ) {
   614 		foreach ( $list as $struc ) {
   480 
   615 
   481 			if ( '.' == $struc['name'] || '..' == $struc['name'] )
   616 			if ( '.' == $struc['name'] || '..' == $struc['name'] ) {
   482 				continue;
   617 				continue;
   483 
   618 			}
   484 			if ( ! $include_hidden && '.' == $struc['name'][0] )
   619 
       
   620 			if ( ! $include_hidden && '.' == $struc['name'][0] ) {
   485 				continue;
   621 				continue;
   486 
   622 			}
   487 			if ( $limit_file && $struc['name'] != $limit_file )
   623 
       
   624 			if ( $limit_file && $struc['name'] != $limit_file ) {
   488 				continue;
   625 				continue;
       
   626 			}
   489 
   627 
   490 			if ( 'd' == $struc['type'] ) {
   628 			if ( 'd' == $struc['type'] ) {
   491 				if ( $recursive )
   629 				if ( $recursive ) {
   492 					$struc['files'] = $this->dirlist($path . '/' . $struc['name'], $include_hidden, $recursive);
   630 					$struc['files'] = $this->dirlist( $path . '/' . $struc['name'], $include_hidden, $recursive );
   493 				else
   631 				} else {
   494 					$struc['files'] = array();
   632 					$struc['files'] = array();
       
   633 				}
   495 			}
   634 			}
   496 
   635 
   497 			// Replace symlinks formatted as "source -> target" with just the source name
   636 			// Replace symlinks formatted as "source -> target" with just the source name
   498 			if ( $struc['islink'] )
   637 			if ( $struc['islink'] ) {
   499 				$struc['name'] = preg_replace( '/(\s*->\s*.*)$/', '', $struc['name'] );
   638 				$struc['name'] = preg_replace( '/(\s*->\s*.*)$/', '', $struc['name'] );
       
   639 			}
   500 
   640 
   501 			// Add the Octal representation of the file permissions
   641 			// Add the Octal representation of the file permissions
   502 			$struc['permsn'] = $this->getnumchmodfromh( $struc['perms'] );
   642 			$struc['permsn'] = $this->getnumchmodfromh( $struc['perms'] );
   503 
   643 
   504 			$ret[ $struc['name'] ] = $struc;
   644 			$ret[ $struc['name'] ] = $struc;
   508 
   648 
   509 		return $ret;
   649 		return $ret;
   510 	}
   650 	}
   511 
   651 
   512 	/**
   652 	/**
       
   653 	 * Destructor.
       
   654 	 *
       
   655 	 * @since 2.5.0
   513 	 */
   656 	 */
   514 	public function __destruct() {
   657 	public function __destruct() {
   515 		$this->ftp->quit();
   658 		$this->ftp->quit();
   516 	}
   659 	}
   517 }
   660 }