WordPress Version: 6.4
/**
* Displays a form to the user to request for their FTP/SSH details in order
* to connect to the filesystem.
*
* All chosen/entered details are saved, excluding the password.
*
* Hostnames may be in the form of hostname:portnumber (eg: wordpress.org:2467)
* to specify an alternate FTP/SSH port.
*
* Plugins may override this form by returning true|false via the {@see 'request_filesystem_credentials'} filter.
*
* @since 2.5.0
* @since 4.6.0 The `$context` parameter default changed from `false` to an empty string.
*
* @global string $pagenow The filename of the current screen.
*
* @param string $form_post The URL to post the form to.
* @param string $type Optional. Chosen type of filesystem. Default empty.
* @param bool|WP_Error $error Optional. Whether the current request has failed
* to connect, or an error object. Default false.
* @param string $context Optional. Full path to the directory that is tested
* for being writable. Default empty.
* @param array $extra_fields Optional. Extra `POST` fields to be checked
* for inclusion in the post. Default null.
* @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable.
* Default false.
* @return bool|array True if no filesystem credentials are required,
* false if they are required but have not been provided,
* array of credentials if they are required and have been provided.
*/
function request_filesystem_credentials($form_post, $type = '', $error = false, $context = '', $extra_fields = null, $allow_relaxed_file_ownership = false)
{
global $pagenow;
/**
* Filters the filesystem credentials.
*
* Returning anything other than an empty string will effectively short-circuit
* output of the filesystem credentials form, returning that value instead.
*
* A filter should return true if no filesystem credentials are required, false if they are required but have not been
* provided, or an array of credentials if they are required and have been provided.
*
* @since 2.5.0
* @since 4.6.0 The `$context` parameter default changed from `false` to an empty string.
*
* @param mixed $credentials Credentials to return instead. Default empty string.
* @param string $form_post The URL to post the form to.
* @param string $type Chosen type of filesystem.
* @param bool|WP_Error $error Whether the current request has failed to connect,
* or an error object.
* @param string $context Full path to the directory that is tested for
* being writable.
* @param array $extra_fields Extra POST fields.
* @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable.
*/
$req_cred = apply_filters('request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership);
if ('' !== $req_cred) {
return $req_cred;
}
if (empty($type)) {
$type = get_filesystem_method(array(), $context, $allow_relaxed_file_ownership);
}
if ('direct' === $type) {
return true;
}
if (is_null($extra_fields)) {
$extra_fields = array('version', 'locale');
}
$credentials = get_option('ftp_credentials', array('hostname' => '', 'username' => ''));
$submitted_form = wp_unslash($_POST);
// Verify nonce, or unset submitted form field values on failure.
if (!isset($_POST['_fs_nonce']) || !wp_verify_nonce($_POST['_fs_nonce'], 'filesystem-credentials')) {
unset($submitted_form['hostname'], $submitted_form['username'], $submitted_form['password'], $submitted_form['public_key'], $submitted_form['private_key'], $submitted_form['connection_type']);
}
$ftp_constants = array('hostname' => 'FTP_HOST', 'username' => 'FTP_USER', 'password' => 'FTP_PASS', 'public_key' => 'FTP_PUBKEY', 'private_key' => 'FTP_PRIKEY');
/*
* If defined, set it to that. Else, if POST'd, set it to that. If not, set it to an empty string.
* Otherwise, keep it as it previously was (saved details in option).
*/
foreach ($ftp_constants as $key => $constant) {
if (defined($constant)) {
$credentials[$key] = constant($constant);
} elseif (!empty($submitted_form[$key])) {
$credentials[$key] = $submitted_form[$key];
} elseif (!isset($credentials[$key])) {
$credentials[$key] = '';
}
}
// Sanitize the hostname, some people might pass in odd data.
$credentials['hostname'] = preg_replace('|\w+://|', '', $credentials['hostname']);
// Strip any schemes off.
if (strpos($credentials['hostname'], ':')) {
list($credentials['hostname'], $credentials['port']) = explode(':', $credentials['hostname'], 2);
if (!is_numeric($credentials['port'])) {
unset($credentials['port']);
}
} else {
unset($credentials['port']);
}
if (defined('FTP_SSH') && FTP_SSH || defined('FS_METHOD') && 'ssh2' === FS_METHOD) {
$credentials['connection_type'] = 'ssh';
} elseif (defined('FTP_SSL') && FTP_SSL && 'ftpext' === $type) {
// Only the FTP Extension understands SSL.
$credentials['connection_type'] = 'ftps';
} elseif (!empty($submitted_form['connection_type'])) {
$credentials['connection_type'] = $submitted_form['connection_type'];
} elseif (!isset($credentials['connection_type'])) {
// All else fails (and it's not defaulted to something else saved), default to FTP.
$credentials['connection_type'] = 'ftp';
}
if (!$error && (!empty($credentials['hostname']) && !empty($credentials['username']) && !empty($credentials['password']) || 'ssh' === $credentials['connection_type'] && !empty($credentials['public_key']) && !empty($credentials['private_key']))) {
$stored_credentials = $credentials;
if (!empty($stored_credentials['port'])) {
// Save port as part of hostname to simplify above code.
$stored_credentials['hostname'] .= ':' . $stored_credentials['port'];
}
unset($stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key']);
if (!wp_installing()) {
update_option('ftp_credentials', $stored_credentials);
}
return $credentials;
}
$hostname = isset($credentials['hostname']) ? $credentials['hostname'] : '';
$username = isset($credentials['username']) ? $credentials['username'] : '';
$public_key = isset($credentials['public_key']) ? $credentials['public_key'] : '';
$private_key = isset($credentials['private_key']) ? $credentials['private_key'] : '';
$port = isset($credentials['port']) ? $credentials['port'] : '';
$connection_type = isset($credentials['connection_type']) ? $credentials['connection_type'] : '';
if ($error) {
$error_string = __('<strong>Error:</strong> Could not connect to the server. Please verify the settings are correct.');
if (is_wp_error($error)) {
$error_string = esc_html($error->get_error_message());
}
wp_admin_notice($error_string, array('id' => 'message', 'additional_classes' => array('error')));
}
$types = array();
if (extension_loaded('ftp') || extension_loaded('sockets') || function_exists('fsockopen')) {
$types['ftp'] = __('FTP');
}
if (extension_loaded('ftp')) {
// Only this supports FTPS.
$types['ftps'] = __('FTPS (SSL)');
}
if (extension_loaded('ssh2')) {
$types['ssh'] = __('SSH2');
}
/**
* Filters the connection types to output to the filesystem credentials form.
*
* @since 2.9.0
* @since 4.6.0 The `$context` parameter default changed from `false` to an empty string.
*
* @param string[] $types Types of connections.
* @param array $credentials Credentials to connect with.
* @param string $type Chosen filesystem method.
* @param bool|WP_Error $error Whether the current request has failed to connect,
* or an error object.
* @param string $context Full path to the directory that is tested for being writable.
*/
$types = apply_filters('fs_ftp_connection_types', $types, $credentials, $type, $error, $context);
?>
<form action="<?php
echo esc_url($form_post);
?>" method="post">
<div id="request-filesystem-credentials-form" class="request-filesystem-credentials-form">
<?php
// Print a H1 heading in the FTP credentials modal dialog, default is a H2.
$heading_tag = 'h2';
if ('plugins.php' === $pagenow || 'plugin-install.php' === $pagenow) {
$heading_tag = 'h1';
}
echo "<{$heading_tag} id='request-filesystem-credentials-title'>" . __('Connection Information') . "</{$heading_tag}>";
?>
<p id="request-filesystem-credentials-desc">
<?php
$label_user = __('Username');
$label_pass = __('Password');
_e('To perform the requested action, WordPress needs to access your web server.');
echo ' ';
if (isset($types['ftp']) || isset($types['ftps'])) {
if (isset($types['ssh'])) {
_e('Please enter your FTP or SSH credentials to proceed.');
$label_user = __('FTP/SSH Username');
$label_pass = __('FTP/SSH Password');
} else {
_e('Please enter your FTP credentials to proceed.');
$label_user = __('FTP Username');
$label_pass = __('FTP Password');
}
echo ' ';
}
_e('If you do not remember your credentials, you should contact your web host.');
$hostname_value = esc_attr($hostname);
if (!empty($port)) {
$hostname_value .= ":{$port}";
}
$password_value = '';
if (defined('FTP_PASS')) {
$password_value = '*****';
}
?>
</p>
<label for="hostname">
<span class="field-title"><?php
_e('Hostname');
?></span>
<input name="hostname" type="text" id="hostname" aria-describedby="request-filesystem-credentials-desc" class="code" placeholder="<?php
esc_attr_e('example: www.wordpress.org');
?>" value="<?php
echo $hostname_value;
?>"<?php
disabled(defined('FTP_HOST'));
?> />
</label>
<div class="ftp-username">
<label for="username">
<span class="field-title"><?php
echo $label_user;
?></span>
<input name="username" type="text" id="username" value="<?php
echo esc_attr($username);
?>"<?php
disabled(defined('FTP_USER'));
?> />
</label>
</div>
<div class="ftp-password">
<label for="password">
<span class="field-title"><?php
echo $label_pass;
?></span>
<input name="password" type="password" id="password" value="<?php
echo $password_value;
?>"<?php
disabled(defined('FTP_PASS'));
?> spellcheck="false" />
<?php
if (!defined('FTP_PASS')) {
_e('This password will not be stored on the server.');
}
?>
</label>
</div>
<fieldset>
<legend><?php
_e('Connection Type');
?></legend>
<?php
$disabled = disabled(defined('FTP_SSL') && FTP_SSL || defined('FTP_SSH') && FTP_SSH, true, false);
foreach ($types as $name => $text) {
?>
<label for="<?php
echo esc_attr($name);
?>">
<input type="radio" name="connection_type" id="<?php
echo esc_attr($name);
?>" value="<?php
echo esc_attr($name);
?>" <?php
checked($name, $connection_type);
?> <?php
echo $disabled;
?> />
<?php
echo $text;
?>
</label>
<?php
}
?>
</fieldset>
<?php
if (isset($types['ssh'])) {
$hidden_class = '';
if ('ssh' !== $connection_type || empty($connection_type)) {
$hidden_class = ' class="hidden"';
}
?>
<fieldset id="ssh-keys"<?php
echo $hidden_class;
?>>
<legend><?php
_e('Authentication Keys');
?></legend>
<label for="public_key">
<span class="field-title"><?php
_e('Public Key:');
?></span>
<input name="public_key" type="text" id="public_key" aria-describedby="auth-keys-desc" value="<?php
echo esc_attr($public_key);
?>"<?php
disabled(defined('FTP_PUBKEY'));
?> />
</label>
<label for="private_key">
<span class="field-title"><?php
_e('Private Key:');
?></span>
<input name="private_key" type="text" id="private_key" value="<?php
echo esc_attr($private_key);
?>"<?php
disabled(defined('FTP_PRIKEY'));
?> />
</label>
<p id="auth-keys-desc"><?php
_e('Enter the location on the server where the public and private keys are located. If a passphrase is needed, enter that in the password field above.');
?></p>
</fieldset>
<?php
}
foreach ((array) $extra_fields as $field) {
if (isset($submitted_form[$field])) {
echo '<input type="hidden" name="' . esc_attr($field) . '" value="' . esc_attr($submitted_form[$field]) . '" />';
}
}
/*
* Make sure the `submit_button()` function is available during the REST API call
* from WP_Site_Health_Auto_Updates::test_check_wp_filesystem_method().
*/
if (!function_exists('submit_button')) {
require_once ABSPATH . 'wp-admin/includes/template.php';
}
?>
<p class="request-filesystem-credentials-action-buttons">
<?php
wp_nonce_field('filesystem-credentials', '_fs_nonce', false, true);
?>
<button class="button cancel-button" data-js-action="close" type="button"><?php
_e('Cancel');
?></button>
<?php
submit_button(__('Proceed'), '', 'upgrade', false);
?>
</p>
</div>
</form>
<?php
return false;
}