From 0ce4afa786d0c2bf13607faf409bab3ff4021d0c Mon Sep 17 00:00:00 2001 From: Joaquim Homrighausen Date: Wed, 10 Mar 2021 12:12:36 +0100 Subject: [PATCH] 1.1.0, with verified WordPress 5.7 compatibility --- README.md | 30 +- fail2wp/fail2wp.conf | 21 +- fail2wp/fail2wp.php | 1675 ++++++++++++++++++++++--- fail2wp/includes/fail2wp_misc.inc.php | 105 ++ fail2wp/languages/fail2wp-sv_SE.mo | Bin 5835 -> 14600 bytes fail2wp/languages/fail2wp-sv_SE.po | 462 ++++++- fail2wp/languages/fail2wp.pot | 423 ++++++- fail2wp/uninstall.php | 42 +- 8 files changed, 2426 insertions(+), 332 deletions(-) create mode 100644 fail2wp/includes/fail2wp_misc.inc.php diff --git a/README.md b/README.md index 297b03f..08f6325 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -[![Software License](https://img.shields.io/badge/License-GPL%20v2-green.svg?style=flat-square)](LICENSE) [![PHP 7.2\+](https://img.shields.io/badge/PHP-7.2-blue?style=flat-square)](https://php.net) [![WordPress 5](https://img.shields.io/badge/WordPress-5.6-orange?style=flat-square)](https://wordpress.org) +[![Software License](https://img.shields.io/badge/License-GPL%20v2-green.svg?style=flat-square)](LICENSE) [![PHP 7.2\+](https://img.shields.io/badge/PHP-7.2-blue?style=flat-square)](https://php.net) [![WordPress 5](https://img.shields.io/badge/WordPress-5.7-orange?style=flat-square)](https://wordpress.org) # Fail2WP -Security plugin for WordPress with support for Fail2ban and Cloudflare. Tested with WordPress 5.6+. +Security plugin for WordPress with support for Fail2ban and Cloudflare. Tested with WordPress 5.5+. ## Description @@ -17,14 +17,21 @@ Basic security functionality includes: * Disabling login with username (require e-mail address) * Preventing user enumeration (?author=nnn) * Less detailed error messages on login failures +* Minimum username length +* Blocking specific usernames from being used to register new users +* Requiring e-mail address matching for new user registrations +* Warning about new user role setting +* Blocking of portions or all of WordPress REST API +* Disabling of RSS and Atom feeds +* Removal of "Generator" information from HTML and feeds * Detection of Cloudflare IP addresses for logging of actual IP addresses The plugin also plays nicely with Fail2ban, which is an advanced way of blocking IP addresses dynamically upon suspicious behavior. ### Other notes -* This plugin may work with earlier versions of WordPress -* This plugin has been tested with `WordPress 5.5.3`, `5.6`, and `5.6.1` at the time of this writing +* This plugin `may` work with earlier versions of WordPress +* This plugin has been tested with `WordPress 5.5+` at the time of this writing * This plugin optionally makes use of `mb_` PHP functions * This plugin may create entries in your PHP error log (if active) * This plugin contains no Javascript @@ -59,11 +66,24 @@ This is a hard question to answer. There are no known incompatibilities. ## Changelog +### 1.1.0 +* Added minimum username length +* Added blocking of specific usernames (user registration) +* Added requiring e-mail address matching setting +* Added warning about new user role setting +* Added blocking of portions or all of WordPress REST API +* Added setting to disable RSS and Atom feeds +* Added setting to remove "Generator" information from HTML and feeds +* Minor corrections and general improvements + ### 1.0.0 * Initial release ## Upgrade Notice +### 1.1.0 +* Install the new version and walk through the settings. + ### 1.0.0 * Initial release @@ -101,6 +121,8 @@ If there is something you feel to be missing from this plugin, or if you have fo This plugin can also be downloaded from [code.webbplatsen.net](https://code.webbplatsen.net/wordpress/fail2wp/) and [WordPress.org](https://wordpress.org/plugins/fail2wp/) +More detailed documentation is available at [code.webbplatsen.net/documentation/fail2wp/](https://code.webbplatsen.net/documentation/fail2wp/) + Kudos to [Vincent Le Moign and Webalys](https://webalys.com) and [Thomas Lutz](https://github.com/tholu) ### External references diff --git a/fail2wp/fail2wp.conf b/fail2wp/fail2wp.conf index f7cf3a8..1edf6bb 100644 --- a/fail2wp/fail2wp.conf +++ b/fail2wp/fail2wp.conf @@ -2,23 +2,37 @@ before = common.conf after = fail2wp.local -# This filter is intended to be used with fail2ban and the Fail2WP plugin. +# This filter is intended to be used with Fail2ban and the Fail2WP plugin. # # This file should be placed in /etc/fail2ban/filter.d as fail2wp.conf # # I'm by no means a fail2ban filter expert, so I'm sure this could do with some -# improvements. It has been tested with fail2ban 0.11.1 on Ubuntu 20.04.LTS. +# improvements. It has been tested with Fail2ban 0.11.1 on Ubuntu 20.04.LTS. +# +# ADVANCED: You may, of course, split these into several different jails and +# triggers and give them different treatment in Fail2ban so that some +# of the log messages trigger one behavior in Fail2ban, and others +# are ignored or behave differently. # # Joaquim Homrighausen # # The intended log messages to trigger fail2ban on are: # +# @since 1.0.0 +# # Authentication failure for validuser from n.n.n.n port 443 # Invalid email invalidemail from n.n.n.n port 443 # Invalid user invaliduser from n.n.n.n port 443 # User enumeration request from n.n.n.n port 443 # Invalid credentials invalidlogin from n.n.n.n port 443 # +# @since 1.1.0 +# +# Blocked REST API request from n.n.n.n port 443 +# Unauthenticated REST API request from n.n.n.n port 443 +# +# Other messages: +# # The Fail2WP plugin can further emit these messages (no action taken): # # Unknown error "nnn" during login from n.n.n.n port nnn @@ -31,7 +45,8 @@ failregex = fail2wp(.*): Authentication failure for .* from port .*$ fail2wp(.*): Invalid user .* from port .*$ fail2wp(.*): Invalid email .* from port .*$ fail2wp(.*): Invalid credentials .* from port .*$ - fail2wp(.*): User enumeration request from port .*$ + fail2wp(.*): Blocked REST API request from from port .*$ + fail2wp(.*): Unauthenticated REST API request from port .*$ # Your entry for Fail2WP in jail.local should look like this: # diff --git a/fail2wp/fail2wp.php b/fail2wp/fail2wp.php index 49ec040..d4fdef9 100644 --- a/fail2wp/fail2wp.php +++ b/fail2wp/fail2wp.php @@ -11,12 +11,12 @@ * Plugin Name: Fail2WP * Plugin URI: https://code.webbplatsen.net/wordpress/fail2wp/ * Description: Security plugin for WordPress with support for fail2ban - * Version: 1.0.0 + * Version: 1.1.0 * Author: WebbPlatsen, Joaquim Homrighausen * Author URI: https://webbplatsen.se/ * License: GPL-2.0+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt - * Text Domain: playground + * Text Domain: fail2wp * Domain Path: /languages * * fail2wp.php @@ -42,7 +42,6 @@ */ namespace fail2wp; - // If this file is called directly, abort. if ( ! defined( 'WPINC' ) ) { die; @@ -51,16 +50,28 @@ die( '-1' ); } -define( 'FAIL2WP_VERSION', '1.0.0' ); -define( 'FAIL2WP_REV', 1 ); -define( 'FAIL2WP_PLUGINNAME_HUMAN', 'Fail2WP' ); -define( 'FAIL2WP_PLUGINNAME_SLUG', 'fail2wp' ); -define( 'FAIL2WP_DEFAULT_PREFIX', 'fail2wp' ); -define( 'FAIL2WP_ALERT_SUCCESS', 1 ); -define( 'FAIL2WP_ALERT_FAILURE', 2 ); -define( 'FAIL2WP_ALERT_USER_ENUM', 3 ); -define( 'FAIL2WP_DEFAULT_HTTP_PORT', 80 ); -define( 'FAIL2WP_DEFAULT_HTTPS_PORT', 443 ); +define( 'FAIL2WP_WORDPRESS_PLUGIN', true ); +define( 'FAIL2WP_VERSION', '1.1.0' ); +define( 'FAIL2WP_REV', 1 ); +define( 'FAIL2WP_PLUGINNAME_HUMAN', 'Fail2WP' ); +define( 'FAIL2WP_PLUGINNAME_SLUG', 'fail2wp' ); +define( 'FAIL2WP_DEFAULT_PREFIX', 'fail2wp' ); +define( 'FAIL2WP_ALERT_SUCCESS', 1 ); +define( 'FAIL2WP_ALERT_FAILURE', 2 ); +define( 'FAIL2WP_ALERT_USER_ENUM', 3 ); +define( 'FAIL2WP_ALERT_REST_NOTAUTH', 4 ); +define( 'FAIL2WP_ALERT_REST_BLOCKED', 5 ); +define( 'FAIL2WP_DEFAULT_HTTP_PORT', 80 ); +define( 'FAIL2WP_DEFAULT_HTTPS_PORT', 443 ); +define( 'FAIL2WP_DB_VERSION', 2 ); +define( 'FAIL2WP_EXPORT_HEADER', 'fail2wp_export.begin.' ); +define( 'FAIL2WP_EXPORT_FOOTER', '.fail2wp_export.end' ); + +// define( 'FAIL2WP_DEBUG', true ); +if ( defined( 'FAIL2WP_DEBUG' ) ) { + define( 'FAIL2WP_REST_DEBUG', true ); + define( 'FAIL2WP_DUMP_SETTINGS', true ); +} require_once plugin_dir_path( __FILE__ ) . 'includes/class-fail2wp-syslog.php'; @@ -73,7 +84,10 @@ class Fail2WP { public static $instance = null; protected $plugin_name; - protected $version; + protected $fail2wp_plugin_version; + protected $fail2wp_mail_headers; // @since 1.1.0 + protected $fail2wp_have_mbstring; // @since 1.1.0 + protected $fail2wp_wp_roles = null; protected $fail2wp_wp_roles_enus = null; protected $fail2wp_settings_tab = ''; @@ -81,7 +95,10 @@ class Fail2WP { protected $fail2wp_roles_notify; protected $fail2wp_roles_warn; protected $fail2wp_unknown_warn; + protected $fail2wp_settings_dbversion; // @since 1.1.0 protected $fail2wp_settings_remove; + protected $fail2wp_settings_remove_generator; // @since 1.1.0 + protected $fail2wp_settings_remove_feeds; // @since 1.1.0 protected $fail2wp_also_log_php; protected $fail2wp_block_user_enum; protected $fail2wp_block_username_login; @@ -92,10 +109,29 @@ class Fail2WP { protected $fail2wp_cloudflare_ipv4; protected $fail2wp_cloudflare_ipv6; + protected $fail2wp_reguser_warn; // @since 1.1.0 + protected $fail2wp_reguser_warn_role; // @since 1.1.0 + protected $fail2wp_reguser_force; // @since 1.1.0 + protected $fail2wp_reguser_force_role; // @since 1.1.0 + protected $fail2wp_reguser_username_length; // @since 1.1.0 + protected $fail2wp_reguser_username_ban; // @since 1.1.0 + protected $fail2wp_reguser_useremail_require; // @since 1.1.0 + + protected $fail2wp_rest = null; // @since 1.1.0 + protected $fail2wp_rest_filter_log_blocked; // @since 1.1.0 + protected $fail2wp_rest_filter_block_all; // @since 1.1.0 + protected $fail2wp_rest_filter_block_index; // @since 1.1.0 + protected $fail2wp_rest_filter_block_ns; // @since 1.1.0 + protected $fail2wp_rest_filter_block_routes; // @since 1.1.0 + protected $fail2wp_rest_filter_route_list; // @since 1.1.0 + protected $fail2wp_rest_filter_require_authenticated; // @since 1.1.0 + protected $fail2wp_rest_filter_ipv4_bypass; // @since 1.1.0 + protected $fail2wp_rest_filter_ipv6_bypass; // @since 1.1.0 + public static function getInstance( string $version = '', string $slug = '' ) { null === self::$instance AND self::$instance = new self( $version, $slug ); - return self::$instance; + return( self::$instance ); } /** * Start me up ... @@ -103,53 +139,807 @@ public static function getInstance( string $version = '', string $slug = '' ) public function __construct( string $version = '', string $slug = '' ) { if ( empty( $version ) ) { if ( defined( 'FAIL2WP_VERSION' ) ) { - $this->version = FAIL2WP_VERSION; + $this->fail2wp_plugin_version = FAIL2WP_VERSION; } else { - $this->version = '0.0.1'; + $this->fail2wp_plugin_version = '1.0.0'; } } else { - $this->version = $version; + $this->fail2wp_plugin_version = $version; } if ( empty( $slug ) ) { $this->plugin_name = FAIL2WP_PLUGINNAME_SLUG; } else { $this->plugin_name = $slug; } + // Setup default mail headers + $this->fail2wp_mail_headers = array( 'Auto-Submitted: auto-replied', + 'X-Auto-Response-Suppress: All', + 'Precedence: auto_reply' ); + // We only need to query this once really + $this->fail2wp_have_mbstring = extension_loaded( 'mbstring '); + // Things we may be interested in for the REST API + $this->fail2wp_rest_filter_route_list = array( + 'categories', + 'comments', + 'media', + 'pages', + 'posts', + 'search', + 'statuses', + 'tags', + 'taxonomies', + 'types', + 'users', + ); + + // Dump all of our settings, for development + if ( defined( 'FAIL2WP_DUMP_SETTINGS' ) && FAIL2WP_DUMP_SETTINGS ) { + global $wpdb; + $settings = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->options} WHERE option_name LIKE 'fail2wp%'" ), ARRAY_A ); + if ( is_array( $settings ) ) { + error_log( var_export( $settings, true ) ); + } else { + error_log ( basename( __FILE__ ) . ': Unable to fetch settings' ); + } + } // Fetch options and setup defaults - $this->fail2wp_site_label = $this->fail2wp_get_option( 'fail2wp-site-label', true ); - $this->fail2wp_roles_notify = $this->fail2wp_get_option( 'fail2wp-roles-notify', true ); - $this->fail2wp_roles_warn = $this->fail2wp_get_option( 'fail2wp-roles-warn', true ); - $this->fail2wp_unknown_warn = $this->fail2wp_get_option( 'fail2wp-unknown-warn', true ); - - $this->fail2wp_also_log_php = $this->fail2wp_get_option( 'fail2wp-also-log-php', false ); - $this->fail2wp_block_user_enum = $this->fail2wp_get_option( 'fail2wp-block-user-enum', false ); - $this->fail2wp_block_username_login = $this->fail2wp_get_option( 'fail2wp-block-username-login', false ); - $this->fail2wp_log_user_enum = $this->fail2wp_get_option( 'fail2wp-log-user-enum', false ); - $this->fail2wp_secure_login_message = $this->fail2wp_get_option( 'fail2wp-secure-login-message', false ); - $this->fail2wp_cloudflare_check = $this->fail2wp_get_option( 'fail2wp-cloudflare-check', false ); - $this->fail2wp_cloudflare_ipv4 = @ json_decode( $this->fail2wp_get_option( 'fail2wp-cloudflare-ipv4', '' ), true, 2 ); + // ..General + $this->fail2wp_settings_dbversion = get_option( 'fail2wp-settings-dbversion', null ); + if ( $this->fail2wp_settings_dbversion === null ) { + $this->fail2wp_settings_dbversion = FAIL2WP_DB_VERSION; + update_option( 'fail2wp-settings-dbversion', $this->fail2wp_settings_dbversion ); + } + $this->fail2wp_site_label = $this->fail2wp_get_site_label( false ); + $this->fail2wp_roles_notify = @ json_decode( get_option( 'fail2wp-roles-notify', null ), true, 2 ); + if ( ! is_array( $this->fail2wp_roles_notify ) ) { + $this->fail2wp_roles_notify = array(); + update_option( 'fail2wp-roles-notify', json_encode( $this->fail2wp_roles_notify ) ); + } + $this->fail2wp_roles_warn = @ json_decode( get_option( 'fail2wp-roles-warn', null ), true, 2 ); + if ( ! is_array( $this->fail2wp_roles_warn ) ) { + $this->fail2wp_roles_warn = array( 'administrator' ); + update_option( 'fail2wp-roles-warn', json_encode( $this->fail2wp_roles_warn ) ); + } + $this->fail2wp_unknown_warn = get_option( 'fail2wp-unknown-warn', null ); + if ( $this->fail2wp_unknown_warn || $this->fail2wp_unknown_warn === null ) { + $this->fail2wp_unknown_warn = true; + } else { + $this->fail2wp_unknown_warn = false; + } + // ..New users @since 1.1.0 + $default_role = get_option( 'default_role', null );// Get WordPress' default role + $this->fail2wp_reguser_warn = get_option( 'fail2wp-reguser-warn', null ); + if ( $this->fail2wp_reguser_warn === null ) { + $this->fail2wp_reguser_warn = true; + } + $this->fail2wp_reguser_warn_role = get_option( 'fail2wp-reguser-warn-role', null ); + if ( $this->fail2wp_reguser_warn_role === null ) { + $this->fail2wp_reguser_warn_role = ( ! empty( $default_role ) ? $default_role:'subscriber' ); + } + $this->fail2wp_reguser_force = get_option( 'fail2wp-reguser-force', null ); + if ( $this->fail2wp_reguser_force === null ) { + $this->fail2wp_reguser_force = true; + } + $this->fail2wp_reguser_force_role = get_option( 'fail2wp-reguser-force-role', null ); + if ( $this->fail2wp_reguser_force_role === null ) { + $this->fail2wp_reguser_force_role = $this->fail2wp_reguser_warn_role; + } + if ( $this->fail2wp_reguser_force && $default_role != $this->fail2wp_reguser_force_role ) { + // Set WordPress' default role + update_option( 'default_role', $this->fail2wp_reguser_force_role ); + // Tell admin we've done this + add_action( 'admin_notices', [$this, 'fail2wp_admin_alert_new_user_role_forced'] ); + add_action( 'plugins_loaded', [$this, 'fail2wp_admin_alert_new_user_role_forced_email'] ); + $default_role = $this->fail2wp_reguser_force_role; + } + $users_can_register = get_option( 'users_can_register', null ); + if ( $this->fail2wp_reguser_warn ) { + if ( $users_can_register ) { + // Only trigger alarms if users_can_register is true + if ( $default_role != $this->fail2wp_reguser_warn_role ) { + add_action( 'admin_notices', [$this, 'fail2wp_admin_alert_new_users_mismatch'] ); + } elseif ( $default_role == 'administrator' ) { + add_action( 'admin_notices', [$this, 'fail2wp_admin_alert_new_users_admin'] ); + } elseif ( $default_role == null ) { + add_action( 'admin_notices', [$this, 'fail2wp_admin_alert_new_users_null'] ); + } + } + } + $this->fail2wp_reguser_username_length = get_option( 'fail2wp-reguser-username-length', null ); + if ( $this->fail2wp_reguser_username_length === null || $this->fail2wp_reguser_username_length > 200 ) { + $this->fail2wp_reguser_username_length = 0; + update_option( 'fail2wp-reguser-username-length', (int)$this->fail2wp_reguser_username_length ); + } + $this->fail2wp_reguser_username_ban = @ json_decode( get_option ( 'fail2wp-reguser-username-ban', null ), true, 2 ); + if ( ! is_array( $this->fail2wp_reguser_username_ban ) ) { + $this->fail2wp_reguser_username_ban = array( 'administrator', 'admin', 'sysadmin', 'siteadmin' ); + update_option( 'fail2wp-reguser-username-ban', json_encode( $this->fail2wp_reguser_username_ban ) ); + } + $this->fail2wp_reguser_useremail_require = @ json_decode( get_option ( 'fail2wp-reguser-useremail-require', null ), true, 2 ); + if ( ! is_array( $this->fail2wp_reguser_useremail_require ) ) { + $this->fail2wp_reguser_useremail_require = array(); + update_option( 'fail2wp-reguser-useremail-require', json_encode( $this->fail2wp_reguser_useremail_require ) ); + } + // Possibly add new user registration details validation if new user + // registrations are active and we have something to validate against. + if ( $users_can_register && + ( ! empty( $this->fail2wp_reguser_username_ban ) + || ! empty( $this->fail2wp_reguser_useremail_require ) + || $this->fail2wp_reguser_username_length > 1 ) ) { + add_filter( 'registration_errors', [$this, 'fail2wp_admin_check_new_user'], 10, 3 ); + } + // .. REST API @since 1.1.0 + $this->fail2wp_rest_filter_require_authenticated = get_option( 'fail2wp-rest-filter-require-authenticated', null ); + if ( ! $this->fail2wp_rest_filter_require_authenticated || $this->fail2wp_rest_filter_require_authenticated === null ) { + $this->fail2wp_rest_filter_require_authenticated = false; + } else { + $this->fail2wp_rest_filter_require_authenticated = true; + } + if ( $this->fail2wp_rest_filter_require_authenticated ) { + add_filter( 'rest_authentication_errors', [$this, 'fail2wp_rest_filter_authenticate'], 10, 1 ); + } + $this->fail2wp_rest_filter_log_blocked = get_option( 'fail2wp-rest-filter-log-blocked', null ); + if ( ! $this->fail2wp_rest_filter_log_blocked || $this->fail2wp_rest_filter_log_blocked === null ) { + $this->fail2wp_rest_filter_log_blocked = false; + } else { + $this->fail2wp_rest_filter_log_blocked = true; + } + $this->fail2wp_rest_filter_block_index = get_option( 'fail2wp-rest-filter-block-index', null ); + if ( ! $this->fail2wp_rest_filter_block_index || $this->fail2wp_rest_filter_block_index === null ) { + $this->fail2wp_rest_filter_block_index = false; + } else { + $this->fail2wp_rest_filter_block_index = true; + } + $this->fail2wp_rest_filter_block_all = get_option( 'fail2wp-rest-filter-block-all', null ); + if ( ! $this->fail2wp_rest_filter_block_all || $this->fail2wp_rest_filter_block_all === null ) { + $this->fail2wp_rest_filter_block_all = false; + } else { + $this->fail2wp_rest_filter_block_all = true; + } + $this->fail2wp_rest_filter_block_ns = @ json_decode( get_option( 'fail2wp-rest-filter-block-ns', null ), true, 2 ); + if ( ! is_array( $this->fail2wp_rest_filter_block_ns ) ) { + $this->fail2wp_rest_filter_block_ns = array(); + update_option( 'fail2wp-rest-filter-block-ns', json_encode( $this->fail2wp_rest_filter_block_ns ) ); + } + $this->fail2wp_rest_filter_block_routes = @ json_decode( get_option( 'fail2wp-rest-filter-block-routes', null ), true, 2 ); + if ( ! is_array( $this->fail2wp_rest_filter_block_routes ) ) { + $this->fail2wp_rest_filter_block_routes = array(); + update_option( 'fail2wp-rest-filter-block-routes', json_encode( $this->fail2wp_rest_filter_block_routes ) ); + } + $this->fail2wp_rest_filter_ipv4_bypass = @ json_decode( get_option ( 'fail2wp-rest-filter-ipv4-bypass', null ), true, 2 ); + if ( ! is_array( $this->fail2wp_rest_filter_ipv4_bypass ) ) { + $this->fail2wp_rest_filter_ipv4_bypass = array(); + update_option( 'fail2wp-rest-filter-ipv4-bypass', json_encode( $this->fail2wp_rest_filter_ipv4_bypass ) ); + } + $this->fail2wp_rest_filter_ipv6_bypass = @ json_decode( get_option ( 'fail2wp-rest-filter-ipv6-bypass', null ), true, 2 ); + if ( ! is_array( $this->fail2wp_rest_filter_ipv6_bypass ) ) { + $this->fail2wp_rest_filter_ipv6_bypass = array(); + update_option( 'fail2wp-rest-filter-ipv6-bypass', json_encode( $this->fail2wp_rest_filter_ipv6_bypass ) ); + } + // ..Logging + $this->fail2wp_also_log_php = get_option( 'fail2wp-also-log-php', null ); + if ( ! $this->fail2wp_also_log_php || $this->fail2wp_also_log_php === null ) { + $this->fail2wp_also_log_php = false; + } else { + $this->fail2wp_also_log_php = true; + } + $this->fail2wp_block_user_enum = get_option( 'fail2wp-block-user-enum', null ); + if ( $this->fail2wp_block_user_enum || $this->fail2wp_block_user_enum === null ) { + $this->fail2wp_block_user_enum = true; + } else { + $this->fail2wp_block_user_enum = false; + } + $this->fail2wp_block_username_login = get_option( 'fail2wp-block-username-login', null ); + if ( $this->fail2wp_block_username_login === null || ! $this->fail2wp_block_username_login ) { + $this->fail2wp_block_username_login = false; + } else { + $this->fail2wp_block_username_login = true; + } + $this->fail2wp_log_user_enum = get_option( 'fail2wp-log-user-enum', null ); + if ( $this->fail2wp_log_user_enum || $this->fail2wp_log_user_enum === null ) { + $this->fail2wp_log_user_enum = true; + } else { + $this->fail2wp_log_user_enum = false; + } + // ..Failed login message + $this->fail2wp_secure_login_message = get_option( 'fail2wp-secure-login-message', null ); + if ( $this->fail2wp_secure_login_message || $this->fail2wp_secure_login_message === null ) { + $this->fail2wp_secure_login_message = true; + } else { + $this->fail2wp_secure_login_message = false; + } + // ..Cloudflare + $this->fail2wp_cloudflare_check = get_option( 'fail2wp-cloudflare-check', null ); + if ( $this->fail2wp_cloudflare_check === null || ! $this->fail2wp_cloudflare_check ) { + $this->fail2wp_cloudflare_check = false; + } else { + $this->fail2wp_cloudflare_check = true; + } + $this->fail2wp_cloudflare_ipv4 = @ json_decode( get_option ( 'fail2wp-cloudflare-ipv4', null ), true, 2 ); if ( ! is_array( $this->fail2wp_cloudflare_ipv4 ) ) { $this->fail2wp_cloudflare_ipv4 = array(); + update_option( 'fail2wp-cloudflare-ipv4', json_encode( $this->fail2wp_cloudflare_ipv4 ) ); } - $this->fail2wp_cloudflare_ipv6 = @ json_decode( $this->fail2wp_get_option( 'fail2wp-cloudflare-ipv6', '' ), true, 2 ); + $this->fail2wp_cloudflare_ipv6 = @ json_decode( get_option ( 'fail2wp-cloudflare-ipv6', null ), true, 2 ); if ( ! is_array( $this->fail2wp_cloudflare_ipv6 ) ) { - $this->fail2wp_cloudflare_ipv4 = array(); + $this->fail2wp_cloudflare_ipv6 = array(); + update_option( 'fail2wp-cloudflare-ipv6', json_encode( $this->fail2wp_cloudflare_ipv6 ) ); + } + // ..Other options + $this->fail2wp_settings_remove_generator = get_option( 'fail2wp-settings-remove-generator', null ); + if ( $this->fail2wp_settings_remove_generator === null || ! $this->fail2wp_settings_remove_generator ) { + $this->fail2wp_settings_remove_generator = false; + } else { + $this->fail2wp_settings_remove_generator = true; } - $this->fail2wp_settings_remove = $this->fail2wp_get_option( 'fail2wp-settings-remove', false ); + $this->fail2wp_settings_remove_feeds = get_option( 'fail2wp-settings-remove-feeds', null ); + if ( $this->fail2wp_settings_remove_feeds === null || ! $this->fail2wp_settings_remove_feeds ) { + $this->fail2wp_settings_remove_feeds = false; + } else { + $this->fail2wp_settings_remove_feeds = true; + } + $this->fail2wp_settings_remove = get_option( 'fail2wp-settings-remove', null ); + if ( $this->fail2wp_settings_remove === null || ! $this->fail2wp_settings_remove ) { + $this->fail2wp_settings_remove = false; + } else { + $this->fail2wp_settings_remove = true; + } + // .. REST filtering + if ( ! $this->fail2wp_rest_filter_require_authenticated ) { + if ( $this->fail2wp_rest_filter_block_all ) { + remove_action( 'wp_head', 'rest_output_link_wp_head' ); + } + if ( $this->fail2wp_rest_filter_block_all + || ! empty( $this->fail2wp_rest_filter_block_index ) + || ! empty( $this->fail2wp_rest_filter_block_ns ) + || ! empty( $this->fail2wp_rest_filer_block_routes ) ) { + add_action( 'rest_api_init', [$this, 'fail2wp_rest_init'] ); + } + } + // .. Generator filtering + if ( $this->fail2wp_settings_remove_generator ) { + remove_action( 'wp_head', 'wp_generator' ); + add_filter( 'the_generator', [$this, 'fail2wp_the_generator'], 10, 2 ); + } + // .. Feed filtering + if ( $this->fail2wp_settings_remove_feeds ) { + add_filter( 'feed_links_show_posts_feed', [$this, 'fail2wp_noshow_feeds'], 10, 1 ); + add_filter( 'feed_links_show_comments_feed', [$this, 'fail2wp_noshow_feeds'], 10, 1 ); + remove_action( 'wp_head', 'rsd_link' ); + remove_action( 'wp_head', 'feed_links', 2 ); + remove_action( 'wp_head', 'index_rel_link' ); + remove_action( 'wp_head', 'wlwmanifest_link' ); + remove_action( 'wp_head', 'feed_links_extra', 3 ); + remove_action( 'wp_head', 'start_post_rel_link', 10, 0 ); + remove_action( 'wp_head', 'parent_post_rel_link', 10, 0 ); + remove_action( 'wp_head', 'adjacent_posts_rel_link', 10, 0 ); + remove_action( 'wp_head', 'wp_shortlink_wp_head', 10, 0 ); + remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0 ); + remove_action( 'wp_head', 'wp_oembed_add_discovery_links' ); + add_action( 'do_feed', [$this, 'fail2wp_remove_feeds'] ); + add_action( 'do_feed_rdf', [$this, 'fail2wp_remove_feeds'] ); + add_action( 'do_feed_rss', [$this, 'fail2wp_remove_feeds'] ); + add_action( 'do_feed_rss2', [$this, 'fail2wp_remove_feeds'] ); + add_action( 'do_feed_atom', [$this, 'fail2wp_remove_feeds'] ); + add_action( 'do_feed_rss2_comments', [$this, 'fail2wp_remove_feeds'] ); + add_action( 'do_feed_atom_comments', [$this, 'fail2wp_remove_feeds'] ); + } + // .. Various settings $this->fail2wp_default_http_port = FAIL2WP_DEFAULT_HTTP_PORT; $this->fail2wp_default_https_port = FAIL2WP_DEFAULT_HTTPS_PORT; - + $this->fail2wp_prefix = get_option( 'fail2wp-prefix', null ); + if ( $this->fail2wp_prefix === null ) { + $this->fail2wp_prefix = ''; + } + // Validate selected configuration tab $this->fail2wp_settings_tab = ( ! empty( $_GET['tab'] ) ? $_GET['tab'] : '' ); - if ( ! in_array( $this->fail2wp_settings_tab, ['logging', 'advanced', 'cloudflare', 'about'] ) ) { + if ( ! in_array( $this->fail2wp_settings_tab, ['newuser', 'logging', 'advanced', 'restapi', 'cloudflare', 'importexport', 'about'] ) ) { $this->fail2wp_settings_tab = ''; } + // Add 'Settings' link in plugin list + add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), [$this, 'fail2wp_settings_link']); } /** - * Fetch filemtime() of filen and return it. + * Add link to Fail2WP settings in plugin list. + * + * @since 1.1.0 + */ + public function fail2wp_settings_link( array $links ) { + $our_link = '' . + esc_html__( 'Settings ', $this->plugin_name ) . ''; + array_unshift( $links, $our_link ); + return ( $links ); + } + + /** + * Remove "generator" output. + * + * @since 1.1.0 + */ + public function fail2wp_the_generator( string $generator_type, string $type ) { + return( '' ); + } + + /** + * Remove feeds. + * + * Instead of a 404, we issue a WordPress redirect to the main page. + * + * @since 1.1.0 + */ + public function fail2wp_remove_feeds( $feed ) { + wp_redirect( home_url(), 302 ); + exit(); + // One could have something like this too of course, but WordPress + // doesn't seem to like this for some reason and will still display + // the feed content. + /* + wp_die( __( 'This feed has been disabled, please visit', $this->plugin_name ) . + ' ' . + home_url() . + '', + __( 'Feed disabled', $this->plugin_name ), + array( 'link_url' => home_url(), + 'link_text' => home_url(), + 'code' => 'feed_disabled', + 'response' => 404 ) ); + */ + + } + public function fail2wp_noshow_feeds( $noshow ) { + return( false ); + } + + /** + * Setup various REST handlers. + * + * We only do this on 'rest_api_init' so that we don't clutter chain. + * + * @since 1.1.0 + */ + public function fail2wp_rest_init( \WP_REST_Server $wp_rest ) { + if ( $this->fail2wp_rest === null ){ + $this->fail2wp_rest = $wp_rest; + } + if ( $this->fail2wp_rest_filter_block_all ) { + // Block everything + $add_pre_dispatch_filter = true; + } else { + $add_pre_dispatch_filter = false; + // Block selectively + if ( $this->fail2wp_rest_filter_block_index ) { + // Block REST API index calls + add_filter( 'rest_index', [$this, 'fail2wp_rest_index'] ); + $add_pre_dispatch_filter = true; + } elseif ( ! empty( $this->fail2wp_rest_filter_block_ns ) ) { + // Check namespaces on requests + $add_pre_dispatch_filter = true; + } + } + if ( $add_pre_dispatch_filter ) { + add_filter( 'rest_pre_dispatch', [$this, 'fail2wp_rest_pre_dispatch'], 10, 4 ); + } + } + + /** + * Require authentication for REST API calls. + * + * This effectively bypasses all other "block" settings. + * + * @since 1.1.0 + */ + public function fail2wp_rest_filter_authenticate( $result ) { + // Pass along previous success or failure + if ( $result || is_wp_error( $result ) ) { + return( $result ); + } + if ( ! is_user_logged_in() ) { + if ( $this->fail2wp_rest_filter_log_blocked ) { + // Possibly log this for Fail2ban + $alert_message = $this->fail2wp_make_alert_message( '', null, FAIL2WP_ALERT_REST_NOTAUTH ); + if ( ! empty( $alert_message ) ) { + $this->fail2wp_alert_send( $alert_message ); + } + } + // This text is taken verbatim from WordPress and will thus be + // translated in the same way. + return new \WP_Error( 'rest_not_logged_in', + __( 'You are not currently logged in.' ), + array( 'status' => 401 ) + ); + } + return( $result ); + } + + /** + * Block REST API index. + * + * This simply makes the REST API index request return an empty body. + * + * @since 1.1.0 + */ + public function fail2wp_rest_index( $response ) { + if ( defined( 'FAIL2WP_REST_DEBUG' ) ) { + error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): We are blocking REST API requests for index' ); + } + if ( $response instanceof \WP_REST_Response ) { + $response->data = array(); + } + return( $response ); + + } + + /** + * Handle majority of REST API filtering. + * + * @since 1.1.0 + */ + public function fail2wp_rest_pre_dispatch( $result, \WP_REST_Server $rest_server, \WP_REST_Request $request ) { + // Figure out who we're talking to + $remote_ip = $_SERVER['REMOTE_ADDR']; + $remote_ip_cf = $this->fail2wp_do_cloudflare_lookup( $remote_ip ); + if ( defined( 'FAIL2WP_REST_DEBUG' ) ) { + error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): ' . + 'Remote address="' . $remote_ip_cf . '" ' . ( $remote_ip !== $remote_ip_cf ? '(' . $remote_ip . ') ' : '' ) . + 'REST URL="' . rest_url() . '" ' . + 'Route="' . $request->get_route() . '"' ); + } + // Check bypass lists + if ( ! empty( $this->fail2wp_rest_filter_ipv4_bypass ) || ! empty( $this->fail2wp_rest_filter_ipv6_bypass ) ) { + // Setup CIDRmatch + $cidrm = new \CIDRmatch\CIDRmatch(); + $allowed_to_bypass = false; + if ( ! empty( $this->fail2wp_rest_filter_ipv4_bypass ) && is_array( $this->fail2wp_rest_filter_ipv4_bypass ) ) { + foreach( $this->fail2wp_rest_filter_ipv4_bypass as $bypass ) { + if ( ! empty( $bypass ) && $cidrm->match( $remote_ip_cf, $bypass ) ) { + $allowed_to_bypass = true; + break; + } + } + } + if ( ! $allowed_to_bypass && ! empty( $this->fail2wp_rest_filter_ipv6_bypass ) && is_array( $this->fail2wp_rest_filter_ipv6_bypass ) ) { + foreach( $this->fail2wp_rest_filter_ipv6_bypass as $bypass ) { + if ( ! empty( $bypass ) && $cidrm->match( $remote_ip_cf, $bypass ) ) { + $allowed_to_bypass = true; + break; + } + } + } + if ( $allowed_to_bypass ) { + if ( defined( 'FAIL2WP_REST_DEBUG' ) ) { + error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): IP address is allowed to bypass REST API blocks' ); + } + return( $result ); + } + } + // Should we block all REST requests? + if ( $this->fail2wp_rest_filter_block_all ) { + if ( defined( 'FAIL2WP_REST_DEBUG' ) ) { + error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): We are blocking all REST API requests' ); + } + $alert_message = $this->fail2wp_make_alert_message( $remote_ip_cf, null, FAIL2WP_ALERT_REST_BLOCKED, true ); + if ( ! empty( $alert_message ) ) { + $this->fail2wp_alert_send( $alert_message ); + } + return new \WP_Error( + 'rest_no_route', + // This text is taken verbatim from WordPress and will thus be + // translated in the same way. + __( 'No route was found matching the URL and request method.' ), + array( 'status' => '404' ) + ); + } + // Figure out what has been requested and where we live + $namespaces = $this->fail2wp_get_rest_ns( $rest_server ); + $route = $request->get_route(); + $wp_route = ''; + $is_wp_route = false; + if ( ! empty( $route ) ) { + // "Normalize" route, remove leading / + if ( $this->fail2wp_have_mbstring ) { + $route = mb_substr( $route, 1 ); + $is_wp_route = ( mb_stripos( $route, 'wp/v2/' ) === 0 ); + if ( $is_wp_route ) { + $wp_route = mb_substr( $route, 6 ); + } + } else { + $route = substr( $route, 1 ); + $is_wp_route = ( stripos( $route, 'wp/v2/' ) === 0 ); + if ( $is_wp_route ) { + $wp_route = substr( $route, 6 ); + } + } + } + // Figure out if we're blocking index requests and if this is one + if ( in_array( $route, $namespaces ) ) { + if ( defined( 'FAIL2WP_REST_DEBUG' ) ) { + error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): We are blocking REST API requests for index' ); + } + $alert_message = $this->fail2wp_make_alert_message( $remote_ip_cf, null, FAIL2WP_ALERT_REST_BLOCKED, true ); + if ( ! empty( $alert_message ) ) { + $this->fail2wp_alert_send( $alert_message ); + } + return new \WP_Error( + 'rest_no_route', + // This text is taken verbatim from WordPress and will thus be + // translated in the same way. + __( 'No route was found matching the URL and request method.' ), + array( 'status' => '404' ) + ); + } + // Figure out if we're blocking this namespace. Request will be /... + // Namespaces to block are stored without the leading slash. + if ( ! empty( $this->fail2wp_rest_filter_block_ns ) ) { + if ( in_array( $route, $this->fail2wp_rest_filter_block_ns ) ) { + if ( defined( 'FAIL2WP_REST_DEBUG' ) ) { + error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): We are blocking REST API requests for NS "' . $route . '"' ); + } + $alert_message = $this->fail2wp_make_alert_message( $remote_ip_cf, null, FAIL2WP_ALERT_REST_BLOCKED, true ); + if ( ! empty( $alert_message ) ) { + $this->fail2wp_alert_send( $alert_message ); + } + return new \WP_Error( + 'rest_no_route', + // This text is taken verbatim from WordPress and will thus be + // translated in the same way. + __( 'No route was found matching the URL and request method.' ), + array( 'status' => '404' ) + ); + } + } + // Figure out if we're blocking this WP REST API route. Request will be + // /... Routes to block are stored without the leading slash. + if ( $is_wp_route && ! empty( $this->fail2wp_rest_filter_block_routes ) ) { + if ( in_array( $wp_route, $this->fail2wp_rest_filter_block_routes ) ) { + if ( defined( 'FAIL2WP_REST_DEBUG' ) ) { + error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): We are blocking REST API requests for route "' . $route . '"' ); + } + $alert_message = $this->fail2wp_make_alert_message( $remote_ip_cf, null, FAIL2WP_ALERT_REST_BLOCKED, true ); + if ( ! empty( $alert_message ) ) { + $this->fail2wp_alert_send( $alert_message ); + } + return new \WP_Error( + 'rest_no_route', + // This text is taken verbatim from WordPress and will thus be + // translated in the same way. + __( 'No route was found matching the URL and request method.' ), + array( 'status' => '404' ) + ); + } + } + // Carry on ... + return( $result ); + } + + /** + * Checks username and e-mail address prior to registration. + * + * Hooks 'registration_errors' and validates username and e-mail address + * according to our settings. + * + * @since 1.1.0 + * @param WP_Error $errors. + * @param string $sanitized_user_login. + * @param string $user_email. + * @return WP_Error With or without errors + */ + public function fail2wp_admin_check_new_user( \WP_Error $errors, string $user_login, string $user_email ) { + // apply_filters( 'registration_errors', $errors, $sanitized_user_login, $user_email ); + if ( ! is_object( $errors ) || ! is_a( $errors, 'WP_Error' ) ) { + //Make sure we have what we need, otherwise just return it + if ( is_object( $errors ) ) { + error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): Unknown context "' . get_class( $errors ) . '"' ); + } else { + error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): Unknown context "' . $errors . '"' ); + } + return( $errors ); + } + error_log(print_r($errors, true)); + // Check for existing error message and possibly make it less revealing + if ( $this->fail2wp_secure_login_message ) { + if ( ! empty( $errors->errors['username_exists'] ) ) { + $errors->remove( 'username_exists' ); + $errors->add( 'username_exists', esc_html__( 'Invalid username, please try again.', $this->plugin_name ) ); + return( $errors ); + } elseif ( ! empty( $errors->errors['email_exists'] ) ) { + $errors->remove( 'email_exists' ); + $errors->add( 'email_exists', esc_html__( 'Invalid e-mail address, please try again.', $this->plugin_name ) ); + return( $errors ); + } + } + // Check usernames + $have_error = false; + if ( empty( $user_login ) || in_array( $user_login, $this->fail2wp_reguser_username_ban ) ) { + $errors->add( 'fail2wp_username_ban', esc_html__( 'Invalid username, please try again.', $this->plugin_name ) ); + $have_error = true; + } elseif ( $this->fail2wp_reguser_username_length > 1 ) { + if ( $this->fail2wp_have_mbstring ) { + if ( mb_strlen( $user_login ) < $this->fail2wp_reguser_username_length ) { + $have_error = true; + } + } elseif ( strlen( $user_login ) < $this->fail2wp_reguser_username_length ) { + $have_error = true; + } + if ( $have_error ) { + $errors->add( 'fail2wp_username_ban', esc_html__( 'Invalid username, please try again.', $this->plugin_name ) ); + } + } + if ( ! $have_error ) { + $invalid_email = true; + if ( ! empty ( $user_email ) ) { + $invalid_email = true; + if ( $this->fail2wp_have_mbstring ) { + // mb_stripos + foreach( $this->fail2wp_reguser_useremail_require as $email_required ) { + if ( mb_stripos( $user_email, $email_required ) !== false ) { + $invalid_email = false; + break; + } + }// foreach + } else { + // stripos + foreach( $this->fail2wp_reguser_useremail_require as $email_required ) { + if ( stripos( $user_email, $email_required ) !== false ) { + $invalid_email = false; + break; + } + }// foreach + } + }// !empty email + if ( $invalid_email ) { + error_log( FAIL2WP_PLUGINNAME_HUMAN . ': ' . + '"' . $user_email . '" is not an acceptable e-mail address' ); + $errors->add( 'fail2wp_username_ban', esc_html__( 'Invalid e-mail address, please try again.', $this->plugin_name ) ); + } + }// e-mail + + return( $errors ); + } + + /* + * Display admin alert about setting default user role. + * + * This will display an admin alert if we have overridden WordPress' + * default new user role. + * + * @since 1.1.0 + */ + public function fail2wp_admin_alert_new_user_role_forced() { + echo '

'. + ' ' . + esc_html__( 'New user role default has been reset according to your', $this->plugin_name ) . + ' ' . + FAIL2WP_PLUGINNAME_HUMAN . + ' ' . + esc_html__( 'settings', $this->plugin_name ) . + '!' . + '

'; + echo '
'; + } + // Send the same alert via e-mail + public function fail2wp_admin_alert_new_user_role_forced_email() { + $admin_email = get_option( 'admin_email', null ); + if ( empty( $admin_email ) || $admin_email === null ) { + error_log( FAIL2WP_PLUGINNAME_HUMAN . ': ' . + 'No admin e-mail address from WordPress!' ); + return; + } + if ( ! empty($this->fail2wp_site_label ) ) { + $from_site = $this->fail2wp_site_label; + } else { + $from_site = $this->fail2wp_get_site_label( true ); + } + wp_mail( $admin_email, + __( 'Notification about new user role from', $this->plugin_name ) . ' ' . FAIL2WP_PLUGINNAME_HUMAN, + "\n" . + __( 'This is a notification from', $this->plugin_name ) . ' ' . FAIL2WP_PLUGINNAME_HUMAN . + "\n\n" . + __( 'The role for newly registered users has been reset to your configured setting.', $this->plugin_name ) . + "\n\n" . + __( 'This notification was sent from the site', $this->plugin_name ) . + ' ' . $from_site . "\n\n" . + __( 'You may access the admin interface to the site here', $this->plugin_name ) . + ': ' . admin_url() . + "\n" + , + $this->fail2wp_mail_headers ); + } + /** + * Display admin alert about default user role. + * + * This will display an admin alert if the default new user role does not + * match that configured in Fail2WP. + * + * @since 1.1.0 + */ + public function fail2wp_admin_alert_new_users_mismatch() { + echo '

'. + ' ' . + esc_html__( 'User registration is enabled, but the new user role does not match', $this->plugin_name ) . + ' ' . + FAIL2WP_PLUGINNAME_HUMAN . + '!' . + '

'; + if ( is_admin( ) && is_user_logged_in() && current_user_can( 'administrator' ) ) { + // Include link to General settings, if we're not on that page already + if ( empty( $_SERVER['REQUEST_URI'] ) || basename( $_SERVER['REQUEST_URI'] ) != 'options-general.php' ) { + $action = admin_url( 'options-general.php' ); + echo esc_html__( 'Go to', $this->plugin_name ) . + ' ' . + esc_html__( 'General settings', $this->plugin_name ) . + ' ' . + esc_html__( 'to check your membership and new user settings.', $this->plugin_name ). + '

'; + } + } + echo '
'; + } + /* + * Display admin alert about default user role. + * + * This will display an admin alert if the default new user role is set to + * 'administrator'. + * + * @since 1.1.0 + */ + public function fail2wp_admin_alert_new_users_admin() { + echo '

'. + ' ' . + esc_html__( 'User registration is enabled, and new users will be administrators', $this->plugin_name ) . + ' ' . + FAIL2WP_PLUGINNAME_HUMAN . + '!' . + '

'; + if ( is_admin( ) && is_user_logged_in() && current_user_can( 'administrator' ) ) { + // Include link to General settings, if we're not on that page already + if ( empty( $_SERVER['REQUEST_URI'] ) || basename( $_SERVER['REQUEST_URI'] ) != 'options-general.php' ) { + $action = admin_url( 'options-general.php' ); + echo esc_html__( 'Go to', $this->plugin_name ) . + ' ' . + esc_html__( 'General settings', $this->plugin_name ) . + ' ' . + esc_html__( 'to check your membership and new user settings.', $this->plugin_name ). + '

'; + } + } + echo '
'; + } + /* + * Display admin alert about default user role. + * + * This will display an admin alert if the default new user role is not set + * + * @since 1.1.0 + */ + public function fail2wp_admin_alert_new_users_null() { + echo '

'. + ' ' . + esc_html__( 'User registration is enabled, but no role has been configured', $this->plugin_name ) . + ' ' . + FAIL2WP_PLUGINNAME_HUMAN . + '!' . + '

'; + if ( is_admin( ) && is_user_logged_in() && current_user_can( 'administrator' ) ) { + // Include link to General settings, if we're not on that page already + if ( empty( $_SERVER['REQUEST_URI'] ) || basename( $_SERVER['REQUEST_URI'] ) != 'options-general.php' ) { + $action = admin_url( 'options-general.php' ); + echo esc_html__( 'Go to', $this->plugin_name ) . + ' ' . + esc_html__( 'General settings', $this->plugin_name ) . + ' ' . + esc_html__( 'to check your membership and new user settings.', $this->plugin_name ). + '

'; + } + } + echo '
'; + } + + /** + * Fetch filemtime() of file and return it. * - * Fetch filemtime() of $filename and return it, upon error, $this->version - * is returned instead. This could possibly simply return $this->version in + * Fetch filemtime() of $filename and return it, upon error, plugin_version + * is returned instead. This could possibly simply return plugin_version in * production. * * @since 1.0.0 @@ -159,52 +949,30 @@ public function __construct( string $version = '', string $slug = '' ) { protected function resource_mtime( $filename ) { $filetime = @ filemtime( $filename ); if ( $filetime === false ) { - $filetime = $this->version; + $filetime = $this->fail2wp_plugin_version; } return ( $filetime ); } /** - * Fetch setting with default value. + * Fetch site label setting with default value. * - * @since 1.0.0 + * @since 1.1.0 */ - protected function fail2wp_get_option( string $option_name, bool $auto_logic = false ) { - switch( $option_name ) { - case 'fail2wp-site-label': - $option_val = get_option( 'fail2wp-site-label', '' ); - if ( empty( $option_val ) && $auto_logic ) { - $option_val = trim( get_bloginfo( 'name' ) ); - if ( empty( $option_val ) ) { - $option_val = trim( $_SERVER['SERVER_NAME'] ); - if ( empty( $option_val ) ) { - $option_val = 'IP:' . $_SERVER['SERVER_ADDR']; - } - } - } - if ( $auto_logic ) { - $default_val = '(' . __( 'Unknown', $this->plugin_name ) . ')'; - } else { - $default_val = ''; - } - break; - case 'fail2wp-roles-notify': - case 'fail2wp-roles-warn': - // Default is in JSON format - $default_val = ( $auto_logic ? '["administrator"]' : '' ); - break; - case 'fail2wp-unknown-warn': - $default_val = ( $auto_logic ? '0' : '' ); - break; - default: - $default_val = ''; - break; - } // switch - if ( $option_name != 'fail2wp-site-label' ) { - $option_val = get_option ( $option_name, $default_val ); + protected function fail2wp_get_site_label( bool $auto_logic = false ) { + $option_val = get_option( 'fail2wp-site-label', null ); + if ( $option_val === null ) { + update_option( 'fail2wp-site-label', '' ); + $option_val = ''; } - if ( empty( $option_val ) ) { - $option_val = $default_val; + if ( empty( $option_val ) && $auto_logic ) { + $option_val = trim( get_bloginfo( 'name' ) ); + if ( empty( $option_val ) ) { + $option_val = trim( $_SERVER['SERVER_NAME'] ); + if ( empty( $option_val ) ) { + $option_val = 'IP:' . $_SERVER['SERVER_ADDR']; + } + } } return( $option_val ); } @@ -229,6 +997,7 @@ protected function fail2wp_get_wp_roles() : array { // $roles = $wp_roles->get_roles_data(); $roles = array_keys( $wp_roles->roles ); $role_names = $role_names_en = $wp_roles->get_names(); + } else { $roles = false; $role_names = $role_names_en = array(); @@ -254,7 +1023,6 @@ protected function fail2wp_get_wp_roles() : array { $this->fail2wp_wp_roles_enus = $return_roles_en; return( $return_roles ); } - /** * Setup WordPress admin menu. * @@ -283,36 +1051,241 @@ public function fail2wp_admin_page() { // Get ourselves a proper URL $action = admin_url( 'admin.php' ) . '?page=' . FAIL2WP_PLUGINNAME_SLUG; // - $html = '
'; - $html .= '

  ' . FAIL2WP_PLUGINNAME_HUMAN . '

'; - $html .= '

' . esc_html__( 'Provides authentication related logging and security functions for WordPress, suitable for use with Fail2ban', $this->plugin_name ) . '

'; - $html .= ''; if ( ! function_exists( 'openlog' ) || ! function_exists( 'closelog' ) || ! function_exists( 'syslog' ) ) { - $html .= '

'. - esc_html__( 'One or more of openlog(), closelog(), and/or syslog() seem to be missing on this system', $this->plugin_name ). - '

'; + $tab_header .= '

'. + esc_html__( 'One or more of openlog(), closelog(), and/or syslog() seem to be missing on this system', $this->plugin_name ). + '

'; } + ob_start(); if ( $this->fail2wp_settings_tab == 'about' ) { $this->fail2wp_about_page(); $html .= ob_get_contents(); ob_end_clean(); + } elseif ( $this->fail2wp_settings_tab == 'importexport' ) { + $html .= '
'; + $html .= wp_nonce_field( 'importexport', 'fail2wp_nonce' ); + $html .= '
'; + $html .= '
'; + $do_import = false; + + if ( isset( $_POST['fail2wpimport']) && isset( $_POST['fail2wp_nonce'] ) && wp_verify_nonce( $_POST['fail2wp_nonce'], 'importexport' ) ) { + if ( ! empty( $_POST['fail2wp-import-settings'] ) ) { + $xs = trim( $_POST['fail2wp-import-settings'] ); + $_POST['fail2wp-import-settings'] = sanitize_text_field( $xs ); + if ( $xs != $_POST['fail2wp-import-settings'] ) { + // Something was stripped + $tab_header .= '

'. + esc_html__( 'Some data was filtered, please make sure you paste the content exactly as copied', $this->plugin_name ). + '

'; + } elseif ( strpos( $xs, FAIL2WP_EXPORT_HEADER ) !== 0 || strpos( $xs, FAIL2WP_EXPORT_FOOTER) !== ( strlen( $xs ) - strlen( FAIL2WP_EXPORT_FOOTER ) ) ) { + // Header or footer check failed + $tab_header .= '

'. + esc_html__( 'Please make sure you paste the content exactly as copied', $this->plugin_name ). + '

'; + } else { + // Remove header/footer, convert, and decode before validating + $xs = @ json_decode( base64_decode( substr( $xs, strlen( FAIL2WP_EXPORT_HEADER ), strlen( $xs ) - ( strlen( FAIL2WP_EXPORT_HEADER ) + strlen( FAIL2WP_EXPORT_FOOTER ) ) ) ), true, 10 ); + if ( ! is_array( $xs ) || empty( $xs['fail2wp_plugin_version'] ) ) { + // Decoding failed + $tab_header .= '

'. + esc_html__( 'Please make sure you paste the content exactly as copied', $this->plugin_name ). + '

'; + } elseif ( $xs['fail2wp_plugin_version'] !== $this->fail2wp_plugin_version ) { + // Version mismatch + $tab_header .= '

'. + esc_html__( 'Plugin version mismatch. You can only import exported data from the same version as the one currently installed', $this->plugin_name ). + '

'; + } else { + // Good to go + $do_import = true; + } + } + } + if ( $do_import ) { + // Variables are named fail2wp_this_and_that. Settings are named fail2wp-this-and-that. + // So we check if the variable exists in our context, and if so, update the setting. + // We do need to make exceptions for array settings, since we store them as json. + $warn_about_site_label = false; + $import_result = ''; + foreach( $xs as $k => $v ) { + if ( ! property_exists( $this, $k ) ) { + $import_result .= ' ' . str_replace( '_', '-', $k ) . '
'; + continue; + } + // What we call our option in WP DB + $k_wp = str_replace( '_', '-', $k ); + // Handle exceptions + switch( $k ) { + case 'fail2wp_cloudflare_ipv4': + case 'fail2wp_cloudflare_ipv6': + case 'fail2wp_reguser_username_ban': + case 'fail2wp_reguser_useremail_require': + case 'fail2wp_rest_filter_ipv4_bypass': + case 'fail2wp_rest_filter_ipv6_bypass': + // Some special treatment of these since WordPress + // calls the registered sanitization functions for + // update_option() + update_option( $k_wp, implode( "\n", $v ) ); + break; + case 'fail2wp_roles_notify': + case 'fail2wp_roles_warn': + case 'fail2wp_rest_filter_block_ns': + case 'fail2wp_rest_filter_block_routes': + // Array elements (store as json) + $v_json = @ json_encode( $v ); + if ( ! $v_json ) { + error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): Unable to encode import value for "' . $k . '"' ); + $import_result .= ' ' . $k_wp . '
'; + } else { + update_option( $k_wp, $v ); + } + break; + case 'fail2wp_plugin_version': + // Ignore + break; + default: + update_option( $k_wp, $v ); + if ( $k == 'fail2wp_site_label' ) { + if ( $v != $this->fail2wp_site_label ) { + // Notify user that "Site label" option may need to be checked + $warn_about_site_label = true; + } + } + break; + } + }// foreach + // Possibly show warning/notices from import + if ( ! empty( $import_result ) ) { + $import_result = '

' . + esc_html__( 'The following settings were ignored during the import', $this->plugin_name ) . + ':
' . + $import_result . + '

'; + $html .= $import_result; + } else { + $html .= '

' . + esc_html__( 'Settings were successfully imported', $this->plugin_name ) . + '

'; + if ( $warn_about_site_label ) { + $html .= '

' . + esc_html__( 'The setting "Site label" may need manual correction', $this->plugin_name ) . + '

'; + } + } + } + }// import settings + if ( ! $do_import ) { + $html .= '

' . esc_html__( 'This tab can be used to import and export settings for the plugin.', $this->plugin_name ) . '

'; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + ob_end_clean(); + } + $html .= '
'; + $html .= '
'; + $html .= '
'; } else { $html .= '
'; $html .= '
'; @@ -322,10 +1295,23 @@ public function fail2wp_admin_page() { settings_fields( 'fail2wp-settings' ); do_settings_sections( 'fail2wp-settings' ); break; + case 'newuser': + settings_fields( 'fail2wp_settings_newuser' ); + do_settings_sections( 'fail2wp_settings_newuser' ); + break; case 'logging': + $html .= '

' . + esc_html__( "This is logged to the system's authentication log, which allows Fail2ban to dynamically block offending IP addresses.", $this->plugin_name ) . + ' '. + esc_html__( 'Configuration of the Fail2ban system daemon, or similar, must be done outside of WordPress for this to have any effect.', $this->plugin_name ) . + '

'; settings_fields( 'fail2wp_settings_notify' ); do_settings_sections( 'fail2wp_settings_notify' ); break; + case 'restapi': + settings_fields( 'fail2wp_settings_restapi' ); + do_settings_sections( 'fail2wp_settings_restapi' ); + break; case 'advanced': settings_fields( 'fail2wp_settings_advanced' ); do_settings_sections( 'fail2wp_settings_advanced' ); @@ -334,17 +1320,17 @@ public function fail2wp_admin_page() { settings_fields( 'fail2wp_settings_cloudflare' ); do_settings_sections( 'fail2wp_settings_cloudflare' ); break; - }// switch + }// switchfail2ban submit_button(); $html .= ob_get_contents(); ob_end_clean(); + $html .= '
'; + $html .= '
'; // tab-content $html .= ''; } - $html .= ''; - $html .= ''; // tab-content $html .= ''; // wrap // - echo $html; + echo $tab_header . $html; } /** * Display about/support. @@ -363,10 +1349,15 @@ public function fail2wp_about_page() { ' WebbPlatsen i Sverige AB '. esc_html__('in Stockholm, Sweden. We speak Swedish and English', $this->plugin_name ) . ' :-)' . '

' . - esc_html__( 'The plugin is written by Joaquim Homrighausen and sponsored by WebbPlatsen i Sverige AB.', $this->plugin_name ) . '

' . - '

' . esc_html__( 'If you find this plugin useful, the author is happy to receive a donation, good review, or just a kind word.', $this->plugin_name ) . '

' . - '

' . esc_html__( 'If there is something you feel to be missing from this plugin, or if you have found a problem with the code or a feature, please do not hesitate to reach out to', $this->plugin_name ) . - ' support@webbplatsen.se' . '

'; + esc_html__( 'The plugin is written by Joaquim Homrighausen and sponsored by WebbPlatsen i Sverige AB.', $this->plugin_name ) . + '

' . + '

' . esc_html__( 'If you find this plugin useful, the author is happy to receive a donation, good review, or just a kind word.', $this->plugin_name ) . ' ' . + esc_html__( 'If there is something you feel to be missing from this plugin, or if you have found a problem with the code or a feature, please do not hesitate to reach out to', $this->plugin_name ) . + ' support@webbplatsen.se' . ' '. + esc_html__( 'There is more documentation available at', $this->plugin_name ) . ' ' . + ''. + 'code.webbplatsen.net/documentation/fail2wp/' . + '

'; ''; echo ''; } @@ -384,14 +1375,36 @@ public function fail2wp_settings() { add_settings_field( 'fail2wp-block-user-enum', esc_html__( 'Block user enum', $this->plugin_name ), [$this, 'fail2wp_setting_block_enums'], 'fail2wp-settings', 'fail2wp-settings', ['label_for' => 'fail2wp-block-user-enum'] ); add_settings_field( 'fail2wp-block-username-login', esc_html__( 'Block username login', $this->plugin_name ), [$this, 'fail2wp_setting_block_username_login'], 'fail2wp-settings', 'fail2wp-settings', ['label_for' => 'fail2wp-block-username-login'] ); add_settings_field( 'fail2wp-secure-login-message', esc_html__( 'Secure login messages', $this->plugin_name ), [$this, 'fail2wp_setting_secure_login_messages'], 'fail2wp-settings', 'fail2wp-settings', ['label_for' => 'fail2wp-secure-login-message'] ); + add_settings_section( 'fail2wp_section_other', esc_html__( 'Other settings', $this->plugin_name ), false, 'fail2wp-settings' ); + add_settings_field( 'fail2wp-settings-remove-generator', esc_html__( 'Remove generator info', $this->plugin_name ), [$this, 'fail2wp_setting_remove_generator'], 'fail2wp-settings', 'fail2wp_section_other', ['label_for' => 'fail2wp-settings-remove-generator'] ); + add_settings_field( 'fail2wp-settings-remove-feeds', esc_html__( 'Remove feeds', $this->plugin_name ), [$this, 'fail2wp_setting_remove_feeds'], 'fail2wp-settings', 'fail2wp_section_other', ['label_for' => 'fail2wp-settings-remove-feeds'] ); add_settings_field( 'fail2wp-settings-remove', esc_html__( 'Remove settings', $this->plugin_name ), [$this, 'fail2wp_setting_remove'], 'fail2wp-settings', 'fail2wp_section_other', ['label_for' => 'fail2wp-settings-remove'] ); + add_settings_section( 'fail2wp_section_newuser', '', false, 'fail2wp_settings_newuser' ); + add_settings_field( 'fail2wp-reguser-warn', esc_html__( 'Membership warnings', $this->plugin_name ), [$this, 'fail2wp_setting_reguser_warn'], 'fail2wp_settings_newuser', 'fail2wp_section_newuser', ['label_for' => 'fail2wp-reguser-warn'] ); + add_settings_field( 'fail2wp-reguser-warn-role', esc_html__( 'Check for role', $this->plugin_name ), [$this, 'fail2wp_setting_reguser_warn_role'], 'fail2wp_settings_newuser', 'fail2wp_section_newuser', ['label_for' => 'fail2wp-reguser-warn-role'] ); + add_settings_field( 'fail2wp-reguser-force', esc_html__( 'Force role', $this->plugin_name ), [$this, 'fail2wp_setting_reguser_force'], 'fail2wp_settings_newuser', 'fail2wp_section_newuser', ['label_for' => 'fail2wp-reguser-force'] ); + add_settings_field( 'fail2wp-reguser-force-role', esc_html__( 'Role to force', $this->plugin_name ), [$this, 'fail2wp_setting_reguser_force_role'], 'fail2wp_settings_newuser', 'fail2wp_section_newuser', ['label_for' => 'fail2wp-reguser-force-role'] ); + add_settings_field( 'fail2wp-reguser-username-length', esc_html__( 'Minimum username length', $this->plugin_name ), [$this, 'fail2wp_settings_reguser_username_length'], 'fail2wp_settings_newuser', 'fail2wp_section_newuser', ['label_for' => 'fail2wp-reguser-username-length'] ); + add_settings_field( 'fail2wp-reguser-username-ban', esc_html__( 'Banned usernames', $this->plugin_name ), [$this, 'fail2wp_settings_reguser_username_ban'], 'fail2wp_settings_newuser', 'fail2wp_section_newuser', ['label_for' => 'fail2wp-reguser-username-ban'] ); + add_settings_field( 'fail2wp-reguser-useremail-require', esc_html__( 'E-mail must match', $this->plugin_name ), [$this, 'fail2wp_settings_reguser_useremail_require'], 'fail2wp_settings_newuser', 'fail2wp_section_newuser', ['label_for' => 'fail2wp-reguser-useremail-require'] ); + add_settings_section( 'fail2wp_settings_notify', '', false, 'fail2wp_settings_notify' ); add_settings_field( 'fail2wp-roles-notify', esc_html__( 'Successful login', $this->plugin_name ), [$this, 'fail2wp_setting_roles_notify'], 'fail2wp_settings_notify', 'fail2wp_settings_notify', ['label_for' => 'fail2wp-roles-notify'] ); add_settings_field( 'fail2wp-roles-warn', esc_html__( 'Unsuccessful login', $this->plugin_name ), [$this, 'fail2wp_setting_roles_warn'], 'fail2wp_settings_notify', 'fail2wp_settings_notify', ['label_for' => 'fail2wp-roles-warn'] ); add_settings_field( 'fail2wp-unknown-warn', '', [$this, 'fail2wp_setting_unknown_notify'], 'fail2wp_settings_notify', 'fail2wp_settings_notify', ['label_for' => 'fail2wp-unknown-warn'] ); - add_settings_field( 'fail2wp-log-user-enum', 'Log user enum', [$this, 'fail2wp_setting_log_enums'], 'fail2wp_settings_notify', 'fail2wp_settings_notify', ['label_for' => 'fail2wp-log-user-enum'] ); + add_settings_field( 'fail2wp-log-user-enum', esc_html__( 'Log user enum', $this->plugin_name ), [$this, 'fail2wp_setting_log_enums'], 'fail2wp_settings_notify', 'fail2wp_settings_notify', ['label_for' => 'fail2wp-log-user-enum'] ); + + add_settings_section( 'fail2wp_section_restapi', '', [$this, 'fail2wp_settings_restapi_callback'], 'fail2wp_settings_restapi' ); + add_settings_field( 'fail2wp-rest-filter-require-authenticated', esc_html__( 'Require authentication', $this->plugin_name ), [$this, 'fail2wp_setting_rest_filter_require_authenticated'], 'fail2wp_settings_restapi', 'fail2wp_section_restapi', ['label_for' => 'fail2wp-rest-filter-require-authenticated'] ); + add_settings_field( 'fail2wp-rest-filter-log-blocked', esc_html__( 'Log blocked requests', $this->plugin_name ), [$this, 'fail2wp_setting_rest_filter_log_blocked'], 'fail2wp_settings_restapi', 'fail2wp_section_restapi', ['label_for' => 'fail2wp-rest-filter-log-blocked'] ); + add_settings_field( 'fail2wp-rest-filter-block-index', esc_html__( 'Block index requests', $this->plugin_name ), [$this, 'fail2wp_setting_rest_filter_block_index'], 'fail2wp_settings_restapi', 'fail2wp_section_restapi', ['label_for' => 'fail2wp-rest-filter-block-index'] ); + add_settings_field( 'fail2wp-rest-filter-block-all', esc_html__( 'Block all requests', $this->plugin_name ), [$this, 'fail2wp_setting_rest_filter_block_all'], 'fail2wp_settings_restapi', 'fail2wp_section_restapi', ['label_for' => 'fail2wp-rest-filter-block-all'] ); + add_settings_field( 'fail2wp-rest-filter-block-ns', esc_html__( 'Block specific namespaces', $this->plugin_name ), [$this, 'fail2wp_setting_rest_filter_block_ns'], 'fail2wp_settings_restapi', 'fail2wp_section_restapi', ['label_for' => 'fail2wp-rest-filter-block-ns'] ); + add_settings_field( 'fail2wp-rest-filter-block-routes', esc_html__( 'Block specific routes', $this->plugin_name ), [$this, 'fail2wp_setting_rest_filter_block_routes'], 'fail2wp_settings_restapi', 'fail2wp_section_restapi', ['label_for' => 'fail2wp-rest-filter-block-routes'] ); + add_settings_field( 'fail2wp-rest-filter-ipv4-bypass', esc_html__( 'Bypass blocks for IPv4', $this->plugin_name ), [$this, 'fail2wp_settings_rest_filter_bypass_ipv4'], 'fail2wp_settings_restapi', 'fail2wp_section_restapi', ['label_for' => 'fail2wp-rest-filter-ipv4-bypass'] ); + add_settings_field( 'fail2wp-rest-filter-ipv6-bypass', esc_html__( 'Bypass blocks for IPv6', $this->plugin_name ), [$this, 'fail2wp_settings_rest_filter_bypass_ipv6'], 'fail2wp_settings_restapi', 'fail2wp_section_restapi', ['label_for' => 'fail2wp-rest-filter-ipv6-bypass'] ); add_settings_section( 'fail2wp_settings_advanced', '', [$this, 'fail2wp_settings_advanced_callback'], 'fail2wp_settings_advanced' ); add_settings_field( 'fail2wp-prefix', esc_html__( 'Logging prefix', $this->plugin_name ), [$this, 'fail2wp_settings_prefix'], 'fail2wp_settings_advanced', 'fail2wp_settings_advanced', ['label_for' => 'fail2wp-prefix'] ); @@ -407,18 +1420,38 @@ public function fail2wp_settings() { register_setting( 'fail2wp-settings', 'fail2wp-block-username-login' ); register_setting( 'fail2wp-settings', 'fail2wp-block-username-login' ); register_setting( 'fail2wp-settings', 'fail2wp-secure-login-message' ); + register_setting( 'fail2wp-settings', 'fail2wp-settings-remove-generator' ); + register_setting( 'fail2wp-settings', 'fail2wp-settings-remove-feeds' ); + register_setting( 'fail2wp-settings', 'fail2wp-settings-remove' ); + + register_setting( 'fail2wp_settings_newuser', 'fail2wp-reguser-warn' ); + register_setting( 'fail2wp_settings_newuser', 'fail2wp-reguser-warn-role' ); + register_setting( 'fail2wp_settings_newuser', 'fail2wp-reguser-force' ); + register_setting( 'fail2wp_settings_newuser', 'fail2wp-reguser-force-role' ); + register_setting( 'fail2wp_settings_newuser', 'fail2wp-reguser-username-length', ['type' => 'number', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_username_length']] ); + register_setting( 'fail2wp_settings_newuser', 'fail2wp-reguser-username-ban', ['type' => 'string', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_textarea_setting']] ); + register_setting( 'fail2wp_settings_newuser', 'fail2wp-reguser-useremail-require', ['type' => 'string', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_textarea_setting']] ); register_setting( 'fail2wp_settings_notify', 'fail2wp-roles-notify', ['type' => 'array', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_roles']] ); register_setting( 'fail2wp_settings_notify', 'fail2wp-roles-warn', ['type' => 'array', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_roles']] ); register_setting( 'fail2wp_settings_notify', 'fail2wp-unknown-warn' ); register_setting( 'fail2wp_settings_notify', 'fail2wp-log-user-enum' ); + register_setting( 'fail2wp_settings_restapi', 'fail2wp-rest-filter-require-authenticated' ); + register_setting( 'fail2wp_settings_restapi', 'fail2wp-rest-filter-log-blocked' ); + register_setting( 'fail2wp_settings_restapi', 'fail2wp-rest-filter-block-index' ); + register_setting( 'fail2wp_settings_restapi', 'fail2wp-rest-filter-block-all' ); + register_setting( 'fail2wp_settings_restapi', 'fail2wp-rest-filter-block-ns', ['type' => 'array', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_block_ns']] ); + register_setting( 'fail2wp_settings_restapi', 'fail2wp-rest-filter-block-routes', ['type' => 'array', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_block_routes']] ); + register_setting( 'fail2wp_settings_restapi', 'fail2wp-rest-filter-ipv4-bypass', ['type' => 'string', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_textarea_setting']] ); + register_setting( 'fail2wp_settings_restapi', 'fail2wp-rest-filter-ipv6-bypass', ['type' => 'string', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_textarea_setting']] ); + register_setting( 'fail2wp_settings_advanced', 'fail2wp-prefix', ['type' => 'string', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_advanced']] ); register_setting( 'fail2wp_settings_advanced', 'fail2wp-also-log-php' ); register_setting( 'fail2wp_settings_cloudflare', 'fail2wp-cloudflare-check' ); - register_setting( 'fail2wp_settings_cloudflare', 'fail2wp-cloudflare-ipv4', ['type' => 'string', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_cloudflare_ip']] ); - register_setting( 'fail2wp_settings_cloudflare', 'fail2wp-cloudflare-ipv6', ['type' => 'string', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_cloudflare_ip']] ); + register_setting( 'fail2wp_settings_cloudflare', 'fail2wp-cloudflare-ipv4', ['type' => 'string', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_textarea_setting']] ); + register_setting( 'fail2wp_settings_cloudflare', 'fail2wp-cloudflare-ipv6', ['type' => 'string', 'sanitize_callback' => [$this, 'fail2wp_setting_sanitize_textarea_setting']] ); } /** @@ -432,7 +1465,7 @@ public function fail2wp_setting_sanitize_site_label( $input ) { if ( ! is_admin( ) || ! is_user_logged_in() || ! current_user_can( 'administrator' ) ) { return; } - if ( function_exists( 'mb_substr' ) ) { + if ( $this->fail2wp_have_mbstring ) { return( mb_substr( sanitize_text_field( $input ), 0, 200 ) ); } return( substr( sanitize_text_field( $input ), 0, 200 ) ); @@ -454,129 +1487,275 @@ public function fail2wp_setting_sanitize_roles( $input ) { } return( json_encode( $return_val ) ); } + public function fail2wp_setting_sanitize_block_ns( $input ) { + if ( ! is_admin( ) || ! is_user_logged_in() || ! current_user_can( 'administrator' ) ) { + return; + } + $namespaces = $this->fail2wp_get_rest_ns(); + $return_val = array(); + if ( is_array( $input ) ) { + foreach( $input as $block_ns ) { + if ( in_array( $block_ns, $namespaces ) ) { + // We know $bock_ns is clean since it matches + $return_val[] = $block_ns; + } + } + } + return( json_encode( $return_val ) ); + } + public function fail2wp_setting_sanitize_block_routes( $input ) { + if ( ! is_admin( ) || ! is_user_logged_in() || ! current_user_can( 'administrator' ) ) { + return; + } + $return_val = array(); + if ( is_array( $input ) ) { + foreach( $input as $block_route ) { + if ( in_array( $block_route, $this->fail2wp_rest_filter_route_list ) ) { + // We know $block_route is clean since it matches + $return_val[] = $block_route; + } + } + } + return( json_encode( $return_val ) ); + } public function fail2wp_setting_sanitize_advanced( $input ) { if ( ! is_admin( ) || ! is_user_logged_in() || ! current_user_can( 'administrator' ) ) { return; } - if ( function_exists( 'mb_substr' ) ) { + if ( $this->fail2wp_have_mbstring ) { return( mb_substr( sanitize_text_field( $input ), 0, 200 ) ); } return( substr( sanitize_text_field( $input ), 0, 200 ) ); } - public function fail2wp_setting_sanitize_cloudflare_ip( $input ) { + public function fail2wp_setting_sanitize_textarea_setting( $input ) { if ( ! is_admin( ) || ! is_user_logged_in() || ! current_user_can( 'administrator' ) ) { return; } $input = explode( "\n", sanitize_textarea_field( $input ) ); $output = array(); - foreach( $input as $one_line ) { - $output[] = trim( $one_line ); + if ( $this->fail2wp_have_mbstring ) { + foreach( $input as $one_line ) { + $one_line = trim( mb_substr( $one_line, 0, 80 ) ); + if ( mb_strlen( $one_line ) > 0 ) { + $output[] = $one_line; + } + } + } else { + foreach( $input as $one_line ) { + $one_line = trim( substr( $one_line, 0, 80 ) ); + if ( strlen( $one_line) > 0 ) { + $output[] = $one_line; + } + } + } + $input = @ json_encode( $output ); + return( $input ); + } + public function fail2wp_setting_sanitize_username_length( $input ) { + if ( ! is_admin( ) || ! is_user_logged_in() || ! current_user_can( 'administrator' ) ) { + return; + } + // We really should strip everything but numbers here, but ... + $input = (int) sanitize_text_field( $input ); + if ( $input < 0 ) { + $input = 0; + } elseif ( $input > 200 ) { + $input = 200; } - $input = json_encode( $output ); return( $input ); } /** * Output input fields. * - * @since 1.0.0 + * @since 1.0.0 */ public function fail2wp_setting_site_label() { - $option_val = $this->fail2wp_get_option( 'fail2wp-site-label', false ); - echo ''; + $placeholder = $this->fail2wp_get_site_label( true ); + echo 'fail2wp_site_label ) && ! empty( $placeholder ) ) { + echo ' placeholder="' . esc_attr( $placeholder ) . '"'; + } + echo ' />'; echo '

' . esc_html__( 'The site name to use for logging, defaults to your site name if left empty', $this->plugin_name ) . '

'; } public function fail2wp_setting_roles_notify($args) { - $option_val = $this->fail2wp_get_option( 'fail2wp-roles-notify', false ); $available_roles = $this->fail2wp_get_wp_roles(); - if ( ! empty( $option_val ) ) { - $checkboxes = @ json_decode( $option_val, true, 2 ); - if ( ! is_array( $checkboxes ) ) { - $checkboxes = array(); - } - } else { - $checkboxes = array(); - } foreach( $available_roles as $k => $v ) { echo '
'; echo ' '; echo '
'; } } public function fail2wp_setting_roles_warn() { - $option_val = $this->fail2wp_get_option( 'fail2wp-roles-warn', false ); $available_roles = $this->fail2wp_get_wp_roles(); - if ( ! empty( $option_val ) ) { - $checkboxes = @ json_decode( $option_val, true, 2 ); - if ( ! is_array( $checkboxes ) ) { - $checkboxes = array(); - } - } else { - $checkboxes = array(); - } foreach( $available_roles as $k => $v ) { echo '
'; echo ' '; echo '
'; } } public function fail2wp_setting_unknown_notify() { - $option_val = $this->fail2wp_get_option( 'fail2wp-unknown-warn', false ); echo '
'; echo ' '; echo '
'; } public function fail2wp_setting_log_enums() { - $option_val = $this->fail2wp_get_option( 'fail2wp-log-user-enum', false ); echo '
'; echo ' '; echo '
'; } + // @since 1.1.0 + public function fail2wp_setting_reguser_warn() { + echo '
'; + echo ' '; + echo '
'; + } + public function fail2wp_setting_reguser_warn_role() { + $option_val = $this->fail2wp_reguser_warn_role; + $available_roles = $this->fail2wp_get_wp_roles(); + echo '
'; + if ( is_array( $available_roles ) ) { + echo ''; + echo '

' . + esc_html__( 'Check WordPress setting against the value configured here', $this->plugin_name ) . + '

'; + + } else { + echo esc_html__( 'No available roles (?)', $this->plugin_name ); + } + echo '
'; + } + public function fail2wp_setting_reguser_force() { + echo '
'; + echo ' '; + echo '
'; + } + public function fail2wp_setting_reguser_force_role() { + $option_val = $this->fail2wp_reguser_force_role; + $available_roles = $this->fail2wp_get_wp_roles(); + echo '
'; + if ( is_array( $available_roles ) ) { + echo ''; + echo '

' . + esc_html__( 'Force WordPress setting to the value configured here', $this->plugin_name ) . + '

'; + } else { + echo esc_html__( 'No available roles (?)', $this->plugin_name ); + } + echo '
'; + } + public function fail2wp_settings_reguser_username_length() { + echo ''; + echo '

' . esc_html__( 'Minimum length of usernames, 2-200 characters, 0 ignores the setting', $this->plugin_name ) . '

'; + } + public function fail2wp_settings_reguser_username_ban() { + echo ''; + echo '

' . esc_html__( 'These usernames will be blocked for new user registrations', $this->plugin_name ) . '

'; + } + public function fail2wp_settings_reguser_useremail_require() { + echo ''; + echo '

' . esc_html__( 'E-mail address must match at least one of these for new users', $this->plugin_name ) . '

'; + } + public function fail2wp_setting_remove_generator() { + echo '
'; + echo ' '; + echo '
'; + } + public function fail2wp_setting_remove_feeds() { + echo '
'; + echo ' '; + echo '
'; + } + // @since 1.0.0 public function fail2wp_setting_remove() { - $option_val = $this->fail2wp_get_option( 'fail2wp-settings-remove', false ); echo '
'; echo ' '; echo '
'; } public function fail2wp_setting_block_enums() { - $option_val = $this->fail2wp_get_option( 'fail2wp-block-user-enum', false ); echo '
'; echo ' '; echo '
'; } public function fail2wp_setting_block_username_login() { - $option_val = $this->fail2wp_get_option( 'fail2wp-block-username-login', false ); echo '
'; echo ' '; echo '
'; } public function fail2wp_setting_secure_login_messages() { - $option_val = $this->fail2wp_get_option( 'fail2wp-secure-login-message', false ); echo '
'; echo ' '; echo '
'; } + public function fail2wp_settings_restapi_callback() { + if ( ! is_admin( ) || ! is_user_logged_in() || ! current_user_can( 'administrator' ) ) { + return; + } + echo '

' . + esc_html__( 'Please make sure you understand how these settings can impact the operation of WordPress and other plugins before making changes to them.', $this->plugin_name ) . + '

'; + $rest_url = get_rest_url(); + echo '

' . + esc_html__( 'The REST API URL of this site is', $this->plugin_name ) . + ': ' . esc_html( $rest_url ) . '' . + '

'; + // Possibly output notice about blocking settings + if ( $this->fail2wp_rest_filter_require_authenticated ) { + echo '

 ' . esc_html__( 'NOTE', $this->plugin_name ) . ': ' . + esc_html__( 'If "Require authentication" is enabled, no REST API calls will be blocked for logged in users and/or authenticated requests!', $this->plugin_name ) . + '

'; + } + } public function fail2wp_settings_advanced_callback() { if ( ! is_admin( ) || ! is_user_logged_in() || ! current_user_can( 'administrator' ) ) { return; } - echo '

'. - esc_html__( 'Please make sure you understand how these settings can impact the operation of the plugin before making changes to them.', $this->plugin_name ). + echo '

' . + esc_html__( 'Please make sure you understand how these settings can impact the operation of the plugin before making changes to them.', $this->plugin_name ) . '

'; } public function fail2wp_settings_cloudflare_callback() { @@ -595,46 +1774,100 @@ public function fail2wp_settings_cloudflare_callback() { ''. '

'; } + // @since 1.1.0 + public function fail2wp_setting_rest_filter_require_authenticated() { + echo '
'; + echo ' '; + echo '
'; + } + public function fail2wp_setting_rest_filter_block_index() { + echo '
'; + echo ' '; + echo '
'; + } + public function fail2wp_setting_rest_filter_log_blocked() { + echo '
'; + echo ' '; + echo '
'; + } + public function fail2wp_setting_rest_filter_block_all() { + echo '
'; + echo ' '; + echo '
'; + } + public function fail2wp_setting_rest_filter_block_ns() { + $rest_ns = $this->fail2wp_get_rest_ns(); + if ( ! is_array( $rest_ns ) || empty( $rest_ns ) ) { + echo '
'; + echo esc_html__( 'WordPress did not return any available namespaces', $this->plugin_name ); + echo '
'; + return; + } + foreach( $rest_ns as $namespace ) { + echo '
'; + echo ' '; + echo '
'; + } + } + public function fail2wp_setting_rest_filter_block_routes() { + foreach( $this->fail2wp_rest_filter_route_list as $route ) { + echo '
'; + echo ' '; + echo '
'; + } + } + public function fail2wp_settings_rest_filter_bypass_ipv4() { + echo ''; + echo '

' . esc_html__( 'IPs matching these addresses will be allowed to make any REST API call', $this->plugin_name ) . '

'; + } + public function fail2wp_settings_rest_filter_bypass_ipv6() { + echo ''; + echo '

' . esc_html__( 'IPs matching these addresses will be allowed to make any REST API call', $this->plugin_name ) . '

'; + } public function fail2wp_settings_prefix() { - $option_val = $this->fail2wp_get_option( 'fail2wp-prefix', false ); - echo ''; + echo ''; echo '

' . esc_html__( 'The logging prefix, this should normally be left empty', $this->plugin_name ) . '

'; } public function fail2wp_setting_also_log_php() { - $option_val = $this->fail2wp_get_option( 'fail2wp-also-log-php', false ); echo '
'; echo ' '; echo '
'; } public function fail2wp_setting_cloudflare_check() { - $option_val = $this->fail2wp_get_option( 'fail2wp-cloudflare-check', false ); + $option_val = $this->fail2wp_cloudflare_check; echo '
'; echo ' '; echo '
'; } public function fail2wp_settings_cloudflare_ipv4() { - $option_val = $this->fail2wp_get_option( 'fail2wp-cloudflare-ipv4', false ); - $ip_list = @ json_decode( $option_val, true, 2 ); - if ( ! is_array( $ip_list ) ) { - $ip_list = array(); - } echo ''; echo '

' . esc_html__( 'IPs matching these addresses will be considerd to be coming from Cloudflare', $this->plugin_name ) . '

'; } public function fail2wp_settings_cloudflare_ipv6() { - $option_val = $this->fail2wp_get_option( 'fail2wp-cloudflare-ipv6', false ); - $ip_list = @ json_decode( $option_val, true, 2 ); - if ( ! is_array( $ip_list ) ) { - $ip_list = array(); - } echo ''; echo '

' . esc_html__( 'IPs matching these addresses will be considerd to be coming from Cloudflare', $this->plugin_name ) . '

'; } @@ -647,11 +1880,14 @@ public function fail2wp_settings_cloudflare_ipv6() { */ protected function fail2wp_alert_send( string $alert_message ) { // Logging prefix (i.e. "this is us") - $prefix = $this->fail2wp_get_option( 'fail2wp-prefix', false ); + $prefix = $this->fail2wp_prefix; if ( empty( $prefix ) ) { $prefix = FAIL2WP_DEFAULT_PREFIX; } // Site label + if ( empty( $this->fail2wp_site_label ) ) { + $this->fail2wp_site_label = $this->fail2wp_get_site_label( true ); + } if ( empty( $this->fail2wp_site_label ) ) { $prefix .= '(unknown.site)'; } else { @@ -682,7 +1918,7 @@ protected function fail2wp_alert_send( string $alert_message ) { $syslog_ok = true; } else { $syslog_ok = false; - error_log( 'Unable to initialize syslog interface' ); + error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): Unable to initialize syslog interface' ); } // Possibly log to PHP log as well if ( $this->fail2wp_also_log_php ) { @@ -712,21 +1948,20 @@ protected function fail2wp_get_message_user_display( \WP_User $user ) : string { $name = '`' . $name . '`'; } return( $name ); - } + } /** - * Build context based alert message. + * Possibly process Cloudflare address. + * + * If the passed IP address matches one of the configured Cloudflare addresses, + * the function attempts to fetch the actual IP address from Cloudflare + * headers. * * @since 1.0.0 - * @param string $username Username as entered when logging in. - * @param mixed $context Either WP_User or WP_Error. - * @param int $alert_type Type of notification. - * @return mixed String with alert message or false on error. + * @param string $remote_ip Remote IP address + * @return string The actual IP address */ - protected function fail2wp_make_alert_message( string $username, $context, int $alert_type ) { - $alert_message = ''; - // Fetch remote IP if set - $remote_ip = $_SERVER['REMOTE_ADDR']; + protected function fail2wp_do_cloudflare_lookup( string $remote_ip ) { if ( $this->fail2wp_cloudflare_check ) { // Setup CIDRmatch $cidrm = new \CIDRmatch\CIDRmatch(); @@ -749,10 +1984,30 @@ protected function fail2wp_make_alert_message( string $username, $context, int $ } } if ( $is_cloudflare && ! empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) { - // error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): Cloudflare IP=' . $remote_ip . ', actual IP=' . $_SERVER['HTTP_CF_CONNECTING_IP'] ); $remote_ip = $_SERVER['HTTP_CF_CONNECTING_IP']; } } + return( $remote_ip ); + } + /** + * Build context based alert message. + * + * @since 1.0.0 + * @param string $username Username as entered when logging in. + * @param mixed $context Either WP_User or WP_Error. + * @param int $alert_type Type of notification. + * @param bool $username_is_ip_address If $username contains already handled IP address + * @return mixed String with alert message or false on error. + */ + protected function fail2wp_make_alert_message( string $username, $context, int $alert_type, bool $username_is_ip_address = false ) { + $alert_message = ''; + if ( $username_is_ip_address ) { + // Username string contains IP address (some REST API requests) + $remote_ip = $username; + } else { + // Fetch remote IP if set + $remote_ip = $this->fail2wp_do_cloudflare_lookup( $_SERVER['REMOTE_ADDR'] ); + } if ( ! empty( $remote_ip ) ) { $remote_ip = ' from' . ' ' . $remote_ip; } else { @@ -817,6 +2072,12 @@ protected function fail2wp_make_alert_message( string $username, $context, int $ case FAIL2WP_ALERT_USER_ENUM: $alert_message = 'User enumeration request' . $remote_ip . $our_port; break; + case FAIL2WP_ALERT_REST_NOTAUTH: + $alert_message = 'Unauthenticated REST API request' . $remote_ip . $our_port; + break; + case FAIL2WP_ALERT_REST_BLOCKED: + $alert_message = 'Blocked REST API request' . $remote_ip . $our_port; + break; } // switch return( $alert_message ); } @@ -832,15 +2093,11 @@ protected function fail2wp_make_alert_message( string $username, $context, int $ * @param string $notify_rules JSON string with configured roles. * @return boolean true=Notify, false=Don't notify */ - protected function fail2wp_role_is_active( array $roles, string $notify_roles ) : bool { - $notify_array = @ json_decode( $notify_roles, true, 2 ); - if ( ! is_array( $notify_array ) || empty( $notify_array ) ) { - return( false ); - } + protected function fail2wp_role_is_active( array $roles, array $notify_roles ) : bool { // Lookup our selected notification roles. We could walk the other way // too, but we're likely to have less configured roles/caps than what // is available. So maybe this will save an iteration or two :-) - foreach( $notify_array as $role ) { + foreach( $notify_roles as $role ) { if ( in_array( $role, $roles ) && $roles[$role] ) { return( true ); } @@ -883,6 +2140,26 @@ protected function fail2wp_roles_merge( array $roles, string $notify_roles ) : s return( ' [' . implode( ',', $new_roles ) . ']' ); } + /** + * Get REST API namespaces from WordPress. + * + * @since 1.1.0 + * @param object $rest_server WP_REST_Server object to use or null + * @return array List of known namespaces + */ + protected function fail2wp_get_rest_ns( $rest_server = null ) { + if ( $rest_server === null ) { + if ( $this->fail2wp_rest === null ) { + if ( defined( 'FAIL2WP_REST_DEBUG' ) ) { + error_log( basename( __FILE__ ) . ' (' . __FUNCTION__ . '): Getting WP_REST_Server instance' ); + } + $this->fail2wp_rest = rest_get_server(); + } + return( $this->fail2wp_rest->get_namespaces() ); + } + return( $rest_server->get_namespaces() ); + } + /** * Send alert when user with configured role(s) login. * @@ -962,7 +2239,7 @@ public function fail2wp_auth_check( $user, string $username, string $password ) return( $user ); } $wp_error = $user; - if ( function_exists( 'mb_substr_count' ) && function_exists( 'mb_strpos' ) ) { + if ( $this->fail2wp_have_mbstring ) { if ( mb_substr_count( $username, '@' ) !== 1 || mb_strpos( $username, '@' ) === 0) { $wp_error = new \WP_Error( 'invalid_username', __('Please specify your e-mail address to login', $this->plugin_name) ); } @@ -1098,7 +2375,7 @@ public function fail2wp_login_errors( $error ) { // We're configured to notify about unknown users if ( ! empty( $_REQUEST['user_login'] ) ) { $username = sanitize_user( $_REQUEST['user_login'], false ); - if ( function_exists( 'mb_ereg_replace' ) ) { + if ( $this->fail2wp_have_mbstring ) { $username = mb_ereg_replace( ' ', '', $username ); } else { $username = str_replace( ' ', '', $username ); @@ -1139,7 +2416,6 @@ public function fail2wp_login_errors( $error ) { /** * Lost password errors - * * NOT HOOKED ATM */ /* @@ -1181,10 +2457,12 @@ public function setup_locale() { if ( ! load_plugin_textdomain( $this->plugin_name, false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ) ) { - error_log( 'Unable to load language file (' . dirname( plugin_basename( __FILE__ ) ) . '/languages' . ')' ); + /** + * We don't consider this to be a "real" error since 1.1.0 + */ + // error_log( 'Unable to load language file (' . dirname( plugin_basename( __FILE__ ) ) . '/languages' . ')' ); } } - /** * Setup CSS (admin) * @@ -1207,20 +2485,21 @@ public function run() { // Setup i18n. We use the 'init' action rather than 'plugins_loaded' as per // https://developer.wordpress.org/reference/functions/load_plugin_textdomain/#user-contributed-notes - add_action( 'init', [$this, 'setup_locale'] ); - // Setup CSS + add_action( 'init', [$this, 'setup_locale'] ); + + // Admin setup if ( is_admin() ) { - add_action( 'admin_enqueue_scripts', [$this, 'fail2wp_setup_css'] ); + add_action( 'admin_enqueue_scripts', [$this, 'fail2wp_setup_css'] ); + add_action( 'admin_menu', [$this, 'fail2wp_menu'] ); + add_action( 'admin_init', [$this, 'fail2wp_settings'] ); } - // Setup - add_action( 'admin_menu', [$this, 'fail2wp_menu'] ); - add_action( 'admin_init', [$this, 'fail2wp_settings'] ); - add_action( 'wp_loaded', [$this, 'fail2wp_wp_loaded'] ); - add_action( 'parse_request', [$this, 'fail2wp_parse_request'] ); + // Other setup + add_action( 'wp_loaded', [$this, 'fail2wp_wp_loaded'] ); + add_action( 'parse_request', [$this, 'fail2wp_parse_request'] ); + // add_action( 'wp', [$this, 'fail2wp_wp_main'] ); // add_action( 'pre_get_posts', [$this, 'fail2wp_pgp'] ); - - // Plugin deactivation + // Plugin deactivation, not needed atm :-) // register_deactivation_hook( __FILE__, [$this, 'fail2wp_deactivate_plugin'] ); } diff --git a/fail2wp/includes/fail2wp_misc.inc.php b/fail2wp/includes/fail2wp_misc.inc.php new file mode 100644 index 0000000..ee38eb1 --- /dev/null +++ b/fail2wp/includes/fail2wp_misc.inc.php @@ -0,0 +1,105 @@ + + * + * fail2wp_misc.inc.php + * Copyright (C) 2021 Joaquim Homrighausen; all rights reserved. + * Development sponsored by WebbPlatsen i Sverige AB, www.webbplatsen.se + * + * This file is part of Fail2WP. Fail2WP is free software. + * + * You may redistribute it and/or modify it under the terms of the + * GNU General Public License version 2, as published by the Free Software + * Foundation. + * + * Fail2WP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Fail2WP package. If not, write to: + * The Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + */ + +// If this file is called directly, abort. +if ( ! defined( 'WPINC' ) ) { + if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) { + error_log( 'fail2wp-uninstall: (misc) WPINC not defined ' ); + } + die; +} +if ( ! defined( 'ABSPATH' ) ) { + if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) { + error_log( 'fail2wp-uninstall: (misc) ABSPATH not defined ' ); + } + die( '-1' ); +} +if ( ! defined( 'FAIL2WP_WORDPRESS_PLUGIN' ) ) { + if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) { + error_log( 'fail2wp-uninstall: (misc) FAIL2WP_WORDPRESS_PLUGIN not defined ' ); + } + die( '-1' ); +} + + +/** + * Remove all settings from WordPress database. + * + * This can, possibly, be used by more than one module. + * + * @since 1.1.0 + */ +function fail2wp_misc_delete_all_settings() { + + if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) { + error_log( 'fail2wp-uninstall: ' . __FUNCTION__ . ' start' ); + } + delete_option( 'fail2wp-site-label' ); + delete_option( 'fail2wp-prefix' ); + delete_option( 'fail2wp-roles-notify' ); + delete_option( 'fail2wp-roles-warn' ); + delete_option( 'fail2wp-unknown-warn' ); + delete_option( 'fail2wp-reguser-warn' ); + delete_option( 'fail2wp-reguser-warn-role' ); + delete_option( 'fail2wp-reguser-force' ); + delete_option( 'fail2wp-reguser-force-role' ); + delete_option( 'fail2wp-reguser-username-length' ); + delete_option( 'fail2wp-reguser-username-ban' ); + delete_option( 'fail2wp-reguser-useremail-require' ); + + delete_option( 'fail2wp-rest-filter-log-blocked' ); + delete_option( 'fail2wp-rest-filter-block-all' ); + delete_option( 'fail2wp-rest-filter-block-index' ); + delete_option( 'fail2wp-rest-filter-block-ns' ); + delete_option( 'fail2wp-rest-filter-block-routes' ); + delete_option( 'fail2wp-rest-filter-require-authenticated' ); + delete_option( 'fail2wp-rest-filter-ipv4-bypass' ); + delete_option( 'fail2wp-rest-filter-ipv6-bypass' ); + + delete_option( 'fail2wp-settings-dbversion' ); + delete_option( 'fail2wp-settings-remove' ); + delete_option( 'fail2wp-settings-remove-generator' ); + delete_option( 'fail2wp-settings-remove-feeds' ); + + delete_option( 'fail2wp-also-log-php' ); + delete_option( 'fail2wp-block-user-enum' ); + delete_option( 'fail2wp-log-user-enum' ); + delete_option( 'fail2wp-block-username-login' ); + delete_option( 'fail2wp-secure-login-message' ); + + delete_option( 'fail2wp-cloudflare-check' ); + delete_option( 'fail2wp-cloudflare-ipv4' ); + delete_option( 'fail2wp-cloudflare-ipv6' ); + + if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) { + error_log( 'fail2wp-uninstall: ' . __FUNCTION__ . ' end' ); + } +} diff --git a/fail2wp/languages/fail2wp-sv_SE.mo b/fail2wp/languages/fail2wp-sv_SE.mo index 4e040f04abb168d1e4335467891d64d1e0071962..7bd2925f8d0d7d062225e534e1a33ff255e24918 100644 GIT binary patch literal 14600 zcmb`Nd5k1iea9bQV_0s3xsURaU>jz7jbn^ipR>EO-tk_;%y`%4h^gtW?y2qWs-~)X zW``pX1OkFc{Tpg+P?g_xI}R z*)d)sT2u3_s(#1s{qFaB{ZH3k^{C_XUapsOU3Hanp9KHo8vgP5$Uf(;20scu4g3Z0 z`QRtO+ri%kp9}tL`To1$wLI^8hI20fUje=Zyal`tJOMrjTnFy~Gf?e53!Vc16kG;h z_)IQvP~y*kxAOcb_$Khrz!!m6J)>JVg>+KC zxA5;SFb1CoeiBsw&x3b>Uj#Mp)x7M02SLpfgIae1ia#F$kAfcqkAq(Z-vhpqM!y06 zDmVh~WRg|zQy@*;4?swA&w^<;fiDB+z(w%YUJETcdVJHfvr;5$I^>mrDV-DBWw;2(oe2mc!szn{V+n(w9HPl7ApS@1ll@&6ip z9r!g+@_ze)c5x>SPJH#*8O#`3ob*1^yv3Nt@ANZ`tc3$Zt$uXJNHcR zFvw7D85E!Tpy=BKHSWj3o53G|T5pFz)c#IT>sbNCzqf&s*ZaX`@M9pNa9;)K>b?mE z;8S1f`FaC*8_#bC9|V61JOzFe{3-CxmpOMoI00`2{}2>^{{=h<-h>fx>w4f6d_Smm z-vv*CuX=@ZhrlryfS&;+zrO*s?yrDa&%c3)*!>syCh(e9`tKJ&^*;sP1wK+f{}p(E z=WAc(-0Q(ZU;q}N=zbKu1^gl?{(m1-`>O-=`$?=Fd@|4HuJ`;KgRsVJfzs7!iCC_zGdND5Xy`bj#IQX;R--B-kA3}-6*H43Yf}aCXUH2VO^56Fw zZ;!7A7kJ(RCI8QZxFYv0Q0=}Cif`B5;Kz9-sQKOsz8ky~~=Klhyb^j$OzWif}{|QP@ zpN{cqya&On!1saofFA^9zh3|)r@sZ4z;AV6@WbWl(beIZ*ulHBjrh1WK>| z0{j5@&!GBsne3V1hd{OaMesS`Z-D{$IZ*ul3iu%S@8I*mdr^8!&#i!3cM9^A`#AUp z@Q=Ws2EPNU-x|iE_rC+a9sD|osknt(eH;nFdwKp8sBymmz8}1XPU7Q7K#lWzp!oYV zlu7ijfKLSr@EPE{!E3<}gV%w-0^SRL1{B}E4ITr(2i^}JMj4KRKM&px{v8Nu?%6!3 z{~-_+au0%t)O`Syynh!||Hr^5frq$GbE&Q9%enN~$F<+?nt%6lyUq0=muyFLqh|Jr z`2SX}kW2hr=Mw)Bmwhz1H5u(t$T~iPxyIeO|CQQ>tGV9EC7l$%PjIDN;$ii9FE^5* z{QA4N60R|q^kR!kpQm`R|CoYX;fh=DZsZ=3)t`sBdpg%0T!`3gY6xD#)#s9~Y;Z}& z&*wtT>?0eHJ)h?~$%WdQ-xz@UJef;+f_t-%bY36nvOd!FyX?;EGiu>7u9tE>%+=$1 z0hd0@9_&Bi#XfK2|2zDx`Df{tJ|nId+nv|Pv)sRi>oC`;^8Ex{E$^RJ;&Z{{x?fmN zr-fVSZH7rV>bZqco(7|I5EN;!d~8{_ZlNfm@ubkJX)+G;VUR`PC|Ft!wl<<9I6O+H zz5Xc7BE5?9pcfTUw_vbCVUk3>V46o+5{{#s7kS(bx@ppn2h%Jp;xuuGMrn5#grgCo zzH=Jo1u)j^RKL~SL6l6#Wvlu*^3w;QUtAvC z7DlgK}v>Ka59HWQyGKa1cRuD#;aLoCG7@Nz62%Q-u7^rde+})9XAc ziZ~er;eeJoTC))ao8f30)pAEAHli#ldy1$T6>7cD(h<#S{c7F%bu?e;nFXZdag=pq zv@@ShCTUg#VbTk_)4WK>@w-gnJR3|blhG6*N@c@MmJ+UyqM)CpW5#J|Xpr`85l3lq z%X*ju`F4&$1idgCr^#FZ{dqj5do~x0nJHM0f?k?L0eYCnXaW5j_Gq~_IqcjBHzNq# z4x)Y^i|H`$WZUK{qVq)nY1XnIN28wDADy=)Nk=O-n^;2h2#%VXxyAW0ga%=+Cvoh0 z^aPH>qPv0SBgYVWjaFM@&ibJ*iMGsW>)y63>*W?b4W*QGca-+X_L)p4RwI6^ZzV0aKR7Tu~)vg@I|0Z*I5uMG01q zYIqIJ-F=v6>Xw#et#CwI3m+k|WFAVt72}u?DCU}Ou|5VKheI#58H>NNVK5-6L}mjq zY!|g;Rw-SJWp1e->|enf#@L^HP?QvS47tnw($*-Es`lpaAHfU}4ZUXZ(A!5mxT*oS zsFS=O%AWKNNNUZ%d|Nr+>ovKwDbh@NI*!mmNJQy^eiV(w0SF(*d0tcAGA1*JxNpr( zz5D4D3k-uvmO}Fwh87zdpQXz*AcJAU0#8#bNiQ|Wh$P|yrP7Q995fb#GJ*Nw`*=pL zOr;&Oy8d(|KkK>1;4EPyoXDs&MmLIOaI#Kg=3FpHQ!RZnj<$^QbF$ws7;5M(Tv|um z@b-oa*Kl&#lj%Boz5&-ZZJzMZ z1HBcZMNvOK&m&?|WRr~C$!NTeUT?&cU@OcL+U4$KoW$el7!Q{WP&hnNwQiVm%ls`j z-yC!|!Ym|D$#`>fDJtBlDE1Db9;E78j^<9CUR!jhN+)S5-ce?#HB!J}+VUW#pKN(dz6Ys`o4sDe8;Y3-JsCX%2ak zgH*_l`egu;JKA|YmY`;R&1dAia9$PzkVlG%&U7ZV}qT8t&^TNhkAQM#}?d8=s za=4|mtB7DNjq|p{D!(QAO!00Ne@iN6#u`|P14ZU=s~M_cdEP6hs9zTw25)*R`(A#Wv+Pjj93_n$+pmk~xy3gXyjJOalD$F$BjpX1g)V=S$}lD zv|7(tTHYG|VrXtn5ww&bj>N5DJ~cDt*l|PNtNLui8uU};p7J}D90OaJPcE=fEJp!` zvApqGFKoUQF-wW$8|jp-1WCylIc}SjqdvZs?ftfOYgBCud&Kbu>)XNoX^7s8gJbD9 ziw7Gaa!X7_^9h!UW9+Hb*=T)z8PnF6aj=S)h}ldo9O~G>3UJltf|-5VE=HFMw<~ZW zY)iYUS^@3K)%)SfhhTpr$o;ryw$zvwH_4PlpV&*%XH^DqTDx$o8%UMpLL`lnPjgiS zH?M9}H_gdsM>A|ftx#?YqP?^`9pn1SxLXJGu+XwwX`kOhc$Zmn88X(n6?A%iJRR<2KsUMB#VNnjx1~+ZVWvtU6<<`2kFtJq}@icVY1GeQ7 zpGdQ{De-ZUR=y_eB+>sczVvxk|TFnq3e ziCSN!BFzpl?6LLqTjw;(pJ0e}Ws^!wG2+@%8{52s-eP7&gdeAuk{f6{HP+_aoh5Y- zeBPS&FyT6>KQObfNpbKk#bBViM1V?)%ZH0@*Kq~wQUlX(gNb7 zw$ar~kTzNM%=}HWVryM9t7=Y~nWg9OXY)(F`7=I?J7{IPTec>_>pQRS+`Mlk(#~@J zWIl*{^M|H`e10uG80=d^Yek}`=96z3B2lw{;cb-c((tEdX_vB7;_nkbto4(0lN}+ImEegj6gVnQ(N0wHP z1wXv;!64s!cy)2#$)%Hv&6>0lN^na1=hwC;kgIyw0cMMnJ8a(uPwskP?dbgNO*>KD zkFxp2gvtT|@nCTKdR**tr^jx=ircsd+ee1M&V@~UL?KVgMs!{p;EwKG%JQ8{RH6&E zZJWciN1cFHgt6f$9)uJa=R+&0@iN)DFjKUo)>!OZpv3A{Cw!Hu_8TUfI~S53S{;rl z6jL^D7ZrV}7ps>;oq^__{Kkl^lH?6=K@?pun2d+ z3=E7LP!`1IhG7UzXv5A$#i5;xiK@xBUjFJfxAVcIM`wyV0ki0x1iD)Eu2yoQ@CTH z zA~s?Xws>*Cu2LoRAKIrXY=U z^Ok4g1VT;+e`QNB2LuCTY>14C``V_2*`!DF?K!<|(kc5P>Q|1??)Nx+s90&*Sa!4f zh0$mXH4xPv1$TAd&PAiaHNF->V+Nj=@(a?0{?5g$wBkKHz!P*fPl7h1SLm+TYE|x* zIaO{xBXk7&k7q;+ZEp!iF-{cz?Keji4hyQu*kzDIwok`3m@#;l(=#>kEh(Scd7NX^4Mj_SP${9Vg>q&aWXx18V2tU zbu`S>NvrO@j=HDg-p(bq-VHafvlymAtGqn}bjry+bw0>IM5Fv=xF88rfbx=vHg+x{ z7;(b-DGD=P$w2wgssg`<(Jfi|t188x9#9Q{4^hhn%-NilD#*+rv24`1c#7KWA5dCj zu!Mhkb7E0DXkIqz3BpC098|i6=Io_c>yex=2Wr&r8;5H}c}8e;_W6V#m_puIQm^Us z#!otQFt&fs-1I!d93wf3(xx(Sx=|ij6n}g-3#m7rO z=`L3;OCl+#7DEW8TpkYyNpTXoQ!{>_(-`<8%NU?>WcoZAQJIzX`IeRW%Dx$=Dx>Dx z(n9Qz{VO4=b4&$bYWti|89UY^h9$@Ckj<8U2n)g!q1>hD)vjKTTdb_TPuzvUn~zuQ zZ|twG_bcgDjP=fDf>C?B_w@m{UJErf%;OpLj>-$!emqiKZRN%WSyZFRkJ>SljH@ z4(_yFS+MIFt5Jxk&4S;qsS0jo5!?1tZ1GL{BoZ0CD>!Vf!xhv0e;wV4%eIwkAwR=1 zt0>28gj_UKMc728hIW)pP*ETpox|L9Oh2<+f5f2Jl#>szi}m)OSRF;7zl`|RR@-mN zX1CV-_=|O-tA#sa`|(N>O}E|_;_&t|BDMtEw7S6W!h}YkAs- zHe}_)SoIoa_d?LN0{7}Qw>q}j9lN|Q+7+OPKspa@&L+lez6hi(u~PN+^Ym>N}l zW_rtk@r<=|F{fY0-Cd?i;X}WCunAQgnUGUiT-Gq<5yk1Daunym7m55m45;=Z5gBxz zUtYArMKtA6ucu8XxwaivMC}of(_UZX0-hWkbn5R0GxSrK(Iw(8~HyCX?K>hYP}%svXaK zb&6qBvO#P-3Y(xtC;S-({=;N>*@mK3`6VyYS}j|vf-3qv)kb%QTlzJ{K#8qNd7L&< z_@JZXh_*Vn=mckCep-dTkUdixM}NGs!MqULPeQ&B%5th@u9_nJtW(4{|BvX BT|)o> delta 1483 zcmZY8Urbw79Ki8Y!EJ27Mz@xK0;NU%wreS{fy|+SL75tcY_N#pZrfX|=^xg6!MSJ_ zjT(J1X1@@h{PTc+K+n4=3<2CU6uVuk<(ZApMVU0zbzi z_$LlyJ%cpjB<{y$ek*uU5HofPm-pW8(ry8#Yxl`7mzv4Td1Y} zxbplTe2spB*`CAOxQKHN#vH}>@i=~m6xYxnW(&}hyA%a93(N1&_+9CwiHv9?7dk&^gFEky$ zO7~w^q>Zu5e-RFRIT?M{s~1{~(7o2xYAH2oEw#3OqPu*z``6S8)>QhfsAq-(m;eza zLWCYzt=RoU`Ov6gz0h>e{@b)7dQP-y6}pIfwIeU@Pc}H&R3KF>WU{O4CCkkg3t}C} z$Y$+$L#9{?Oy-K~=}gWl*@4+JVs(XOu`W?}Dw4Odxqy{UmuyF_*PoQz2j{lmK6Jq& z)84S$@s3IV;hrc`{|PPviUbY#&k zIb?ocAVXU5XVwCPM`PP(121{x_s$k62J><)H04WJ1=lWF|69{W?WxFG(Qz$ZjU&PC zbJE_^F3\n" "Language-Team: SWEDISH \n" @@ -21,7 +21,7 @@ msgstr "" msgid "Unknown" msgstr "Okänd" -#: fail2wp.php:240 fail2wp.php:245 +#: fail2wp.php:240 fail2wp.php:245 fail2wp.php:1011 fail2wp.php:1016 msgid "Unknown role" msgstr "Okänd roll" @@ -33,7 +33,7 @@ msgstr "" "Tillhandahåller autentiseringsloggning och säkerhetsfunktioner för " "WordPress, användbar tillsammans med Fail2ban" -#: fail2wp.php:289 +#: fail2wp.php:289 fail2wp.php:1060 msgid "Basic configuration" msgstr "Baskonfiguration" @@ -41,47 +41,47 @@ msgstr "Baskonfiguration" msgid "Logging" msgstr "Loggning" -#: fail2wp.php:295 +#: fail2wp.php:295 fail2wp.php:1072 msgid "Advanced" msgstr "Avancerat" -#: fail2wp.php:298 +#: fail2wp.php:298 fail2wp.php:1075 msgid "Cloudflare" msgstr "Cloudflare" -#: fail2wp.php:301 +#: fail2wp.php:301 fail2wp.php:1081 msgid "About" msgstr "Om" -#: fail2wp.php:307 +#: fail2wp.php:307 fail2wp.php:1086 msgid "" "One or more of openlog(), closelog(), and/or syslog() seem to be missing on " "this system" msgstr "" -"En eller flera av openlog(), closelog(), och/eller syslog() verkar saknas " -"på detta system" +"En eller flera av openlog(), closelog(), och/eller syslog() verkar saknas på " +"detta system" -#: fail2wp.php:355 +#: fail2wp.php:355 fail2wp.php:1343 msgid "Thank you for installing" msgstr "Tack för att du installerat" -#: fail2wp.php:356 +#: fail2wp.php:356 fail2wp.php:1344 msgid "" -"This plugin provides security functions and integration between " -"WordPress and" +"This plugin provides security functions and integration between WordPress and" msgstr "" -"Detta plugin tillhandahåller säkerhetsfunktioner samt integration " -"mellan WordPress och" +"Detta plugin tillhandahåller säkerhetsfunktioner samt integration mellan " +"WordPress och" -#: fail2wp.php:360 +#: fail2wp.php:360 fail2wp.php:1348 msgid "Commercial support and customizations for this plugin is available from" -msgstr "Kommersiell support och anpassningar av detta tillägg tillhandahålls av" +msgstr "" +"Kommersiell support och anpassningar av detta tillägg tillhandahålls av" -#: fail2wp.php:362 +#: fail2wp.php:362 fail2wp.php:1350 msgid "in Stockholm, Sweden. We speak Swedish and English" msgstr "i Stockholm. Vi pratar svenska och engelska" -#: fail2wp.php:364 +#: fail2wp.php:364 fail2wp.php:1352 msgid "" "The plugin is written by Joaquim Homrighausen and sponsored by WebbPlatsen i " "Sverige AB." @@ -89,7 +89,7 @@ msgstr "" "Detta tillägg är utvecklat av Joaquim Homrighausen och sponsrat av " "WebbPlatsen i Sverige AB." -#: fail2wp.php:365 +#: fail2wp.php:365 fail2wp.php:1353 msgid "" "If you find this plugin useful, the author is happy to receive a donation, " "good review, or just a kind word." @@ -97,7 +97,7 @@ msgstr "" "Om du tycker detta tillägg är användbart så uppskattar utvecklaren en " "donation, bra omdöme och/eller positiv kommentar." -#: fail2wp.php:366 +#: fail2wp.php:366 fail2wp.php:1354 msgid "" "If there is something you feel to be missing from this plugin, or if you " "have found a problem with the code or a feature, please do not hesitate to " @@ -106,90 +106,90 @@ msgstr "" "Om du anser att något saknas i detta tillägg eller om du har hittat ett fel " "i koden eller en funktion, tveka inte att höra av dig till" -#: fail2wp.php:381 +#: fail2wp.php:381 fail2wp.php:1369 msgid "Site label" msgstr "Webbplatsnamn" -#: fail2wp.php:382 +#: fail2wp.php:382 fail2wp.php:1370 msgid "Block user enum" msgstr "Blockera 'user enum'" -#: fail2wp.php:383 +#: fail2wp.php:383 fail2wp.php:1371 msgid "Block username login" msgstr "Blockera användarnamn" -#: fail2wp.php:384 +#: fail2wp.php:384 fail2wp.php:1372 msgid "Secure login messages" msgstr "Säkra felmeddelanden" -#: fail2wp.php:385 +#: fail2wp.php:385 fail2wp.php:1374 msgid "Other settings" msgstr "Andra inställningar" -#: fail2wp.php:386 +#: fail2wp.php:386 fail2wp.php:1377 msgid "Remove settings" msgstr "Radera inställningar" -#: fail2wp.php:389 +#: fail2wp.php:389 fail2wp.php:1389 msgid "Successful login" msgstr "Lyckad inloggning" -#: fail2wp.php:390 +#: fail2wp.php:390 fail2wp.php:1390 msgid "Unsuccessful login" msgstr "Misslyckad inloggning" -#: fail2wp.php:395 +#: fail2wp.php:395 fail2wp.php:1405 msgid "Logging prefix" msgstr "Loggningsprefix" -#: fail2wp.php:396 +#: fail2wp.php:396 fail2wp.php:1406 msgid "Also log to PHP log" msgstr "Logga även till PHP-logg" -#: fail2wp.php:399 +#: fail2wp.php:399 fail2wp.php:1409 msgid "Check for Cloudflare IP" msgstr "Hantera Cloudflare IP" -#: fail2wp.php:400 +#: fail2wp.php:400 fail2wp.php:1410 msgid "Cloudflare IPv4" msgstr "Cloudflare IPv4" -#: fail2wp.php:401 +#: fail2wp.php:401 fail2wp.php:1411 msgid "Cloudflare IPv6" msgstr "Cloudflare IPv6" -#: fail2wp.php:484 +#: fail2wp.php:484 fail2wp.php:1574 msgid "" "The site name to use for logging, defaults to your site name if left empty" msgstr "" "Namn för webbplatsen att använda för loggning, hämtas från WordPress om " "fältet är tomt" -#: fail2wp.php:529 +#: fail2wp.php:529 fail2wp.php:1600 msgid "Unknown users" msgstr "Okända användare" -#: fail2wp.php:537 +#: fail2wp.php:537 fail2wp.php:1607 msgid "User enumeration attempts (i.e. your.site/...?author=nnn)" msgstr "Försök att hämta användarnamn (t ex din.sajt.../?author=nnn)" -#: fail2wp.php:545 +#: fail2wp.php:545 fail2wp.php:1705 msgid "Remove all plugin settings and data when plugin is uninstalled" msgstr "Ta bort alla inställningar när tillägget avinstalleras" -#: fail2wp.php:553 +#: fail2wp.php:553 fail2wp.php:1712 msgid "Block user enumeration attempts (i.e. your.site/...?author=nnn)" msgstr "Blockera försök att hämta användarnamn (t ex din.sajt.../?author=nnn)" -#: fail2wp.php:561 +#: fail2wp.php:561 fail2wp.php:1719 msgid "Require users to login with their e-mail address" msgstr "Kräv inloggning med e-postadress" -#: fail2wp.php:569 +#: fail2wp.php:569 fail2wp.php:1726 msgid "Change login failure messages to contain less detail" msgstr "Ändra felmeddelanden till färre detaljer vi misslyckad inloggning" -#: fail2wp.php:577 +#: fail2wp.php:577 fail2wp.php:1753 msgid "" "Please make sure you understand how these settings can impact the operation " "of the plugin before making changes to them." @@ -197,11 +197,11 @@ msgstr "" "Var säker på att du förstår hur dessa inställningar kan förändra hur " "tillägget fungerar innan du ändrar dem." -#: fail2wp.php:585 +#: fail2wp.php:585 fail2wp.php:1761 msgid "These settings allows the plugin to better interact with Cloudflare." msgstr "Dessa inställning förbättrar hur tillägget fungerar med Cloudflare." -#: fail2wp.php:587 +#: fail2wp.php:587 fail2wp.php:1763 msgid "" "If your site is not published via Cloudflare, you can safely ignore these " "settings." @@ -209,41 +209,391 @@ msgstr "" "Om du inte använder Cloudflare för din sajt så behöver du inte ändra något " "här." -#: fail2wp.php:589 +#: fail2wp.php:589 fail2wp.php:1765 msgid "For an updated list of Cloudflare IPs, please use this link" -msgstr "För en uppdaterad lista med Cloudflares IP-adresser, vänligen använd " -"denna länk" +msgstr "" +"För en uppdaterad lista med Cloudflares IP-adresser, vänligen använd denna " +"länk" -#: fail2wp.php:599 +#: fail2wp.php:599 fail2wp.php:1840 msgid "The logging prefix, this should normally be left empty" msgstr "Loggningsprefix, detta skall i normala fall vara tomt" -#: fail2wp.php:606 +#: fail2wp.php:606 fail2wp.php:1846 msgid "Log the same information to PHP log using error_log()" msgstr "Logga samma information till PHP:s logg via error_log()" -#: fail2wp.php:614 +#: fail2wp.php:614 fail2wp.php:1854 msgid "Attempt to unmask real IP when Cloudflare IP is detected" msgstr "Försök att avkoda den verkliga IP-adressen när Cloudflare upptäcks" -#: fail2wp.php:626 fail2wp.php:637 +#: fail2wp.php:626 fail2wp.php:637 fail2wp.php:1861 fail2wp.php:1867 msgid "" "IPs matching these addresses will be considerd to be coming from Cloudflare" msgstr "" "IP-adresser som matchar dessa kommer att hanteras som Cloudflare-adresser" -#: fail2wp.php:962 fail2wp.php:966 +#: fail2wp.php:962 fail2wp.php:966 fail2wp.php:2239 fail2wp.php:2243 msgid "Please specify your e-mail address to login" msgstr "Vänligen ange din e-postadress för att logga in" -#: fail2wp.php:989 +#: fail2wp.php:989 fail2wp.php:2261 msgid "E-mail address" msgstr "E-postadress" -#: fail2wp.php:1094 fail2wp.php:1122 +#: fail2wp.php:1094 fail2wp.php:1122 fail2wp.php:2366 fail2wp.php:2399 msgid "Invalid login credentials, please try again." msgstr "Ogiltig inloggningsinformation, försök igen." -#: fail2wp.php:1126 +#: fail2wp.php:1126 fail2wp.php:2403 msgid "Lost password" msgstr "Återställ lösenord" + +#: fail2wp.php:450 +msgid "Settings " +msgstr "Inställningar " + +#: fail2wp.php:548 +msgid "You are not currently logged in." +msgstr "Du är inte inloggad." + +#: fail2wp.php:629 fail2wp.php:667 fail2wp.php:686 fail2wp.php:706 +msgid "No route was found matching the URL and request method." +msgstr "Ingen route hittades som motsvarade webbadressen och metod för begäran." + +#: fail2wp.php:743 fail2wp.php:754 fail2wp.php:765 +msgid "Invalid username, please try again." +msgstr "Ogiltigt användarnamn, försök igen." + +#: fail2wp.php:747 fail2wp.php:793 +msgid "Invalid e-mail address, please try again." +msgstr "Ogiltig e-postadress, försök igen." + +#: fail2wp.php:811 +msgid "New user role default has been reset according to your" +msgstr "Förvald roll för nya användare har återställts enligt dina" + +#: fail2wp.php:815 +msgid "settings" +msgstr "inställningar" + +#: fail2wp.php:834 +msgid "Notification about new user role from" +msgstr "Avisering om ändring av förvald roll" + +#: fail2wp.php:836 +msgid "This is a notification from" +msgstr "Detta är en avisering från" + +#: fail2wp.php:838 +msgid "" +"The role for newly registered users has been reset to your configured " +"setting." +msgstr "" +"Den förvalda rollen för nya användare har återställts till den som du " +"konfigurerat." + +#: fail2wp.php:840 +msgid "This notification was sent from the site" +msgstr "Denna avisering skickades från sajten" + +#: fail2wp.php:842 +msgid "You may access the admin interface to the site here" +msgstr "Du når administrationsgränssnittet här" + +#: fail2wp.php:859 +msgid "User registration is enabled, but the new user role does not match" +msgstr "Användarregistrering är aktiv, men den förvalda rollen matchar inte" + +#: fail2wp.php:868 fail2wp.php:898 fail2wp.php:927 +msgid "Go to" +msgstr "Gå till" + +#: fail2wp.php:870 fail2wp.php:900 fail2wp.php:929 +msgid "General settings" +msgstr "Allmänna inställningar" + +#: fail2wp.php:872 fail2wp.php:902 fail2wp.php:931 +msgid "to check your membership and new user settings." +msgstr "för att kontrollera inställningarna för medlemsskap och nya användare." + +#: fail2wp.php:889 +msgid "User registration is enabled, and new users will be administrators" +msgstr "Användarregistrering är aktiv och nya användare blir administratörer" + +#: fail2wp.php:918 +msgid "User registration is enabled, but no role has been configured" +msgstr "Användarregistrering är aktiv, men ingen förvald roll har konfigurerats" + +#: fail2wp.php:1057 +msgid "" +"Provides authentication security functions for WordPress, plays nicely with " +"Fail2ban and Cloudflare" +msgstr "" +"Tillhandahåller autentiseringsloggning och säkerhetsfunktioner för " +"WordPress, fungerar även bra med Fail2ban och Cloudflare" + +#: fail2wp.php:1063 +msgid "New users" +msgstr "Nya användare" + +#: fail2wp.php:1066 +msgid "User logging" +msgstr "Loggning" + +#: fail2wp.php:1069 +msgid "REST API" +msgstr "REST API" + +#: fail2wp.php:1078 +msgid "Import/Export" +msgstr "Import/Export" + +#: fail2wp.php:1108 +msgid "" +"Some data was filtered, please make sure you paste the content exactly as " +"copied" +msgstr "" +"Data har filtrerats, kontrollera att du klistrar in innehållet exakt som " +"det kopierades" + +#: fail2wp.php:1113 fail2wp.php:1121 +msgid "Please make sure you paste the content exactly as copied" +msgstr "Kontrollera att du klistrar in innehållet exakt som det kopierades" + +#: fail2wp.php:1126 +msgid "" +"Plugin version mismatch. You can only import exported data from the same " +"version as the one currently installed" +msgstr "" +"Versionerna matchar inte varandra. Du kan bara importera exporterat data " +"från samma version som den som är installerad" + +#: fail2wp.php:1190 +msgid "The following settings were ignored during the import" +msgstr "Följande inställningar ignorerades vid importen" + +#: fail2wp.php:1197 +msgid "Settings were successfully imported" +msgstr "Inställningarna har importerats" + +#: fail2wp.php:1201 +msgid "The setting \"Site label\" may need manual correction" +msgstr "Inställningen \"Webbplatsnamn\" kan behöva korrigeras" + +#: fail2wp.php:1208 +msgid "This tab can be used to import and export settings for the plugin." +msgstr "Denna flik kan användas för att importera och exportera inställningar." + +#: fail2wp.php:1212 fail2wp.php:1221 +msgid "Import settings" +msgstr "Importera inställningar" + +#: fail2wp.php:1217 +msgid "Paste settings in this field to import them" +msgstr "Klistra in inställningarna i detta fält för att importera dem" + +#: fail2wp.php:1226 +msgid "Export settings" +msgstr "Exportera inställningar" + +#: fail2wp.php:1272 +msgid "Unable to create data for export" +msgstr "Kunde inte skapa data för export" + +#: fail2wp.php:1280 +msgid "Copy the text in this field to export your settings to another site" +msgstr "Kopiera texten i detta fält för att exportera inställningar till en annan sajt" + +#: fail2wp.php:1304 +msgid "" +"This is logged to the system's authentication log, which allows Fail2ban to " +"dynamically block offending IP addresses." +msgstr "" +"Detta loggas till systemetes autentiseringslogg, som gör det möjligt för " +"Fail2ban att på dynamisk väg blockera IP-adresser." + +#: fail2wp.php:1306 +msgid "" +"Configuration of the Fail2ban system daemon, or similar, must be done " +"outside of WordPress for this to have any effect." +msgstr "" +"Konfiguration av Fail2ban, eller liknande, måste göras utanför WordPress för " +"att dessa inställningar skall ha någon effekt." + +#; fail2wp.php:1357 +msgid "There is more documentation available at" +msgstr "Det finns mer dokumentation på" + +#: fail2wp.php:1375 +msgid "Remove generator info" +msgstr "Inaktivera generatorinfo" + +#: fail2wp.php:1376 +msgid "Remove feeds" +msgstr "Inaktivera flöden" + +#: fail2wp.php:1380 +msgid "Membership warnings" +msgstr "Medlemsskapsvarningar" + +#: fail2wp.php:1381 +msgid "Check for role" +msgstr "Jämför med roll" + +#: fail2wp.php:1382 +msgid "Force role" +msgstr "Tvinga roll" + +#: fail2wp.php:1383 +msgid "Role to force" +msgstr "Tvingad roll" + +#: fail2wp.php:1384 +msgid "Minimum username length" +msgstr "Minsta antal tecken för användarnamn" + +#: fail2wp.php:1385 +msgid "Banned usernames" +msgstr "Spärrade användarnamn" + +#: fail2wp.php:1386 +msgid "E-mail must match" +msgstr "E-post måste matcha" + +#: fail2wp.php:1392 +msgid "Log user enum" +msgstr "Logga 'user enum'" + +#: fail2wp.php:1395 +msgid "Require authentication" +msgstr "Kräv autentisering" + +#: fail2wp.php:1396 +msgid "Log blocked requests" +msgstr "Logga blockerade anrop" + +#: fail2wp.php:1397 +msgid "Block index requests" +msgstr "Blockera index-anrop" + +#: fail2wp.php:1398 +msgid "Block all requests" +msgstr "Blockera alla anrop" + +#: fail2wp.php:1399 +msgid "Block specific namespaces" +msgstr "Blockera specifika namespaces" + +#: fail2wp.php:1400 +msgid "Block specific routes" +msgstr "Blockera specifika sökvägar" + +#: fail2wp.php:1401 +msgid "Bypass blocks for IPv4" +msgstr "Tillåt från IPv4" + +#: fail2wp.php:1402 +msgid "Bypass blocks for IPv6" +msgstr "Tillåt från IPv6" + +#: fail2wp.php:1615 +msgid "Warn about odd membership/registration settings" +msgstr "Varna för udda medlemsskap-/registreringsinställningar" + +#: fail2wp.php:1633 +msgid "Check WordPress setting against the value configured here" +msgstr "Jämför WordPress inställning med det som konfigurerats här" + +#: fail2wp.php:1637 fail2wp.php:1666 +msgid "No available roles (?)" +msgstr "Inga tillgängliga roller (?)" + +#: fail2wp.php:1645 +msgid "Force new user registration settings to a role" +msgstr "Tvinga inställningen för nya användare till en roll" + +#: fail2wp.php:1663 +msgid "Force WordPress setting to the value configured here" +msgstr "Tvinga WordPress-inställning till värdet som konfigurerats här" + +#: fail2wp.php:1672 +msgid "Minimum length of usernames, 2-200 characters, 0 ignores the setting" +msgstr "Minsta antal tecken för användarnamn, 2-200 tecken, 0 avaktiverar kontrollen" + +#: fail2wp.php:1678 +msgid "These usernames will be blocked for new user registrations" +msgstr "Dessa användarnamn blockeras vid registrering av nya användare" + +#: fail2wp.php:1684 +msgid "E-mail address must match at least one of these for new users" +msgstr "E-postadressen för nya användare måste matcha en av dessa" + +#: fail2wp.php:1690 +msgid "Remove \"Generator\" output in HTML, RSS, etc." +msgstr "Ta bort \"Generator\"-infor från HTML, RSS, etc." + +#: fail2wp.php:1697 +msgid "Remove RSS and Atom feeds" +msgstr "Inaktiverar RSS- och Atom-flöden" + +#: fail2wp.php:1734 +msgid "" +"Please make sure you understand how these settings can impact the operation " +"of WordPress and other plugins before making changes to them." +msgstr "" +"Var säker på att du förstår hur dessa inställningar kan förändra hur " +"WordPress och andra tillägg fungerar innan du ändrar dem." + +#: fail2wp.php:1738 +msgid "The REST API URL of this site is" +msgstr "Webbplatsens REST API URL är" + +#: fail2wp.php:1743 +msgid "NOTE" +msgstr "OBS!" + +#: fail2wp.php:1744 +msgid "" +"If \"Require authentication\" is enabled, no REST API calls will be blocked " +"for logged in users and/or authenticated requests!" +msgstr "" +"Om \"Kräv autentisering\" är aktiverat så blockeras inga REST API-anrop för " +"inloggade och/eller autentiserade användare!" + +#: fail2wp.php:1777 +msgid "" +"Require that users be logged in and/or that all REST API calls are " +"authenticated, this is typically safe to do." +msgstr "" +"Kräv att användare är inloggad och/eller att alla REST API-anrop är " +"autentiserade. Detta är säkert att göra i normala fall." + +#: fail2wp.php:1784 +msgid "" +"Blocks all REST API index calls made to this site, this is typically safe to " +"do." +msgstr "" +"Blockera alla REST API-anrop till index, detta är säkert att göra i " +"normala fall." + +#: fail2wp.php:1791 +msgid "Log all blocked REST API calls for Fail2ban processing." +msgstr "Logga alla blockerade REST API-anrop för behandling av Fail2ban." + +#: fail2wp.php:1798 +msgid "" +"Blocks all REST API calls made to this site, this is typically not safe to " +"do." +msgstr "" +"Blockera alla REST API-anrop till denna webbplats. Detta är för det mesta " +"inte rekommenderat." + +#: fail2wp.php:1805 +msgid "WordPress did not return any available namespaces" +msgstr "WordPress returnerade inga tillgängliga namespaces" + +#: fail2wp.php:1830 fail2wp.php:1836 +msgid "IPs matching these addresses will be allowed to make any REST API call" +msgstr "" +"IP-adresser som matchar dessa får göra alla REST API-anrop" diff --git a/fail2wp/languages/fail2wp.pot b/fail2wp/languages/fail2wp.pot index 278cffc..afc365d 100644 --- a/fail2wp/languages/fail2wp.pot +++ b/fail2wp/languages/fail2wp.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: fail2wp 1.0.0\n" +"Project-Id-Version: fail2wp 1.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-02-03 10:24+0100\n" +"POT-Creation-Date: 2021-03-08 15:28+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Joaquim Homrighausen \n" "Language-Team: LANGUAGE \n" @@ -17,213 +17,524 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: fail2wp.php:184 -msgid "Unknown" +#: fail2wp.php:450 +msgid "Settings " msgstr "" -#: fail2wp.php:240 fail2wp.php:245 +#: fail2wp.php:548 +msgid "You are not currently logged in." +msgstr "" + +#: fail2wp.php:629 fail2wp.php:667 fail2wp.php:686 fail2wp.php:706 +msgid "No route was found matching the URL and request method." +msgstr "" + +#: fail2wp.php:743 fail2wp.php:754 fail2wp.php:765 +msgid "Invalid username, please try again." +msgstr "" + +#: fail2wp.php:747 fail2wp.php:793 +msgid "Invalid e-mail address, please try again." +msgstr "" + +#: fail2wp.php:811 +msgid "New user role default has been reset according to your" +msgstr "" + +#: fail2wp.php:815 +msgid "settings" +msgstr "" + +#: fail2wp.php:834 +msgid "Notification about new user role from" +msgstr "" + +#: fail2wp.php:836 +msgid "This is a notification from" +msgstr "" + +#: fail2wp.php:838 +msgid "" +"The role for newly registered users has been reset to your configured " +"setting." +msgstr "" + +#: fail2wp.php:840 +msgid "This notification was sent from the site" +msgstr "" + +#: fail2wp.php:842 +msgid "You may access the admin interface to the site here" +msgstr "" + +#: fail2wp.php:859 +msgid "User registration is enabled, but the new user role does not match" +msgstr "" + +#: fail2wp.php:868 fail2wp.php:898 fail2wp.php:927 +msgid "Go to" +msgstr "" + +#: fail2wp.php:870 fail2wp.php:900 fail2wp.php:929 +msgid "General settings" +msgstr "" + +#: fail2wp.php:872 fail2wp.php:902 fail2wp.php:931 +msgid "to check your membership and new user settings." +msgstr "" + +#: fail2wp.php:889 +msgid "User registration is enabled, and new users will be administrators" +msgstr "" + +#: fail2wp.php:918 +msgid "User registration is enabled, but no role has been configured" +msgstr "" + +#: fail2wp.php:1011 fail2wp.php:1016 msgid "Unknown role" msgstr "" -#: fail2wp.php:286 +#: fail2wp.php:1057 msgid "" -"Provides authentication related logging and security functions for " -"WordPress, suitable for use with Fail2ban" +"Provides authentication security functions for WordPress, plays nicely with " +"Fail2ban and Cloudflare" msgstr "" -#: fail2wp.php:289 +#: fail2wp.php:1060 msgid "Basic configuration" msgstr "" -#: fail2wp.php:292 -msgid "Logging" +#: fail2wp.php:1063 +msgid "New users" msgstr "" -#: fail2wp.php:295 +#: fail2wp.php:1066 +msgid "User logging" +msgstr "" + +#: fail2wp.php:1069 +msgid "REST API" +msgstr "" + +#: fail2wp.php:1072 msgid "Advanced" msgstr "" -#: fail2wp.php:298 +#: fail2wp.php:1075 msgid "Cloudflare" msgstr "" -#: fail2wp.php:301 +#: fail2wp.php:1078 +msgid "Import/Export" +msgstr "" + +#: fail2wp.php:1081 msgid "About" msgstr "" -#: fail2wp.php:307 +#: fail2wp.php:1086 msgid "" "One or more of openlog(), closelog(), and/or syslog() seem to be missing on " "this system" msgstr "" -#: fail2wp.php:355 +#: fail2wp.php:1108 +msgid "" +"Some data was filtered, please make sure you paste the content exactly as " +"copied" +msgstr "" + +#: fail2wp.php:1113 fail2wp.php:1121 +msgid "Please make sure you paste the content exactly as copied" +msgstr "" + +#: fail2wp.php:1126 +msgid "" +"Plugin version mismatch. You can only import exported data from the same " +"version as the one currently installed" +msgstr "" + +#: fail2wp.php:1190 +msgid "The following settings were ignored during the import" +msgstr "" + +#: fail2wp.php:1197 +msgid "Settings were successfully imported" +msgstr "" + +#: fail2wp.php:1201 +msgid "The setting \"Site label\" may need manual correction" +msgstr "" + +#: fail2wp.php:1208 +msgid "This tab can be used to import and export settings for the plugin." +msgstr "" + +#: fail2wp.php:1212 fail2wp.php:1221 +msgid "Import settings" +msgstr "" + +#: fail2wp.php:1217 +msgid "Paste settings in this field to import them" +msgstr "" + +#: fail2wp.php:1226 +msgid "Export settings" +msgstr "" + +#: fail2wp.php:1272 +msgid "Unable to create data for export" +msgstr "" + +#: fail2wp.php:1280 +msgid "Copy the text in this field to export your settings to another site" +msgstr "" + +#: fail2wp.php:1304 +msgid "" +"This is logged to the system's authentication log, which allows Fail2ban to " +"dynamically block offending IP addresses." +msgstr "" + +#: fail2wp.php:1306 +msgid "" +"Configuration of the Fail2ban system daemon, or similar, must be done " +"outside of WordPress for this to have any effect." +msgstr "" + +#: fail2wp.php:1343 msgid "Thank you for installing" msgstr "" -#: fail2wp.php:356 +#: fail2wp.php:1344 msgid "" -"This plugin provides security functions and integration between " -"WordPress and" +"This plugin provides security functions and integration between WordPress and" msgstr "" -#: fail2wp.php:360 +#: fail2wp.php:1348 msgid "Commercial support and customizations for this plugin is available from" msgstr "" -#: fail2wp.php:362 +#: fail2wp.php:1350 msgid "in Stockholm, Sweden. We speak Swedish and English" msgstr "" -#: fail2wp.php:364 +#: fail2wp.php:1352 msgid "" "The plugin is written by Joaquim Homrighausen and sponsored by WebbPlatsen i " "Sverige AB." msgstr "" -#: fail2wp.php:365 +#: fail2wp.php:1354 msgid "" "If you find this plugin useful, the author is happy to receive a donation, " "good review, or just a kind word." msgstr "" -#: fail2wp.php:366 +#: fail2wp.php:1355 msgid "" "If there is something you feel to be missing from this plugin, or if you " "have found a problem with the code or a feature, please do not hesitate to " "reach out to" msgstr "" -#: fail2wp.php:381 +#: fail2wp.php:1357 +msgid "There is more documentation available at" +msgstr "" + +#: fail2wp.php:1374 msgid "Site label" msgstr "" -#: fail2wp.php:382 +#: fail2wp.php:1375 msgid "Block user enum" msgstr "" -#: fail2wp.php:383 +#: fail2wp.php:1376 msgid "Block username login" msgstr "" -#: fail2wp.php:384 +#: fail2wp.php:1377 msgid "Secure login messages" msgstr "" -#: fail2wp.php:385 +#: fail2wp.php:1379 msgid "Other settings" msgstr "" -#: fail2wp.php:386 +#: fail2wp.php:1380 +msgid "Remove generator info" +msgstr "" + +#: fail2wp.php:1381 +msgid "Remove feeds" +msgstr "" + +#: fail2wp.php:1382 msgid "Remove settings" msgstr "" -#: fail2wp.php:389 +#: fail2wp.php:1385 +msgid "Membership warnings" +msgstr "" + +#: fail2wp.php:1386 +msgid "Check for role" +msgstr "" + +#: fail2wp.php:1387 +msgid "Force role" +msgstr "" + +#: fail2wp.php:1388 +msgid "Role to force" +msgstr "" + +#: fail2wp.php:1389 +msgid "Minimum username length" +msgstr "" + +#: fail2wp.php:1390 +msgid "Banned usernames" +msgstr "" + +#: fail2wp.php:1391 +msgid "E-mail must match" +msgstr "" + +#: fail2wp.php:1394 msgid "Successful login" msgstr "" -#: fail2wp.php:390 +#: fail2wp.php:1395 msgid "Unsuccessful login" msgstr "" -#: fail2wp.php:395 +#: fail2wp.php:1397 +msgid "Log user enum" +msgstr "" + +#: fail2wp.php:1400 +msgid "Require authentication" +msgstr "" + +#: fail2wp.php:1401 +msgid "Log blocked requests" +msgstr "" + +#: fail2wp.php:1402 +msgid "Block index requests" +msgstr "" + +#: fail2wp.php:1403 +msgid "Block all requests" +msgstr "" + +#: fail2wp.php:1404 +msgid "Block specific namespaces" +msgstr "" + +#: fail2wp.php:1405 +msgid "Block specific routes" +msgstr "" + +#: fail2wp.php:1406 +msgid "Bypass blocks for IPv4" +msgstr "" + +#: fail2wp.php:1407 +msgid "Bypass blocks for IPv6" +msgstr "" + +#: fail2wp.php:1410 msgid "Logging prefix" msgstr "" -#: fail2wp.php:396 +#: fail2wp.php:1411 msgid "Also log to PHP log" msgstr "" -#: fail2wp.php:399 +#: fail2wp.php:1414 msgid "Check for Cloudflare IP" msgstr "" -#: fail2wp.php:400 +#: fail2wp.php:1415 msgid "Cloudflare IPv4" msgstr "" -#: fail2wp.php:401 +#: fail2wp.php:1416 msgid "Cloudflare IPv6" msgstr "" -#: fail2wp.php:484 +#: fail2wp.php:1579 msgid "" "The site name to use for logging, defaults to your site name if left empty" msgstr "" -#: fail2wp.php:529 +#: fail2wp.php:1605 msgid "Unknown users" msgstr "" -#: fail2wp.php:537 +#: fail2wp.php:1612 msgid "User enumeration attempts (i.e. your.site/...?author=nnn)" msgstr "" -#: fail2wp.php:545 +#: fail2wp.php:1620 +msgid "Warn about odd membership/registration settings" +msgstr "" + +#: fail2wp.php:1638 +msgid "Check WordPress setting against the value configured here" +msgstr "" + +#: fail2wp.php:1642 fail2wp.php:1671 +msgid "No available roles (?)" +msgstr "" + +#: fail2wp.php:1650 +msgid "Force new user registration settings to a role" +msgstr "" + +#: fail2wp.php:1668 +msgid "Force WordPress setting to the value configured here" +msgstr "" + +#: fail2wp.php:1677 +msgid "Minimum length of usernames, 2-200 characters, 0 ignores the setting" +msgstr "" + +#: fail2wp.php:1683 +msgid "These usernames will be blocked for new user registrations" +msgstr "" + +#: fail2wp.php:1689 +msgid "E-mail address must match at least one of these for new users" +msgstr "" + +#: fail2wp.php:1695 +msgid "Remove \"Generator\" output in HTML, RSS, etc." +msgstr "" + +#: fail2wp.php:1702 +msgid "Remove RSS and Atom feeds" +msgstr "" + +#: fail2wp.php:1710 msgid "Remove all plugin settings and data when plugin is uninstalled" msgstr "" -#: fail2wp.php:553 +#: fail2wp.php:1717 msgid "Block user enumeration attempts (i.e. your.site/...?author=nnn)" msgstr "" -#: fail2wp.php:561 +#: fail2wp.php:1724 msgid "Require users to login with their e-mail address" msgstr "" -#: fail2wp.php:569 +#: fail2wp.php:1731 msgid "Change login failure messages to contain less detail" msgstr "" -#: fail2wp.php:577 +#: fail2wp.php:1739 +msgid "" +"Please make sure you understand how these settings can impact the operation " +"of WordPress and other plugins before making changes to them." +msgstr "" + +#: fail2wp.php:1743 +msgid "The REST API URL of this site is" +msgstr "" + +#: fail2wp.php:1748 +msgid "NOTE" +msgstr "" + +#: fail2wp.php:1749 +msgid "" +"If \"Require authentication\" is enabled, no REST API calls will be blocked " +"for logged in users and/or authenticated requests!" +msgstr "" + +#: fail2wp.php:1758 msgid "" "Please make sure you understand how these settings can impact the operation " "of the plugin before making changes to them." msgstr "" -#: fail2wp.php:585 +#: fail2wp.php:1766 msgid "These settings allows the plugin to better interact with Cloudflare." msgstr "" -#: fail2wp.php:587 +#: fail2wp.php:1768 msgid "" "If your site is not published via Cloudflare, you can safely ignore these " "settings." msgstr "" -#: fail2wp.php:589 +#: fail2wp.php:1770 msgid "For an updated list of Cloudflare IPs, please use this link" msgstr "" -#: fail2wp.php:599 +#: fail2wp.php:1782 +msgid "" +"Require that users be logged in and/or that all REST API calls are " +"authenticated, this is typically safe to do." +msgstr "" + +#: fail2wp.php:1789 +msgid "" +"Blocks all REST API index calls made to this site, this is typically safe to " +"do." +msgstr "" + +#: fail2wp.php:1796 +msgid "Log all blocked REST API calls for Fail2ban processing." +msgstr "" + +#: fail2wp.php:1803 +msgid "" +"Blocks all REST API calls made to this site, this is typically not safe to " +"do." +msgstr "" + +#: fail2wp.php:1810 +msgid "WordPress did not return any available namespaces" +msgstr "" + +#: fail2wp.php:1835 fail2wp.php:1841 +msgid "IPs matching these addresses will be allowed to make any REST API call" +msgstr "" + +#: fail2wp.php:1845 msgid "The logging prefix, this should normally be left empty" msgstr "" -#: fail2wp.php:606 +#: fail2wp.php:1851 msgid "Log the same information to PHP log using error_log()" msgstr "" -#: fail2wp.php:614 +#: fail2wp.php:1859 msgid "Attempt to unmask real IP when Cloudflare IP is detected" msgstr "" -#: fail2wp.php:626 fail2wp.php:637 +#: fail2wp.php:1866 fail2wp.php:1872 msgid "" "IPs matching these addresses will be considerd to be coming from Cloudflare" msgstr "" -#: fail2wp.php:962 fail2wp.php:966 +#: fail2wp.php:2244 fail2wp.php:2248 msgid "Please specify your e-mail address to login" msgstr "" -#: fail2wp.php:989 +#: fail2wp.php:2266 msgid "E-mail address" msgstr "" -#: fail2wp.php:1094 fail2wp.php:1122 +#: fail2wp.php:2371 fail2wp.php:2404 msgid "Invalid login credentials, please try again." msgstr "" -#: fail2wp.php:1126 +#: fail2wp.php:2408 msgid "Lost password" msgstr "" diff --git a/fail2wp/uninstall.php b/fail2wp/uninstall.php index f7dc15e..1db7746 100644 --- a/fail2wp/uninstall.php +++ b/fail2wp/uninstall.php @@ -29,41 +29,53 @@ * Boston, MA 02110-1301, USA. */ +// define( 'FAIL2WP_UNINSTALL_TRACE', true ); + // Don't load directly defined( 'ABSPATH' ) || die( '-1' ); // If uninstall not called from WordPress, then exit if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) { + if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) { + error_log( 'fail2wp-uninstall: init' ); + } exit; } // If action is not to uninstall, then exit if ( empty( $_REQUEST['action'] ) || $_REQUEST['action'] !== 'delete-plugin' ) { + if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) { + error_log( 'fail2wp-uninstall: REQUEST["action"] is not delete-plugin' ); + } exit; } // If it's not us, then exit if ( empty( $_REQUEST['slug'] ) || $_REQUEST['slug'] !== 'fail2wp' ) { + if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) { + error_log( 'fail2wp-uninstall: REQUEST["slug"] is not fail2wp' ); + } exit; } // If we shouldn't do this, then exit if ( ! current_user_can( 'manage_options' ) || ! current_user_can( 'delete_plugins' ) ) { + if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) { + error_log( 'fail2wp-uninstall: User is not allowed to manage/uninstall plugins' ); + } exit; } // Figure out if an uninstall should remove plugin settings -$remove_settings = get_option( 'fail2wp-remove-settings', '0' ); +$remove_settings = get_option( 'fail2wp-settings-remove', '0' ); if ( $remove_settings == '1' ) { - // Remove Fail2WP settings - delete_option( 'fail2wp-site-label' ); - delete_option( 'fail2wp-roles-notify' ); - delete_option( 'fail2wp-roles-warn' ); - delete_option( 'fail2wp-unknown-warn' ); - delete_option( 'fail2wp-settings-remove' ); - delete_option( 'fail2wp-also-log-php' ); - delete_option( 'fail2wp-block-user-enum' ); - delete_option( 'fail2wp-log-user-enum' ); - delete_option( 'fail2wp-block-username-login' ); - delete_option( 'fail2wp-secure-login-message' ); - delete_option( 'fail2wp-cloudflare-check' ); - delete_option( 'fail2wp-cloudflare-ipv4' ); - delete_option( 'fail2wp-cloudflare-ipv6' ); + if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) { + error_log( 'fail2wp-uninstall: uninstalling' ); + } + define( 'FAIL2WP_WORDPRESS_PLUGIN', true ); + + require_once dirname(__FILE__) . '/includes/fail2wp_misc.inc.php'; + + fail2wp_misc_delete_all_settings(); +} else { + if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) { + error_log( 'fail2wp-uninstall: $remove_settings = ' . var_export( $remove_settings, true ) ); + } }