wp/wp-admin/includes/class-wp-filesystem-ftpext.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    36 		if ( ! extension_loaded( 'ftp' ) ) {
    36 		if ( ! extension_loaded( 'ftp' ) ) {
    37 			$this->errors->add( 'no_ftp_ext', __( 'The ftp PHP extension is not available' ) );
    37 			$this->errors->add( 'no_ftp_ext', __( 'The ftp PHP extension is not available' ) );
    38 			return;
    38 			return;
    39 		}
    39 		}
    40 
    40 
    41 		// This Class uses the timeout on a per-connection basis, Others use it on a per-action basis.
    41 		// This class uses the timeout on a per-connection basis, others use it on a per-action basis.
    42 
       
    43 		if ( ! defined( 'FS_TIMEOUT' ) ) {
    42 		if ( ! defined( 'FS_TIMEOUT' ) ) {
    44 			define( 'FS_TIMEOUT', 240 );
    43 			define( 'FS_TIMEOUT', 240 );
    45 		}
    44 		}
    46 
    45 
    47 		if ( empty( $opt['port'] ) ) {
    46 		if ( empty( $opt['port'] ) ) {
    68 		} else {
    67 		} else {
    69 			$this->options['password'] = $opt['password'];
    68 			$this->options['password'] = $opt['password'];
    70 		}
    69 		}
    71 
    70 
    72 		$this->options['ssl'] = false;
    71 		$this->options['ssl'] = false;
    73 		if ( isset( $opt['connection_type'] ) && 'ftps' == $opt['connection_type'] ) {
    72 
       
    73 		if ( isset( $opt['connection_type'] ) && 'ftps' === $opt['connection_type'] ) {
    74 			$this->options['ssl'] = true;
    74 			$this->options['ssl'] = true;
    75 		}
    75 		}
    76 	}
    76 	}
    77 
    77 
    78 	/**
    78 	/**
    90 		}
    90 		}
    91 
    91 
    92 		if ( ! $this->link ) {
    92 		if ( ! $this->link ) {
    93 			$this->errors->add(
    93 			$this->errors->add(
    94 				'connect',
    94 				'connect',
    95 				/* translators: %s: hostname:port */
       
    96 				sprintf(
    95 				sprintf(
       
    96 					/* translators: %s: hostname:port */
    97 					__( 'Failed to connect to FTP Server %s' ),
    97 					__( 'Failed to connect to FTP Server %s' ),
    98 					$this->options['hostname'] . ':' . $this->options['port']
    98 					$this->options['hostname'] . ':' . $this->options['port']
    99 				)
    99 				)
   100 			);
   100 			);
       
   101 
   101 			return false;
   102 			return false;
   102 		}
   103 		}
   103 
   104 
   104 		if ( ! @ftp_login( $this->link, $this->options['username'], $this->options['password'] ) ) {
   105 		if ( ! @ftp_login( $this->link, $this->options['username'], $this->options['password'] ) ) {
   105 			$this->errors->add(
   106 			$this->errors->add(
   106 				'auth',
   107 				'auth',
   107 				/* translators: %s: username */
       
   108 				sprintf(
   108 				sprintf(
       
   109 					/* translators: %s: Username. */
   109 					__( 'Username/Password incorrect for %s' ),
   110 					__( 'Username/Password incorrect for %s' ),
   110 					$this->options['username']
   111 					$this->options['username']
   111 				)
   112 				)
   112 			);
   113 			);
   113 			return false;
   114 
   114 		}
   115 			return false;
   115 
   116 		}
   116 		// Set the Connection to use Passive FTP
   117 
   117 		@ftp_pasv( $this->link, true );
   118 		// Set the connection to use Passive FTP.
       
   119 		ftp_pasv( $this->link, true );
       
   120 
   118 		if ( @ftp_get_option( $this->link, FTP_TIMEOUT_SEC ) < FS_TIMEOUT ) {
   121 		if ( @ftp_get_option( $this->link, FTP_TIMEOUT_SEC ) < FS_TIMEOUT ) {
   119 			@ftp_set_option( $this->link, FTP_TIMEOUT_SEC, FS_TIMEOUT );
   122 			@ftp_set_option( $this->link, FTP_TIMEOUT_SEC, FS_TIMEOUT );
   120 		}
   123 		}
   121 
   124 
   122 		return true;
   125 		return true;
   130 	 * @param string $file Name of the file to read.
   133 	 * @param string $file Name of the file to read.
   131 	 * @return string|false Read data on success, false if no temporary file could be opened,
   134 	 * @return string|false Read data on success, false if no temporary file could be opened,
   132 	 *                      or if the file couldn't be retrieved.
   135 	 *                      or if the file couldn't be retrieved.
   133 	 */
   136 	 */
   134 	public function get_contents( $file ) {
   137 	public function get_contents( $file ) {
   135 		$tempfile = wp_tempnam( $file );
   138 		$tempfile   = wp_tempnam( $file );
   136 		$temp     = fopen( $tempfile, 'w+' );
   139 		$temphandle = fopen( $tempfile, 'w+' );
   137 
   140 
   138 		if ( ! $temp ) {
   141 		if ( ! $temphandle ) {
   139 			unlink( $tempfile );
   142 			unlink( $tempfile );
   140 			return false;
   143 			return false;
   141 		}
   144 		}
   142 
   145 
   143 		if ( ! @ftp_fget( $this->link, $temp, $file, FTP_BINARY ) ) {
   146 		if ( ! ftp_fget( $this->link, $temphandle, $file, FTP_BINARY ) ) {
   144 			fclose( $temp );
   147 			fclose( $temphandle );
   145 			unlink( $tempfile );
   148 			unlink( $tempfile );
   146 			return false;
   149 			return false;
   147 		}
   150 		}
   148 
   151 
   149 		fseek( $temp, 0 ); // Skip back to the start of the file being written to
   152 		fseek( $temphandle, 0 ); // Skip back to the start of the file being written to.
   150 		$contents = '';
   153 		$contents = '';
   151 
   154 
   152 		while ( ! feof( $temp ) ) {
   155 		while ( ! feof( $temphandle ) ) {
   153 			$contents .= fread( $temp, 8 * KB_IN_BYTES );
   156 			$contents .= fread( $temphandle, 8 * KB_IN_BYTES );
   154 		}
   157 		}
   155 
   158 
   156 		fclose( $temp );
   159 		fclose( $temphandle );
   157 		unlink( $tempfile );
   160 		unlink( $tempfile );
       
   161 
   158 		return $contents;
   162 		return $contents;
   159 	}
   163 	}
   160 
   164 
   161 	/**
   165 	/**
   162 	 * Reads entire file into an array.
   166 	 * Reads entire file into an array.
   180 	 * @param int|false $mode     Optional. The file permissions as octal number, usually 0644.
   184 	 * @param int|false $mode     Optional. The file permissions as octal number, usually 0644.
   181 	 *                            Default false.
   185 	 *                            Default false.
   182 	 * @return bool True on success, false on failure.
   186 	 * @return bool True on success, false on failure.
   183 	 */
   187 	 */
   184 	public function put_contents( $file, $contents, $mode = false ) {
   188 	public function put_contents( $file, $contents, $mode = false ) {
   185 		$tempfile = wp_tempnam( $file );
   189 		$tempfile   = wp_tempnam( $file );
   186 		$temp     = fopen( $tempfile, 'wb+' );
   190 		$temphandle = fopen( $tempfile, 'wb+' );
   187 
   191 
   188 		if ( ! $temp ) {
   192 		if ( ! $temphandle ) {
   189 			unlink( $tempfile );
   193 			unlink( $tempfile );
   190 			return false;
   194 			return false;
   191 		}
   195 		}
   192 
   196 
   193 		mbstring_binary_safe_encoding();
   197 		mbstring_binary_safe_encoding();
   194 
   198 
   195 		$data_length   = strlen( $contents );
   199 		$data_length   = strlen( $contents );
   196 		$bytes_written = fwrite( $temp, $contents );
   200 		$bytes_written = fwrite( $temphandle, $contents );
   197 
   201 
   198 		reset_mbstring_encoding();
   202 		reset_mbstring_encoding();
   199 
   203 
   200 		if ( $data_length !== $bytes_written ) {
   204 		if ( $data_length !== $bytes_written ) {
   201 			fclose( $temp );
   205 			fclose( $temphandle );
   202 			unlink( $tempfile );
   206 			unlink( $tempfile );
   203 			return false;
   207 			return false;
   204 		}
   208 		}
   205 
   209 
   206 		fseek( $temp, 0 ); // Skip back to the start of the file being written to
   210 		fseek( $temphandle, 0 ); // Skip back to the start of the file being written to.
   207 
   211 
   208 		$ret = @ftp_fput( $this->link, $file, $temp, FTP_BINARY );
   212 		$ret = ftp_fput( $this->link, $file, $temphandle, FTP_BINARY );
   209 
   213 
   210 		fclose( $temp );
   214 		fclose( $temphandle );
   211 		unlink( $tempfile );
   215 		unlink( $tempfile );
   212 
   216 
   213 		$this->chmod( $file, $mode );
   217 		$this->chmod( $file, $mode );
   214 
   218 
   215 		return $ret;
   219 		return $ret;
   221 	 * @since 2.5.0
   225 	 * @since 2.5.0
   222 	 *
   226 	 *
   223 	 * @return string|false The current working directory on success, false on failure.
   227 	 * @return string|false The current working directory on success, false on failure.
   224 	 */
   228 	 */
   225 	public function cwd() {
   229 	public function cwd() {
   226 		$cwd = @ftp_pwd( $this->link );
   230 		$cwd = ftp_pwd( $this->link );
       
   231 
   227 		if ( $cwd ) {
   232 		if ( $cwd ) {
   228 			$cwd = trailingslashit( $cwd );
   233 			$cwd = trailingslashit( $cwd );
   229 		}
   234 		}
       
   235 
   230 		return $cwd;
   236 		return $cwd;
   231 	}
   237 	}
   232 
   238 
   233 	/**
   239 	/**
   234 	 * Changes current directory.
   240 	 * Changes current directory.
   248 	 * @since 2.5.0
   254 	 * @since 2.5.0
   249 	 *
   255 	 *
   250 	 * @param string    $file      Path to the file.
   256 	 * @param string    $file      Path to the file.
   251 	 * @param int|false $mode      Optional. The permissions as octal number, usually 0644 for files,
   257 	 * @param int|false $mode      Optional. The permissions as octal number, usually 0644 for files,
   252 	 *                             0755 for directories. Default false.
   258 	 *                             0755 for directories. Default false.
   253 	 * @param bool      $recursive Optional. If set to true, changes file group recursively.
   259 	 * @param bool      $recursive Optional. If set to true, changes file permissions recursively.
   254 	 *                             Default false.
   260 	 *                             Default false.
   255 	 * @return bool True on success, false on failure.
   261 	 * @return bool True on success, false on failure.
   256 	 */
   262 	 */
   257 	public function chmod( $file, $mode = false, $recursive = false ) {
   263 	public function chmod( $file, $mode = false, $recursive = false ) {
   258 		if ( ! $mode ) {
   264 		if ( ! $mode ) {
   266 		}
   272 		}
   267 
   273 
   268 		// chmod any sub-objects if recursive.
   274 		// chmod any sub-objects if recursive.
   269 		if ( $recursive && $this->is_dir( $file ) ) {
   275 		if ( $recursive && $this->is_dir( $file ) ) {
   270 			$filelist = $this->dirlist( $file );
   276 			$filelist = $this->dirlist( $file );
       
   277 
   271 			foreach ( (array) $filelist as $filename => $filemeta ) {
   278 			foreach ( (array) $filelist as $filename => $filemeta ) {
   272 				$this->chmod( $file . '/' . $filename, $mode, $recursive );
   279 				$this->chmod( $file . '/' . $filename, $mode, $recursive );
   273 			}
   280 			}
   274 		}
   281 		}
   275 
   282 
   276 		// chmod the file or directory
   283 		// chmod the file or directory.
   277 		if ( ! function_exists( 'ftp_chmod' ) ) {
   284 		if ( ! function_exists( 'ftp_chmod' ) ) {
   278 			return (bool) @ftp_site( $this->link, sprintf( 'CHMOD %o %s', $mode, $file ) );
   285 			return (bool) ftp_site( $this->link, sprintf( 'CHMOD %o %s', $mode, $file ) );
   279 		}
   286 		}
   280 		return (bool) @ftp_chmod( $this->link, $mode, $file );
   287 
       
   288 		return (bool) ftp_chmod( $this->link, $mode, $file );
   281 	}
   289 	}
   282 
   290 
   283 	/**
   291 	/**
   284 	 * Gets the file owner.
   292 	 * Gets the file owner.
   285 	 *
   293 	 *
   288 	 * @param string $file Path to the file.
   296 	 * @param string $file Path to the file.
   289 	 * @return string|false Username of the owner on success, false on failure.
   297 	 * @return string|false Username of the owner on success, false on failure.
   290 	 */
   298 	 */
   291 	public function owner( $file ) {
   299 	public function owner( $file ) {
   292 		$dir = $this->dirlist( $file );
   300 		$dir = $this->dirlist( $file );
       
   301 
   293 		return $dir[ $file ]['owner'];
   302 		return $dir[ $file ]['owner'];
   294 	}
   303 	}
   295 
   304 
   296 	/**
   305 	/**
   297 	 * Gets the permissions of the specified file or filepath in their octal format.
   306 	 * Gets the permissions of the specified file or filepath in their octal format.
   301 	 * @param string $file Path to the file.
   310 	 * @param string $file Path to the file.
   302 	 * @return string Mode of the file (the last 3 digits).
   311 	 * @return string Mode of the file (the last 3 digits).
   303 	 */
   312 	 */
   304 	public function getchmod( $file ) {
   313 	public function getchmod( $file ) {
   305 		$dir = $this->dirlist( $file );
   314 		$dir = $this->dirlist( $file );
       
   315 
   306 		return $dir[ $file ]['permsn'];
   316 		return $dir[ $file ]['permsn'];
   307 	}
   317 	}
   308 
   318 
   309 	/**
   319 	/**
   310 	 * Gets the file's group.
   320 	 * Gets the file's group.
   314 	 * @param string $file Path to the file.
   324 	 * @param string $file Path to the file.
   315 	 * @return string|false The group on success, false on failure.
   325 	 * @return string|false The group on success, false on failure.
   316 	 */
   326 	 */
   317 	public function group( $file ) {
   327 	public function group( $file ) {
   318 		$dir = $this->dirlist( $file );
   328 		$dir = $this->dirlist( $file );
       
   329 
   319 		return $dir[ $file ]['group'];
   330 		return $dir[ $file ]['group'];
   320 	}
   331 	}
   321 
   332 
   322 	/**
   333 	/**
   323 	 * Copies a file.
   334 	 * Copies a file.
   334 	 */
   345 	 */
   335 	public function copy( $source, $destination, $overwrite = false, $mode = false ) {
   346 	public function copy( $source, $destination, $overwrite = false, $mode = false ) {
   336 		if ( ! $overwrite && $this->exists( $destination ) ) {
   347 		if ( ! $overwrite && $this->exists( $destination ) ) {
   337 			return false;
   348 			return false;
   338 		}
   349 		}
       
   350 
   339 		$content = $this->get_contents( $source );
   351 		$content = $this->get_contents( $source );
       
   352 
   340 		if ( false === $content ) {
   353 		if ( false === $content ) {
   341 			return false;
   354 			return false;
   342 		}
   355 		}
       
   356 
   343 		return $this->put_contents( $destination, $content, $mode );
   357 		return $this->put_contents( $destination, $content, $mode );
   344 	}
   358 	}
   345 
   359 
   346 	/**
   360 	/**
   347 	 * Moves a file.
   361 	 * Moves a file.
   362 	 * Deletes a file or directory.
   376 	 * Deletes a file or directory.
   363 	 *
   377 	 *
   364 	 * @since 2.5.0
   378 	 * @since 2.5.0
   365 	 *
   379 	 *
   366 	 * @param string       $file      Path to the file or directory.
   380 	 * @param string       $file      Path to the file or directory.
   367 	 * @param bool         $recursive Optional. If set to true, changes file group recursively.
   381 	 * @param bool         $recursive Optional. If set to true, deletes files and folders recursively.
   368 	 *                                Default false.
   382 	 *                                Default false.
   369 	 * @param string|false $type      Type of resource. 'f' for file, 'd' for directory.
   383 	 * @param string|false $type      Type of resource. 'f' for file, 'd' for directory.
   370 	 *                                Default false.
   384 	 *                                Default false.
   371 	 * @return bool True on success, false on failure.
   385 	 * @return bool True on success, false on failure.
   372 	 */
   386 	 */
   373 	public function delete( $file, $recursive = false, $type = false ) {
   387 	public function delete( $file, $recursive = false, $type = false ) {
   374 		if ( empty( $file ) ) {
   388 		if ( empty( $file ) ) {
   375 			return false;
   389 			return false;
   376 		}
   390 		}
   377 		if ( 'f' == $type || $this->is_file( $file ) ) {
   391 
   378 			return @ftp_delete( $this->link, $file );
   392 		if ( 'f' === $type || $this->is_file( $file ) ) {
   379 		}
   393 			return ftp_delete( $this->link, $file );
       
   394 		}
       
   395 
   380 		if ( ! $recursive ) {
   396 		if ( ! $recursive ) {
   381 			return @ftp_rmdir( $this->link, $file );
   397 			return ftp_rmdir( $this->link, $file );
   382 		}
   398 		}
   383 
   399 
   384 		$filelist = $this->dirlist( trailingslashit( $file ) );
   400 		$filelist = $this->dirlist( trailingslashit( $file ) );
       
   401 
   385 		if ( ! empty( $filelist ) ) {
   402 		if ( ! empty( $filelist ) ) {
   386 			foreach ( $filelist as $delete_file ) {
   403 			foreach ( $filelist as $delete_file ) {
   387 				$this->delete( trailingslashit( $file ) . $delete_file['name'], $recursive, $delete_file['type'] );
   404 				$this->delete( trailingslashit( $file ) . $delete_file['name'], $recursive, $delete_file['type'] );
   388 			}
   405 			}
   389 		}
   406 		}
   390 		return @ftp_rmdir( $this->link, $file );
   407 
       
   408 		return ftp_rmdir( $this->link, $file );
   391 	}
   409 	}
   392 
   410 
   393 	/**
   411 	/**
   394 	 * Checks if a file or directory exists.
   412 	 * Checks if a file or directory exists.
   395 	 *
   413 	 *
   397 	 *
   415 	 *
   398 	 * @param string $file Path to file or directory.
   416 	 * @param string $file Path to file or directory.
   399 	 * @return bool Whether $file exists or not.
   417 	 * @return bool Whether $file exists or not.
   400 	 */
   418 	 */
   401 	public function exists( $file ) {
   419 	public function exists( $file ) {
   402 		$list = @ftp_nlist( $this->link, $file );
   420 		$list = ftp_nlist( $this->link, $file );
   403 
   421 
   404 		if ( empty( $list ) && $this->is_dir( $file ) ) {
   422 		if ( empty( $list ) && $this->is_dir( $file ) ) {
   405 			return true; // File is an empty directory.
   423 			return true; // File is an empty directory.
   406 		}
   424 		}
   407 
   425 
   408 		return ! empty( $list ); //empty list = no file, so invert.
   426 		return ! empty( $list ); // Empty list = no file, so invert.
   409 	}
   427 	}
   410 
   428 
   411 	/**
   429 	/**
   412 	 * Checks if resource is a file.
   430 	 * Checks if resource is a file.
   413 	 *
   431 	 *
   429 	 * @return bool Whether $path is a directory.
   447 	 * @return bool Whether $path is a directory.
   430 	 */
   448 	 */
   431 	public function is_dir( $path ) {
   449 	public function is_dir( $path ) {
   432 		$cwd    = $this->cwd();
   450 		$cwd    = $this->cwd();
   433 		$result = @ftp_chdir( $this->link, trailingslashit( $path ) );
   451 		$result = @ftp_chdir( $this->link, trailingslashit( $path ) );
       
   452 
   434 		if ( $result && $path == $this->cwd() || $this->cwd() != $cwd ) {
   453 		if ( $result && $path == $this->cwd() || $this->cwd() != $cwd ) {
   435 			@ftp_chdir( $this->link, $cwd );
   454 			@ftp_chdir( $this->link, $cwd );
   436 			return true;
   455 			return true;
   437 		}
   456 		}
       
   457 
   438 		return false;
   458 		return false;
   439 	}
   459 	}
   440 
   460 
   441 	/**
   461 	/**
   442 	 * Checks if a file is readable.
   462 	 * Checks if a file is readable.
   530 	 *                          Default false.
   550 	 *                          Default false.
   531 	 * @return bool True on success, false on failure.
   551 	 * @return bool True on success, false on failure.
   532 	 */
   552 	 */
   533 	public function mkdir( $path, $chmod = false, $chown = false, $chgrp = false ) {
   553 	public function mkdir( $path, $chmod = false, $chown = false, $chgrp = false ) {
   534 		$path = untrailingslashit( $path );
   554 		$path = untrailingslashit( $path );
       
   555 
   535 		if ( empty( $path ) ) {
   556 		if ( empty( $path ) ) {
   536 			return false;
   557 			return false;
   537 		}
   558 		}
   538 
   559 
   539 		if ( ! @ftp_mkdir( $this->link, $path ) ) {
   560 		if ( ! ftp_mkdir( $this->link, $path ) ) {
   540 			return false;
   561 			return false;
   541 		}
   562 		}
       
   563 
   542 		$this->chmod( $path, $chmod );
   564 		$this->chmod( $path, $chmod );
       
   565 
   543 		return true;
   566 		return true;
   544 	}
   567 	}
   545 
   568 
   546 	/**
   569 	/**
   547 	 * Deletes a directory.
   570 	 * Deletes a directory.
   556 	public function rmdir( $path, $recursive = false ) {
   579 	public function rmdir( $path, $recursive = false ) {
   557 		return $this->delete( $path, $recursive );
   580 		return $this->delete( $path, $recursive );
   558 	}
   581 	}
   559 
   582 
   560 	/**
   583 	/**
   561 	 * @staticvar bool $is_windows
       
   562 	 * @param string $line
   584 	 * @param string $line
   563 	 * @return array
   585 	 * @return array
   564 	 */
   586 	 */
   565 	public function parselisting( $line ) {
   587 	public function parselisting( $line ) {
   566 		static $is_windows = null;
   588 		static $is_windows = null;
       
   589 
   567 		if ( is_null( $is_windows ) ) {
   590 		if ( is_null( $is_windows ) ) {
   568 			$is_windows = stripos( ftp_systype( $this->link ), 'win' ) !== false;
   591 			$is_windows = stripos( ftp_systype( $this->link ), 'win' ) !== false;
   569 		}
   592 		}
   570 
   593 
   571 		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 ) ) {
   594 		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 ) ) {
   572 			$b = array();
   595 			$b = array();
       
   596 
   573 			if ( $lucifer[3] < 70 ) {
   597 			if ( $lucifer[3] < 70 ) {
   574 				$lucifer[3] += 2000;
   598 				$lucifer[3] += 2000;
   575 			} else {
   599 			} else {
   576 				$lucifer[3] += 1900; // 4digit year fix
   600 				$lucifer[3] += 1900; // 4-digit year fix.
   577 			}
   601 			}
   578 			$b['isdir'] = ( $lucifer[7] == '<DIR>' );
   602 
       
   603 			$b['isdir'] = ( '<DIR>' === $lucifer[7] );
       
   604 
   579 			if ( $b['isdir'] ) {
   605 			if ( $b['isdir'] ) {
   580 				$b['type'] = 'd';
   606 				$b['type'] = 'd';
   581 			} else {
   607 			} else {
   582 				$b['type'] = 'f';
   608 				$b['type'] = 'f';
   583 			}
   609 			}
       
   610 
   584 			$b['size']   = $lucifer[7];
   611 			$b['size']   = $lucifer[7];
   585 			$b['month']  = $lucifer[1];
   612 			$b['month']  = $lucifer[1];
   586 			$b['day']    = $lucifer[2];
   613 			$b['day']    = $lucifer[2];
   587 			$b['year']   = $lucifer[3];
   614 			$b['year']   = $lucifer[3];
   588 			$b['hour']   = $lucifer[4];
   615 			$b['hour']   = $lucifer[4];
   589 			$b['minute'] = $lucifer[5];
   616 			$b['minute'] = $lucifer[5];
   590 			$b['time']   = @mktime( $lucifer[4] + ( strcasecmp( $lucifer[6], 'PM' ) == 0 ? 12 : 0 ), $lucifer[5], 0, $lucifer[1], $lucifer[2], $lucifer[3] );
   617 			$b['time']   = mktime( $lucifer[4] + ( strcasecmp( $lucifer[6], 'PM' ) == 0 ? 12 : 0 ), $lucifer[5], 0, $lucifer[1], $lucifer[2], $lucifer[3] );
   591 			$b['am/pm']  = $lucifer[6];
   618 			$b['am/pm']  = $lucifer[6];
   592 			$b['name']   = $lucifer[8];
   619 			$b['name']   = $lucifer[8];
   593 		} elseif ( ! $is_windows && $lucifer = preg_split( '/[ ]/', $line, 9, PREG_SPLIT_NO_EMPTY ) ) {
   620 		} elseif ( ! $is_windows ) {
   594 			//echo $line."\n";
   621 			$lucifer = preg_split( '/[ ]/', $line, 9, PREG_SPLIT_NO_EMPTY );
   595 			$lcount = count( $lucifer );
   622 
   596 			if ( $lcount < 8 ) {
   623 			if ( $lucifer ) {
   597 				return '';
   624 				// echo $line."\n";
   598 			}
   625 				$lcount = count( $lucifer );
   599 			$b           = array();
   626 
   600 			$b['isdir']  = $lucifer[0]{0} === 'd';
   627 				if ( $lcount < 8 ) {
   601 			$b['islink'] = $lucifer[0]{0} === 'l';
   628 					return '';
   602 			if ( $b['isdir'] ) {
   629 				}
   603 				$b['type'] = 'd';
   630 
   604 			} elseif ( $b['islink'] ) {
   631 				$b           = array();
   605 				$b['type'] = 'l';
   632 				$b['isdir']  = 'd' === $lucifer[0][0];
   606 			} else {
   633 				$b['islink'] = 'l' === $lucifer[0][0];
   607 				$b['type'] = 'f';
   634 
   608 			}
   635 				if ( $b['isdir'] ) {
   609 			$b['perms']  = $lucifer[0];
   636 					$b['type'] = 'd';
   610 			$b['permsn'] = $this->getnumchmodfromh( $b['perms'] );
   637 				} elseif ( $b['islink'] ) {
   611 			$b['number'] = $lucifer[1];
   638 					$b['type'] = 'l';
   612 			$b['owner']  = $lucifer[2];
       
   613 			$b['group']  = $lucifer[3];
       
   614 			$b['size']   = $lucifer[4];
       
   615 			if ( $lcount == 8 ) {
       
   616 				sscanf( $lucifer[5], '%d-%d-%d', $b['year'], $b['month'], $b['day'] );
       
   617 				sscanf( $lucifer[6], '%d:%d', $b['hour'], $b['minute'] );
       
   618 				$b['time'] = @mktime( $b['hour'], $b['minute'], 0, $b['month'], $b['day'], $b['year'] );
       
   619 				$b['name'] = $lucifer[7];
       
   620 			} else {
       
   621 				$b['month'] = $lucifer[5];
       
   622 				$b['day']   = $lucifer[6];
       
   623 				if ( preg_match( '/([0-9]{2}):([0-9]{2})/', $lucifer[7], $l2 ) ) {
       
   624 					$b['year']   = date( 'Y' );
       
   625 					$b['hour']   = $l2[1];
       
   626 					$b['minute'] = $l2[2];
       
   627 				} else {
   639 				} else {
   628 					$b['year']   = $lucifer[7];
   640 					$b['type'] = 'f';
   629 					$b['hour']   = 0;
       
   630 					$b['minute'] = 0;
       
   631 				}
   641 				}
   632 				$b['time'] = strtotime( sprintf( '%d %s %d %02d:%02d', $b['day'], $b['month'], $b['year'], $b['hour'], $b['minute'] ) );
   642 
   633 				$b['name'] = $lucifer[8];
   643 				$b['perms']  = $lucifer[0];
   634 			}
   644 				$b['permsn'] = $this->getnumchmodfromh( $b['perms'] );
   635 		}
   645 				$b['number'] = $lucifer[1];
   636 
   646 				$b['owner']  = $lucifer[2];
   637 		// Replace symlinks formatted as "source -> target" with just the source name
   647 				$b['group']  = $lucifer[3];
       
   648 				$b['size']   = $lucifer[4];
       
   649 
       
   650 				if ( 8 == $lcount ) {
       
   651 					sscanf( $lucifer[5], '%d-%d-%d', $b['year'], $b['month'], $b['day'] );
       
   652 					sscanf( $lucifer[6], '%d:%d', $b['hour'], $b['minute'] );
       
   653 
       
   654 					$b['time'] = mktime( $b['hour'], $b['minute'], 0, $b['month'], $b['day'], $b['year'] );
       
   655 					$b['name'] = $lucifer[7];
       
   656 				} else {
       
   657 					$b['month'] = $lucifer[5];
       
   658 					$b['day']   = $lucifer[6];
       
   659 
       
   660 					if ( preg_match( '/([0-9]{2}):([0-9]{2})/', $lucifer[7], $l2 ) ) {
       
   661 						$b['year']   = gmdate( 'Y' );
       
   662 						$b['hour']   = $l2[1];
       
   663 						$b['minute'] = $l2[2];
       
   664 					} else {
       
   665 						$b['year']   = $lucifer[7];
       
   666 						$b['hour']   = 0;
       
   667 						$b['minute'] = 0;
       
   668 					}
       
   669 
       
   670 					$b['time'] = strtotime( sprintf( '%d %s %d %02d:%02d', $b['day'], $b['month'], $b['year'], $b['hour'], $b['minute'] ) );
       
   671 					$b['name'] = $lucifer[8];
       
   672 				}
       
   673 			}
       
   674 		}
       
   675 
       
   676 		// Replace symlinks formatted as "source -> target" with just the source name.
   638 		if ( isset( $b['islink'] ) && $b['islink'] ) {
   677 		if ( isset( $b['islink'] ) && $b['islink'] ) {
   639 			$b['name'] = preg_replace( '/(\s*->\s*.*)$/', '', $b['name'] );
   678 			$b['name'] = preg_replace( '/(\s*->\s*.*)$/', '', $b['name'] );
   640 		}
   679 		}
   641 
   680 
   642 		return $b;
   681 		return $b;
   673 			$path       = dirname( $path ) . '/';
   712 			$path       = dirname( $path ) . '/';
   674 		} else {
   713 		} else {
   675 			$limit_file = false;
   714 			$limit_file = false;
   676 		}
   715 		}
   677 
   716 
   678 		$pwd = @ftp_pwd( $this->link );
   717 		$pwd = ftp_pwd( $this->link );
       
   718 
   679 		if ( ! @ftp_chdir( $this->link, $path ) ) { // Can't change to folder = folder doesn't exist.
   719 		if ( ! @ftp_chdir( $this->link, $path ) ) { // Can't change to folder = folder doesn't exist.
   680 			return false;
   720 			return false;
   681 		}
   721 		}
   682 		$list = @ftp_rawlist( $this->link, '-a', false );
   722 
       
   723 		$list = ftp_rawlist( $this->link, '-a', false );
       
   724 
   683 		@ftp_chdir( $this->link, $pwd );
   725 		@ftp_chdir( $this->link, $pwd );
   684 
   726 
   685 		if ( empty( $list ) ) { // Empty array = non-existent folder (real folder will show . at least).
   727 		if ( empty( $list ) ) { // Empty array = non-existent folder (real folder will show . at least).
   686 			return false;
   728 			return false;
   687 		}
   729 		}
   688 
   730 
   689 		$dirlist = array();
   731 		$dirlist = array();
       
   732 
   690 		foreach ( $list as $k => $v ) {
   733 		foreach ( $list as $k => $v ) {
   691 			$entry = $this->parselisting( $v );
   734 			$entry = $this->parselisting( $v );
       
   735 
   692 			if ( empty( $entry ) ) {
   736 			if ( empty( $entry ) ) {
   693 				continue;
   737 				continue;
   694 			}
   738 			}
   695 
   739 
   696 			if ( '.' == $entry['name'] || '..' == $entry['name'] ) {
   740 			if ( '.' === $entry['name'] || '..' === $entry['name'] ) {
   697 				continue;
   741 				continue;
   698 			}
   742 			}
   699 
   743 
   700 			if ( ! $include_hidden && '.' == $entry['name'][0] ) {
   744 			if ( ! $include_hidden && '.' === $entry['name'][0] ) {
   701 				continue;
   745 				continue;
   702 			}
   746 			}
   703 
   747 
   704 			if ( $limit_file && $entry['name'] != $limit_file ) {
   748 			if ( $limit_file && $entry['name'] != $limit_file ) {
   705 				continue;
   749 				continue;
   707 
   751 
   708 			$dirlist[ $entry['name'] ] = $entry;
   752 			$dirlist[ $entry['name'] ] = $entry;
   709 		}
   753 		}
   710 
   754 
   711 		$ret = array();
   755 		$ret = array();
       
   756 
   712 		foreach ( (array) $dirlist as $struc ) {
   757 		foreach ( (array) $dirlist as $struc ) {
   713 			if ( 'd' == $struc['type'] ) {
   758 			if ( 'd' === $struc['type'] ) {
   714 				if ( $recursive ) {
   759 				if ( $recursive ) {
   715 					$struc['files'] = $this->dirlist( $path . '/' . $struc['name'], $include_hidden, $recursive );
   760 					$struc['files'] = $this->dirlist( $path . '/' . $struc['name'], $include_hidden, $recursive );
   716 				} else {
   761 				} else {
   717 					$struc['files'] = array();
   762 					$struc['files'] = array();
   718 				}
   763 				}
   719 			}
   764 			}
   720 
   765 
   721 			$ret[ $struc['name'] ] = $struc;
   766 			$ret[ $struc['name'] ] = $struc;
   722 		}
   767 		}
       
   768 
   723 		return $ret;
   769 		return $ret;
   724 	}
   770 	}
   725 
   771 
   726 	/**
   772 	/**
   727 	 * Destructor.
   773 	 * Destructor.