HEX
Server: Apache/2.4.58 (Ubuntu)
System: Linux wordpress 6.8.0-88-generic #89-Ubuntu SMP PREEMPT_DYNAMIC Sat Oct 11 01:02:46 UTC 2025 x86_64
User: www-data (33)
PHP: 8.3.28
Disabled: NONE
Upload Files
File: /var/www/html/wp-content/plugins/wp-fail2ban/admin/admin.php
<?php declare(strict_types=1);
/**
 * Admin
 *
 * @package wp-fail2ban
 * @since   4.4.0   Require PHP 7.4
 * @since   4.0.0
 */
namespace org\lecklider\charles\wordpress\wp_fail2ban;

use WP_fail2ban\Lib\Badges\BadgeManager;

defined( 'ABSPATH' ) or exit;

require_once __DIR__ . '/config.php';
require_once __DIR__ . '/widgets.php';
require_once __DIR__ . '/lib/about.php';

/**
 * Helper: Add a new submenu "under" the Freemius "Add-Ons" submenu
 *
 * @since  4.4.0        Add type hints
 * @since  4.3.2.1      Backport from 4.3.4.0
 *
 * @param  string      $page_title The text to be displayed in the title tags of the page when the menu
 *                                 is selected.
 * @param  string|null $capability The capability required for this menu to be displayed to the user.
 * @param  string      $menu_slug  The slug name to refer to this menu by. Should be unique for this menu
 *                                 and only include lowercase alphanumeric, dashes, and underscores characters
 *                                 to be compatible with sanitize_key().
 * @param  callable    $function   The function to be called to output the content for this page.
 * @return false|string     The resulting page's hook_suffix, or false if the user does not have the capability required.
 */
function add_wpf2b_addon_page( string $page_title, ?string $capability, string $menu_slug, $function ) {
	global $submenu;

	$menu_title = " - $page_title";

	if ( ! $capability ) {
		$capability = ( is_multisite() )
			? 'manage_network_options'
			: 'manage_options';
	}

	$parent   = ( wf_fs()->is_activation_mode() ) ? null : 'wp-fail2ban-menu';
	$sub_menu = 'wp-fail2ban-menu-addons';

	if ( $hook = add_submenu_page( $parent, $page_title, $menu_title, $capability, $menu_slug, $function ) ) {
		foreach ( $submenu as &$menu ) {
			if ( isset( $menu[0] ) && $menu[0][2] == 'wp-fail2ban-menu' ) {
				$new_submenu = array();
				$menu_item   = array_pop( $menu );

				// Find the submenu we're appending to
				for ( $i = 0; $i < count( $menu ) && $sub_menu != $menu[ $i ][2]; $i++ ) {
					$new_submenu[] = $menu[ $i ];
				}

				if ( $i < count( $menu ) ) {
					$new_submenu[] = $menu[ $i++ ];
				}

				// Find the menu item alphabetically before the new item
				for ( ; $i < count( $menu ) && isset( $menu[ $i ][0] ) && 0 === strpos( $menu[ $i ][0], ' - ' ) && 0 > strcmp( $menu[ $i ][0], $menu_title ); $i++ ) {
					$new_submenu[] = $menu[ $i ];
				}

				$new_submenu[] = $menu_item;

				for ( ; $i < count( $menu ); $i++ ) {
					$new_submenu[] = $menu[ $i ];
				}

				$menu = $new_submenu;

				break;
			}
		}
	}

	return $hook;
}

/**
 * Output readme.txt to standard About format
 *
 * @since  4.4.0    Add type hints, return type
 * @since  4.3.2.2
 *
 * @param  string $ver    Version stem, e.g. 4.3.2
 * @param  string $file   Full path to readme.txt
 *
 * @return void
 */
function readme( string $ver, string $file ): void {
	if ( file_exists( $file ) ) {
		$rm          = file( $file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
		$inChangelog = $inList = $inSubList = false;
		foreach ( $rm as $line ) {
			if ( '== Changelog ==' == $line ) {
				$inChangelog = true;
				echo "<dl>\n";
				continue;
			}
			if ( $inChangelog ) {
				if ( preg_match( '/^= (.*) =$/', $line, $matches ) &&
					( 'Develop' == $matches[1] || substr( $matches[1], 0, strlen( $ver ) ) == $ver ) ) {
					if ( $inList ) {
						echo "</ul></dd>\n";
					}
					echo "<dt>{$matches[1]}</dt>\n";
					echo "<dd><ul>\n";
					$inList = true;

				} elseif ( preg_match( '/^(\s*)\* (.*)$/', $line, $matches ) ) {
					$li = $matches[2];
					$li = preg_replace( '/\[(.*?)\]\((.*?)\)(\.)?/', '<a href="$2" target="_blank" rel="noopener">$1 <span class="dashicons dashicons-external"></span></a>', $li );
					$li = preg_replace( '/`(.*?)`/', '<tt>$1</tt>', $li );
					$li = preg_replace( '/\*\*(.*?)\*\*/', '<b>$1</b>', $li );
					$li = preg_replace( '/\*(.*?)\*/', '<i>$1</i>', $li );
					$li = preg_replace( '|\((h/t .*?)\)|', '<span class="ht">$1</span>', $li );
					$li = preg_replace( '/^(Fix|Deprecate )/', '<span style="color:red">$1</span>', $li );
					$li = preg_replace( '/^(Update )/', '<span style="color:purple">$1</span>', $li );
					$li = preg_replace( '/^(Site Health|WAF: )/', '<span style="color:blue">$1</span>', $li );
					$li = preg_replace( '/\[((Canonical|Premium) only)\]/', '<span style="color: grey; font-style: italic; font-weight: bold">$1</span>', $li );
					if ( ! $inSubList && strlen( $matches[1] ) ) {
						echo '<ul>';
						$inSubList = true;
					} elseif ( $inSubList && ! strlen( $matches[1] ) ) {
						echo '</ul>';
						$inSubList = false;
					}
					echo "<li>{$li}</li>\n";

				} else {
					if ( $inList ) {
						echo "</ul></dd>\n";
						$inList = false;
					}
					echo "</dl>\n";
					$inChangelog = false;

					break;
				}
			}
		}
	} else {
		echo "<p>Error: Cannot open <tt>readme.txt</tt></p>\n";
	}
}

/**
 * Helper: Security and Settings menu
 *
 * @since  5.4.0    Drop security page
 * @since  4.4.0    Add return type
 * @since  4.3.0
 *
 * @param  string $capability Capability
 *
 * @return void
 */
function _security_settings( string $capability = 'manage_options' ): void {
	_settings( apply_filters( __METHOD__ . '.page', 'logging' ), $capability );
}

/**
 * Helper: Settings menu
 *
 * @since  4.4.0    Add return type
 * @since  4.3.0
 *
 * @param  $page        string|null
 * @param  $capability  string
 *
 * @return void
 */
function _settings( string $page = null, string $capability = 'manage_options' ): void {
	if ( $hook = add_submenu_page(
		'wp-fail2ban-menu',
		__( 'Settings', 'wp-fail2ban' ),
		__( 'Settings', 'wp-fail2ban' ),
		$capability,
		'wpf2b-settings',
		__NAMESPACE__ . '\settings'
	) ) {
		add_action(
			"load-$hook",
			function () use ( $page ) {
				wp_enqueue_style( 'wpf2b-admin', plugins_url( 'css/admin.css', __FILE__ ) );
				apply_filters( 'wp_fail2ban_init_tabs', false );
				TabBase::setDefaultTab( $page );
				TabBase::getActiveTab()->current_screen();
			}
		);
	}
}

/**
 * Register admin menus
 *
 * @since  4.4.0    Add return type
 * @since  4.0.0
 *
 * @return void
 */
function admin_menu(): void {
	if ( ! is_multisite() ) {
		if ( ( defined( 'WP_FAIL2BAN_FREE_ONLY' ) && false !== WP_FAIL2BAN_FREE_ONLY ) || ! wf_fs()->can_use_premium_code() ) {
			$hook = add_menu_page(
				'WP fail2ban',
				'WP fail2ban',
				'manage_options',
				'wp-fail2ban-menu',
				__NAMESPACE__ . '\welcome',
				plugin_dir_url( WP_FAIL2BAN_FILE ) . 'assets/menu.svg'
			);
			add_action(
				"load-$hook",
				function () {
					wp_enqueue_style( 'wpf2b-admin', plugins_url( 'css/admin.css', __FILE__ ) );
				}
			);
			add_action( 'admin_menu', __NAMESPACE__ . '\admin_menu_fix', PHP_INT_MAX );
			do_action( 'wp_fail2ban_menu' );

			_security_settings();
		}
	}
}
add_action( 'admin_menu', __NAMESPACE__ . '\admin_menu' );

/**
 * Register network admin menus
 *
 * @since  4.4.0    Add return type
 * @since  4.3.0
 *
 * @return void
 */
function network_admin_menu(): void {
	if ( ( defined( 'WP_FAIL2BAN_FREE_ONLY' ) && false !== WP_FAIL2BAN_FREE_ONLY ) || ! wf_fs()->can_use_premium_code() ) {
		_network_admin_menu();
	}
}
add_action( 'network_admin_menu', __NAMESPACE__ . '\network_admin_menu' );

/**
 * Actual network admin menu handler
 *
 * @since  4.4.0    Add return type
 * @since  4.3.0
 *
 * @return void
 */
function _network_admin_menu(): void {
	$hook = add_menu_page(
		'WP fail2ban',
		'WP fail2ban',
		'manage_options',
		'wp-fail2ban-menu',
		__NAMESPACE__ . '\welcome',
		plugin_dir_url( WP_FAIL2BAN_FILE ) . 'assets/menu.svg'
	);
	add_action(
		"load-$hook",
		function () {
			wp_enqueue_style( 'wpf2b-admin', plugins_url( 'css/admin.css', __FILE__ ) );
		}
	);
	add_action( 'network_admin_menu', __NAMESPACE__ . '\admin_menu_fix', PHP_INT_MAX );
	do_action( 'wp_fail2ban_menu' );

	_security_settings();
}

/**
 * Fix first submenu name.
 *
 * @since  5.4.0    Handle incomplete submenu
 * @since  4.4.0    Add return type
 * @since  4.3.0
 *
 * @return void
 */
function admin_menu_fix(): void {
	global $submenu;

	if ( 'WP fail2ban' == ( $submenu['wp-fail2ban-menu'][0][0] ?? '' ) ) {
		$submenu['wp-fail2ban-menu'][0][0] = __( 'Welcome', 'wp-fail2ban' );
	}

	if ( defined( 'WP_FAIL2BAN_FREE_ONLY' ) && WP_FAIL2BAN_FREE_ONLY ) {
		remove_submenu_page( 'wp-fail2ban-menu', 'wp-fail2ban-menu-pricing' );
	}
}

/**
 * Add Settings link on Plugins page
 *
 * @since  4.4.0.8  Activate "normal" plugin if running as MU
 * @since  4.4.0.3  Allow $plugin_data to be null despite spec
 * @since  4.4.0    Add type hints, return type
 * @since  4.2.0
 *
 * @param  string[]   $actions        An array of plugin action links. By default this can include 'activate',
 *                                    'deactivate', and 'delete'.
 * @param  string     $plugin_file    Path to the plugin file relative to the plugins directory.
 * @param  array|null $plugin_data    An array of plugin data. See `get_plugin_data()`.
 * @param  string     $context        The plugin context. By default this can include 'all', 'active', 'inactive',
 *                                    'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
 *
 * @return string[]
 *
 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
 */
function plugin_action_links( array $actions, string $plugin_file, ?array $plugin_data, string $context ): array {
	if ( preg_match( "|$plugin_file\$|", WP_FAIL2BAN_FILE ) &&
		( ! is_multisite() || is_network_admin() ) ) {
		foreach ( get_mu_plugins() as $plugin => $data ) {
			if ( 0 === strpos( $data['Name'], 'WP fail2ban' ) ) {
				// MU plugin
				//
				// * Make sure the "normal" plugin is activated
				// * Remove "Deactivate" and "Delete" links
				$plugin = plugin_basename( WP_FAIL2BAN_FILE );

				if ( array_key_exists( $plugin, get_plugins() ) && ! is_plugin_active( $plugin ) ) {
					activate_plugin(
						$plugin,
						'',     // don't redirect anywhere
						false,
						true    // don't call activation hooks
					);
				}

				unset( $actions['deactivate'] );
				unset( $actions['delete'] );
				break;
			}
		}

		if ( ! wf_fs()->is_activation_mode() &&
			( ! wf_fs()->can_use_premium_code() ||
			( ( is_multisite() && wf_fs()->is_plan_or_trial( 'silver' ) ) ||
			( ! is_multisite() && wf_fs()->is_plan_or_trial( 'bronze' ) ) ) ) ) {
			$settings = sprintf(
				'<a href="%s?page=wpf2b-settings&tab=about" title="%s">%s</a>',
				network_admin_url( 'admin.php' ),
				__( 'Settings', 'wp-fail2ban' ),
				( function_exists( '\add_security_page' ) )
					? '<span class="dashicon dashicons-admin-generic"></span>'
					: __( 'Settings', 'wp-fail2ban' )
			);
			// Add Settings at the start
			$actions = array_merge(
				array(
					'settings' => $settings,
				),
				$actions
			);
		}
	}

	return $actions;
}
add_filter( 'plugin_action_links', __NAMESPACE__ . '\plugin_action_links', PHP_INT_MAX, 4 );
add_filter( 'network_admin_plugin_action_links', __NAMESPACE__ . '\plugin_action_links', 99999, 4 );

/**
 * Add help tab to Dashboard
 *
 * @since  4.4.0    Add return type
 * @since  4.3.0
 *
 * @return void
 */
function admin_head_dashboard(): void {
	$content = '';

	if ( ( ! is_multisite() && current_user_can( 'manage_options' ) ) ||
		( is_network_admin() && current_user_can( 'manage_network_options' ) ) ) {
		$message = __( 'Shows the last 5 messages sent to <code>syslog</code> - provides simple status at a glance and can be helpful for debugging.', 'wp-fail2ban' );
		if ( ! wf_fs()->can_use_premium_code() ) {
			$message .= sprintf(
				/* translators: %s: <a href> internals */
				__( 'The <a %s>Premium version</a> provides more advanced views.', 'wp-fail2ban' ),
				sprintf( 'href="%s"', network_admin_url( 'admin.php?page=wp-fail2ban-menu-pricing' ) )
			);
		}

		$content = sprintf(
			'<p><strong>%s</strong> &mdash; %s.</p>',
			__( 'Last 5 Messages', 'wp-fail2ban' ),
			$message
		);
	}
	if ( ! empty( $content = apply_filters( 'wp_fail2ban_dashboard_help', $content ) ) ) {
		get_current_screen()->add_help_tab(
			array(
				'id'      => 'wp-fail2ban',
				'title'   => 'WP fail2ban',
				'content' => $content,
			)
		);
	}
}
add_action( 'admin_head-index.php', __NAMESPACE__ . '\admin_head_dashboard', 9999 );

/**
 * Add badges to plugin listing
 *
 * @since  5.4.0
 *
 * @return void
 */
function admin_init(): void {
	require_once WP_FAIL2BAN_DIR . '/vendor/wp-fail2ban/lib-badges/src/BadgeManager.php';

	BadgeManager::init(
		array(
			'plugin_file' => WP_FAIL2BAN_FILE,
			'free'        => true, // @wpf2b-free-only
			'lts'         => true,
			'show'        => array(
				'non-canonical' => false,
			),
		)
	);
}
add_action( 'admin_init', __NAMESPACE__ . '\admin_init' );