diff --git a/assets/admin.css b/assets/admin.css
new file mode 100644
index 0000000..67f9131
--- /dev/null
+++ b/assets/admin.css
@@ -0,0 +1,537 @@
+/* WPBan Pro Admin Styles - WordPress Native Experience */
+
+.wpban-wrap {
+ margin-top: 20px;
+ max-width: 1400px;
+}
+
+/* Dashboard Grid */
+.wpban-dashboard {
+ margin-top: 20px;
+}
+
+.wpban-stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
+ gap: 20px;
+ margin-bottom: 30px;
+}
+
+.wpban-stat-card {
+ background: #fff;
+ border: 1px solid #c3c4c7;
+ padding: 20px;
+ text-align: center;
+ box-shadow: 0 1px 1px rgba(0,0,0,0.04);
+ position: relative;
+ transition: all 0.3s ease;
+}
+
+.wpban-stat-card:hover {
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ transform: translateY(-2px);
+}
+
+.wpban-stat-card h3 {
+ margin: 0 0 10px 0;
+ font-size: 14px;
+ font-weight: 600;
+ color: #1d2327;
+}
+
+.wpban-stat-number {
+ font-size: 36px;
+ font-weight: 300;
+ color: #2271b1;
+ line-height: 1;
+}
+
+.wpban-stat-trend {
+ margin-top: 10px;
+ font-size: 12px;
+}
+
+.wpban-stat-trend .up {
+ color: #d63638;
+}
+
+.wpban-stat-trend .down {
+ color: #00a32a;
+}
+
+/* Grid Layout */
+.wpban-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 20px;
+ margin-bottom: 20px;
+}
+
+@media (max-width: 1200px) {
+ .wpban-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
+/* Cards */
+.wpban-card {
+ background: #fff;
+ border: 1px solid #c3c4c7;
+ padding: 20px;
+ margin-bottom: 20px;
+ box-shadow: 0 1px 1px rgba(0,0,0,0.04);
+}
+
+.wpban-card h2 {
+ margin: 0 0 15px 0;
+ font-size: 1.3em;
+ font-weight: 600;
+ color: #1d2327;
+}
+
+.wpban-card h3 {
+ margin: 20px 0 10px 0;
+ font-size: 1.1em;
+ font-weight: 600;
+}
+
+/* Actions */
+.wpban-actions {
+ display: flex;
+ gap: 10px;
+ margin: 20px 0;
+ flex-wrap: wrap;
+}
+
+/* Bypass URL */
+.wpban-bypass-url {
+ margin: 20px 0;
+}
+
+.wpban-bypass-url label {
+ display: block;
+ margin-bottom: 10px;
+ font-weight: 600;
+}
+
+.wpban-input-group {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 10px;
+}
+
+.wpban-input-group input {
+ flex: 1;
+}
+
+/* Templates Grid */
+.wpban-templates-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: 20px;
+}
+
+.wpban-template-card {
+ background: #f6f7f7;
+ border: 1px solid #dcdcde;
+ padding: 20px;
+ border-radius: 4px;
+ transition: all 0.2s;
+}
+
+.wpban-template-card:hover {
+ background: #fff;
+ border-color: #2271b1;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+
+.wpban-template-card h3 {
+ margin: 0 0 10px 0;
+ font-size: 16px;
+ font-weight: 600;
+}
+
+.wpban-template-card p {
+ margin: 0 0 15px 0;
+ color: #50575e;
+ font-size: 13px;
+ line-height: 1.5;
+}
+
+/* Tabs */
+.wpban-tabs {
+ margin-top: 20px;
+}
+
+.tab-content {
+ display: none;
+ background: #fff;
+ border: 1px solid #c3c4c7;
+ border-top: none;
+ padding: 20px;
+}
+
+.tab-content.active {
+ display: block;
+}
+
+.tab-content h2 {
+ margin-top: 0;
+}
+
+/* Crawler Controls */
+.wpban-crawler-controls {
+ margin-bottom: 20px;
+ display: flex;
+ gap: 10px;
+ flex-wrap: wrap;
+}
+
+/* Crawler Grid */
+.wpban-crawler-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
+ gap: 10px;
+ margin-bottom: 30px;
+}
+
+.wpban-crawler-item {
+ display: block;
+ padding: 12px;
+ background: #f6f7f7;
+ border: 1px solid #dcdcde;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.wpban-crawler-item:hover {
+ background: #fff;
+ border-color: #2271b1;
+}
+
+.wpban-crawler-item input[type="checkbox"] {
+ margin-right: 8px;
+}
+
+.wpban-crawler-name {
+ font-weight: 600;
+ display: inline-block;
+ margin-right: 8px;
+ font-family: Consolas, Monaco, monospace;
+ font-size: 13px;
+}
+
+.wpban-crawler-desc {
+ font-size: 12px;
+ color: #50575e;
+ display: inline;
+}
+
+/* Notices */
+.wpban-notice {
+ padding: 12px;
+ margin: 15px 0;
+ border-left: 4px solid;
+ background: #fff;
+}
+
+.wpban-notice-warning {
+ border-left-color: #f0b849;
+ background-color: #fef8ee;
+}
+
+.wpban-notice-warning p {
+ margin: 0;
+}
+
+/* Logs */
+.wpban-logs-filters {
+ background: #fff;
+ border: 1px solid #c3c4c7;
+ padding: 15px;
+ margin-bottom: 20px;
+}
+
+.wpban-logs-filters form {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ flex-wrap: wrap;
+}
+
+.wpban-logs-filters input[type="date"],
+.wpban-logs-filters input[type="text"],
+.wpban-logs-filters select {
+ height: 30px;
+ line-height: 28px;
+}
+
+/* Badges */
+.wpban-badge {
+ display: inline-block;
+ padding: 3px 8px;
+ border-radius: 3px;
+ font-size: 11px;
+ font-weight: 600;
+ text-transform: uppercase;
+}
+
+.wpban-badge-banned {
+ background: #d63638;
+ color: #fff;
+}
+
+.wpban-badge-blocked {
+ background: #f0b849;
+ color: #000;
+}
+
+.wpban-badge-failed_login {
+ background: #dba617;
+ color: #fff;
+}
+
+.wpban-badge-bypass {
+ background: #00a32a;
+ color: #fff;
+}
+
+/* Truncate */
+.wpban-truncate {
+ display: inline-block;
+ max-width: 200px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ vertical-align: middle;
+}
+
+/* Country Selector */
+.wpban-country-selector select {
+ font-family: Consolas, Monaco, monospace;
+ font-size: 13px;
+}
+
+/* Rate Limit Settings */
+.form-table input[type="number"] {
+ width: 80px;
+}
+
+/* Tools Grid */
+.wpban-tools-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
+ gap: 20px;
+}
+
+/* Chart Container */
+#wpban-country-chart {
+ max-height: 300px;
+}
+
+/* Activity List */
+.wpban-activity-list {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.wpban-activity-list li {
+ padding: 8px 0;
+ border-bottom: 1px solid #f0f0f1;
+}
+
+.wpban-activity-list li:last-child {
+ border-bottom: none;
+}
+
+/* Form Elements */
+.form-table textarea.code {
+ font-family: Consolas, Monaco, monospace;
+ font-size: 13px;
+ line-height: 1.5;
+}
+
+/* Pagination */
+.tablenav-pages {
+ margin: 20px 0;
+ text-align: right;
+}
+
+.tablenav-pages .pagination-links {
+ display: inline-block;
+}
+
+.tablenav-pages a,
+.tablenav-pages span {
+ display: inline-block;
+ padding: 3px 8px;
+ margin: 0 2px;
+ background: #f6f7f7;
+ border: 1px solid #dcdcde;
+ text-decoration: none;
+}
+
+.tablenav-pages .current {
+ background: #2271b1;
+ color: #fff;
+ border-color: #2271b1;
+}
+
+/* Responsive Tables */
+@media (max-width: 782px) {
+ .wp-list-table {
+ display: block;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+
+ .wpban-stats-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .wpban-templates-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .wpban-crawler-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .wpban-tools-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .wpban-logs-filters form {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .wpban-logs-filters input,
+ .wpban-logs-filters select,
+ .wpban-logs-filters button {
+ width: 100%;
+ margin-bottom: 10px;
+ }
+}
+
+/* Loading States */
+.wpban-loading {
+ opacity: 0.6;
+ pointer-events: none;
+ position: relative;
+}
+
+.wpban-loading::after {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 20px;
+ height: 20px;
+ margin: -10px 0 0 -10px;
+ border: 2px solid #f3f3f3;
+ border-top: 2px solid #2271b1;
+ border-radius: 50%;
+ animation: wpban-spin 1s linear infinite;
+}
+
+@keyframes wpban-spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+/* Buttons */
+.button.button-small {
+ padding: 0 8px;
+ line-height: 26px;
+ height: 28px;
+ font-size: 12px;
+}
+
+/* WP Editor in Settings */
+.tab-content .wp-editor-wrap {
+ margin-top: 10px;
+}
+
+/* Empty States */
+.wpban-empty-state {
+ text-align: center;
+ padding: 40px;
+ color: #646970;
+}
+
+.wpban-empty-state p {
+ font-size: 16px;
+ margin: 0;
+}
+
+/* Success/Error Messages */
+.wpban-message {
+ padding: 12px;
+ margin: 15px 0;
+ border-left: 4px solid;
+ background: #fff;
+ animation: wpban-fade-in 0.3s ease;
+}
+
+.wpban-message.success {
+ border-left-color: #00a32a;
+ background-color: #f0f8f0;
+}
+
+.wpban-message.error {
+ border-left-color: #d63638;
+ background-color: #fef1f1;
+}
+
+@keyframes wpban-fade-in {
+ from {
+ opacity: 0;
+ transform: translateY(-10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+/* Row Actions */
+.row-actions {
+ font-size: 12px;
+ padding-top: 4px;
+}
+
+.row-actions a {
+ text-decoration: none;
+}
+
+/* Status Indicators */
+.wpban-status {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ margin-right: 5px;
+}
+
+.wpban-status.active {
+ background: #00a32a;
+}
+
+.wpban-status.inactive {
+ background: #787c82;
+}
+
+/* Code Preview */
+.wpban-code-preview {
+ background: #f6f7f7;
+ border: 1px solid #dcdcde;
+ padding: 15px;
+ font-family: Consolas, Monaco, monospace;
+ font-size: 13px;
+ overflow-x: auto;
+ white-space: pre;
+ max-height: 300px;
+ overflow-y: auto;
+}
\ No newline at end of file
diff --git a/assets/admin.js b/assets/admin.js
new file mode 100644
index 0000000..4c791e8
--- /dev/null
+++ b/assets/admin.js
@@ -0,0 +1,335 @@
+/**
+ * WPBan Pro Admin JavaScript
+ */
+
+(function($) {
+ 'use strict';
+
+ // Tab Navigation
+ $(document).on('click', '.nav-tab', function(e) {
+ e.preventDefault();
+ const target = $(this).attr('href');
+
+ $('.nav-tab').removeClass('nav-tab-active');
+ $(this).addClass('nav-tab-active');
+
+ $('.tab-content').removeClass('active');
+ $(target).addClass('active');
+
+ // Save active tab to localStorage
+ localStorage.setItem('wpban_active_tab', target);
+ });
+
+ // Restore active tab
+ $(document).ready(function() {
+ const activeTab = localStorage.getItem('wpban_active_tab');
+ if (activeTab && $(activeTab).length) {
+ $('.nav-tab[href="' + activeTab + '"]').trigger('click');
+ }
+ });
+
+ // Settings Form Handler
+ $('#wpban-settings-form').on('submit', function(e) {
+ e.preventDefault();
+
+ const $form = $(this);
+ const $button = $('#wpban-save-settings');
+ const $spinner = $button.next('.spinner');
+
+ $button.prop('disabled', true);
+ $spinner.addClass('is-active');
+
+ $.ajax({
+ url: wpban.ajax_url,
+ type: 'POST',
+ data: {
+ action: 'wpban_save_settings',
+ settings: $form.serialize(),
+ _ajax_nonce: $('#wpban_nonce').val()
+ },
+ success: function(response) {
+ if (response.success) {
+ showMessage('success', response.data.message);
+ } else {
+ showMessage('error', response.data || wpban.i18n.error);
+ }
+ },
+ error: function() {
+ showMessage('error', wpban.i18n.error);
+ },
+ complete: function() {
+ $button.prop('disabled', false);
+ $spinner.removeClass('is-active');
+ }
+ });
+ });
+
+ // Apply Template
+ window.wpbanApplyTemplate = function(templateId) {
+ if (!confirm(wpban.i18n.confirm_template)) {
+ return;
+ }
+
+ $.post(wpban.ajax_url, {
+ action: 'wpban_apply_template',
+ template: templateId,
+ _ajax_nonce: wpban.nonce
+ }, function(response) {
+ if (response.success) {
+ showMessage('success', response.data.message);
+ setTimeout(() => location.reload(), 1500);
+ } else {
+ showMessage('error', response.data || wpban.i18n.error);
+ }
+ });
+ };
+
+ // Copy Text to Clipboard
+ window.wpbanCopyText = function(text) {
+ navigator.clipboard.writeText(text).then(() => {
+ showMessage('success', 'Copied to clipboard!');
+ }).catch(() => {
+ // Fallback for older browsers
+ const $temp = $('');
+ $('body').append($temp);
+ $temp.val(text).select();
+ document.execCommand('copy');
+ $temp.remove();
+ showMessage('success', 'Copied to clipboard!');
+ });
+ };
+
+ // Crawler Selection
+ window.wpbanSelectCrawlers = function(type, select) {
+ if (type === 'all') {
+ $('.wpban-crawler-item input').prop('checked', select);
+ } else {
+ $(`.wpban-crawler-item[data-type="${type}"] input`).prop('checked', select);
+ }
+ };
+
+ // Export Logs
+ window.wpbanExportLogs = function() {
+ window.location.href = wpban.ajax_url + '?action=wpban_export_logs&_ajax_nonce=' + wpban.nonce;
+ };
+
+ // Clear Logs
+ window.wpbanClearLogs = function() {
+ if (!confirm(wpban.i18n.confirm_clear)) {
+ return;
+ }
+
+ $.post(wpban.ajax_url, {
+ action: 'wpban_clear_logs',
+ _ajax_nonce: wpban.nonce
+ }, function(response) {
+ if (response.success) {
+ showMessage('success', response.data.message);
+ setTimeout(() => location.reload(), 1500);
+ }
+ });
+ };
+
+ // Test Email
+ window.wpbanTestEmail = function() {
+ const $button = event.target;
+ $button.disabled = true;
+
+ $.post(wpban.ajax_url, {
+ action: 'wpban_test_email',
+ _ajax_nonce: wpban.nonce
+ }, function(response) {
+ if (response.success) {
+ showMessage('success', response.data.message);
+ } else {
+ showMessage('error', response.data || wpban.i18n.error);
+ }
+ }).always(function() {
+ $button.disabled = false;
+ });
+ };
+
+ // Export Settings
+ window.wpbanExportSettings = function() {
+ $.get(wpban.ajax_url, {
+ action: 'wpban_export_settings',
+ _ajax_nonce: wpban.nonce
+ }, function(response) {
+ if (response.success) {
+ const blob = new Blob([response.data], {type: 'application/json'});
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = 'wpban-settings-' + new Date().toISOString().split('T')[0] + '.json';
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ }
+ });
+ };
+
+ // Optimize Database
+ window.wpbanOptimizeDatabase = function() {
+ const $button = event.target;
+ $button.disabled = true;
+
+ $.post(wpban.ajax_url, {
+ action: 'wpban_optimize_database',
+ _ajax_nonce: wpban.nonce
+ }, function(response) {
+ if (response.success) {
+ showMessage('success', 'Database optimized successfully!');
+ }
+ }).always(function() {
+ $button.disabled = false;
+ });
+ };
+
+ // Clear Old Logs
+ window.wpbanClearOldLogs = function() {
+ if (!confirm('This will permanently delete all logs older than 30 days. Continue?')) {
+ return;
+ }
+
+ $.post(wpban.ajax_url, {
+ action: 'wpban_clear_old_logs',
+ _ajax_nonce: wpban.nonce
+ }, function(response) {
+ if (response.success) {
+ showMessage('success', response.data.message);
+ setTimeout(() => location.reload(), 1500);
+ }
+ });
+ };
+
+ // Live Search for Crawlers
+ let searchTimeout;
+ $(document).on('input', '#wpban-crawler-search', function() {
+ const query = $(this).val().toLowerCase();
+ clearTimeout(searchTimeout);
+
+ searchTimeout = setTimeout(() => {
+ $('.wpban-crawler-item').each(function() {
+ const $item = $(this);
+ const text = $item.text().toLowerCase();
+ $item.toggle(text.indexOf(query) > -1);
+ });
+ }, 300);
+ });
+
+ // Real-time Log Updates (optional)
+ if ($('#wpban-logs-container').length) {
+ // Auto-refresh logs every 30 seconds
+ setInterval(function() {
+ if (document.visibilityState === 'visible') {
+ refreshLogs();
+ }
+ }, 30000);
+ }
+
+ function refreshLogs() {
+ const params = new URLSearchParams(window.location.search);
+
+ $.get(wpban.ajax_url, {
+ action: 'wpban_get_logs',
+ page: params.get('paged') || 1,
+ date_from: params.get('date_from'),
+ date_to: params.get('date_to'),
+ action_filter: params.get('action_filter'),
+ ip_filter: params.get('ip_filter'),
+ _ajax_nonce: wpban.nonce
+ }, function(response) {
+ if (response.success) {
+ $('#wpban-logs-container tbody').html(response.data.html);
+ }
+ });
+ }
+
+ // Show Message
+ function showMessage(type, message) {
+ const $message = $('
')
+ .addClass('wpban-message ' + type)
+ .text(message)
+ .prependTo('.wpban-wrap')
+ .hide()
+ .fadeIn();
+
+ setTimeout(() => {
+ $message.fadeOut(() => $message.remove());
+ }, 5000);
+ }
+
+ // Geo Blocking Mode Toggle
+ $('input[name="settings[geo_blocking][mode]"]').on('change', function() {
+ const mode = $(this).val();
+ const $label = $('.wpban-country-selector').prev('th').find('label');
+
+ if (mode === 'whitelist') {
+ $label.text('Allowed Countries');
+ } else {
+ $label.text('Blocked Countries');
+ }
+ });
+
+ // Initialize Select2 for country selector (if available)
+ if ($.fn.select2) {
+ $('.wpban-country-selector select').select2({
+ placeholder: 'Select countries...',
+ width: '100%'
+ });
+ }
+
+ // Handle browser restrictions toggle
+ $('input[name="settings[browser_restrictions][wechat_qq][enabled]"]').on('change', function() {
+ const $fields = $(this).closest('table').find('tr').not(':first');
+ $fields.toggle($(this).is(':checked'));
+ }).trigger('change');
+
+ // Email notifications toggle
+ $('input[name="settings[email_notifications][enabled]"]').on('change', function() {
+ const $fields = $(this).closest('table').find('tr').not(':first');
+ $fields.toggle($(this).is(':checked'));
+ }).trigger('change');
+
+ // Geo blocking toggle
+ $('input[name="settings[geo_blocking][enabled]"]').on('change', function() {
+ const $fields = $(this).closest('table').find('tr').not(':first');
+ $fields.toggle($(this).is(':checked'));
+ }).trigger('change');
+
+ // Keyboard shortcuts
+ $(document).on('keydown', function(e) {
+ // Ctrl/Cmd + S to save
+ if ((e.ctrlKey || e.metaKey) && e.key === 's') {
+ e.preventDefault();
+ $('#wpban-save-settings').trigger('click');
+ }
+ });
+
+ // Tooltip initialization
+ $('.wpban-tooltip').tooltip({
+ position: {
+ my: 'center bottom-10',
+ at: 'center top'
+ }
+ });
+
+ // Confirm before leaving with unsaved changes
+ let formChanged = false;
+ $('#wpban-settings-form').on('change', 'input, select, textarea', function() {
+ formChanged = true;
+ });
+
+ window.addEventListener('beforeunload', function(e) {
+ if (formChanged) {
+ e.preventDefault();
+ e.returnValue = 'You have unsaved changes. Are you sure you want to leave?';
+ }
+ });
+
+ $('#wpban-save-settings').on('click', function() {
+ formChanged = false;
+ });
+
+})(jQuery);
\ No newline at end of file
diff --git a/includes/class-wpban-admin.php b/includes/class-wpban-admin.php
new file mode 100644
index 0000000..e619996
--- /dev/null
+++ b/includes/class-wpban-admin.php
@@ -0,0 +1,994 @@
+security = $GLOBALS['wpban_security'];
+
+ add_action('admin_menu', [$this, 'add_menu']);
+ add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']);
+
+ // AJAX handlers
+ add_action('wp_ajax_wpban_save_settings', [$this, 'ajax_save_settings']);
+ add_action('wp_ajax_wpban_apply_template', [$this, 'ajax_apply_template']);
+ add_action('wp_ajax_wpban_get_logs', [$this, 'ajax_get_logs']);
+ add_action('wp_ajax_wpban_export_logs', [$this, 'ajax_export_logs']);
+ add_action('wp_ajax_wpban_clear_logs', [$this, 'ajax_clear_logs']);
+ add_action('wp_ajax_wpban_test_email', [$this, 'ajax_test_email']);
+ add_action('wp_ajax_wpban_get_country_stats', [$this, 'ajax_get_country_stats']);
+ }
+
+ public function add_menu() {
+ add_menu_page(
+ __('WPBan Security', 'wpban'),
+ __('WPBan Security', 'wpban'),
+ 'manage_options',
+ 'wpban',
+ [$this, 'render_dashboard'],
+ 'dashicons-shield',
+ 30
+ );
+
+ add_submenu_page(
+ 'wpban',
+ __('Settings', 'wpban'),
+ __('Settings', 'wpban'),
+ 'manage_options',
+ 'wpban-settings',
+ [$this, 'render_settings']
+ );
+
+ add_submenu_page(
+ 'wpban',
+ __('Security Logs', 'wpban'),
+ __('Logs', 'wpban'),
+ 'manage_options',
+ 'wpban-logs',
+ [$this, 'render_logs']
+ );
+
+ add_submenu_page(
+ 'wpban',
+ __('Tools', 'wpban'),
+ __('Tools', 'wpban'),
+ 'manage_options',
+ 'wpban-tools',
+ [$this, 'render_tools']
+ );
+ }
+
+ public function enqueue_scripts($hook) {
+ if (strpos($hook, 'wpban') === false) {
+ return;
+ }
+
+ wp_enqueue_style('wpban-admin', WPBAN_URL . 'assets/admin.css', [], WPBAN_VERSION);
+ wp_enqueue_script('wpban-admin', WPBAN_URL . 'assets/admin.js', ['jquery'], WPBAN_VERSION);
+ wp_localize_script('wpban-admin', 'wpban', [
+ 'ajax_url' => admin_url('admin-ajax.php'),
+ 'nonce' => wp_create_nonce('wpban_admin'),
+ 'i18n' => [
+ 'confirm_clear' => __('Are you sure you want to clear all logs?', 'wpban'),
+ 'confirm_template' => __('This will replace your current settings. Continue?', 'wpban'),
+ 'email_sent' => __('Test email sent!', 'wpban'),
+ 'error' => __('An error occurred', 'wpban')
+ ]
+ ]);
+
+ // Add Chart.js for statistics
+ wp_enqueue_script('chartjs', 'https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js', [], '3.9.1');
+ }
+
+ public function render_dashboard() {
+ $stats = $this->security->get_stats();
+ $bypass_url = home_url('/?wpban_bypass=' . get_option('wpban_bypass_path'));
+ ?>
+
+
+
+
+
+
+
+
+
+
+ 0 ? round(($stats['today_blocks'] / $yesterday - 1) * 100) : 0;
+ ?>
+
+ 0 ? '↑' : '↓'; ?> %
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ security->get_templates() as $id => $template): ?>
+
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+ timestamp)); ?> ago |
+ ip); ?> |
+ country_code ?: '-'); ?> |
+
+ action); ?> |
+ reason); ?> |
+
+
+
+
+
+
+
+
+
+ sanitize_text_field($_GET['date_from'] ?? ''),
+ 'date_to' => sanitize_text_field($_GET['date_to'] ?? ''),
+ 'action' => sanitize_text_field($_GET['action_filter'] ?? ''),
+ 'ip' => sanitize_text_field($_GET['ip_filter'] ?? ''),
+ 'country' => sanitize_text_field($_GET['country_filter'] ?? '')
+ ];
+
+ $result = $this->security->get_logs($filters, $current_page, $per_page);
+ ?>
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+ |
+
+
+
+
+ timestamp); ?> |
+
+ ip); ?>
+
+ |
+ country_code ?: '-'); ?> |
+
+
+ action); ?>
+
+ |
+ reason); ?> |
+
+
+ user_agent, 0, 50)); ?>
+
+ |
+
+
+ uri); ?>
+
+ |
+
+
+
+
+
+
+
+ 1): ?>
+
+
+ add_query_arg('paged', '%#%'),
+ 'format' => '',
+ 'prev_text' => '«',
+ 'next_text' => '»',
+ 'total' => $result['pages'],
+ 'current' => $current_page
+ ]);
+ ?>
+
+
+
+
+
+
+ $value) {
+ $settings['rate_limits'][$key] = max(1, intval($value));
+ }
+ }
+
+ update_option('wpban_settings', $settings);
+ $this->security->clear_cache();
+
+ wp_send_json_success(['message' => __('Settings saved successfully!', 'wpban')]);
+ }
+
+ public function ajax_apply_template() {
+ check_ajax_referer('wpban_admin', '_ajax_nonce');
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error('Permission denied');
+ }
+
+ $template_id = sanitize_key($_POST['template']);
+ $templates = $this->security->get_templates();
+
+ if (!isset($templates[$template_id])) {
+ wp_send_json_error(__('Invalid template', 'wpban'));
+ }
+
+ $current = get_option('wpban_settings', []);
+ $new_settings = array_merge($current, $templates[$template_id]['settings']);
+
+ update_option('wpban_settings', $new_settings);
+ $this->security->clear_cache();
+
+ wp_send_json_success(['message' => __('Template applied successfully!', 'wpban')]);
+ }
+
+ public function ajax_get_logs() {
+ check_ajax_referer('wpban_admin', '_ajax_nonce');
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error('Permission denied');
+ }
+
+ $page = intval($_POST['page'] ?? 1);
+ $filters = [
+ 'date_from' => sanitize_text_field($_POST['date_from'] ?? ''),
+ 'date_to' => sanitize_text_field($_POST['date_to'] ?? ''),
+ 'action' => sanitize_text_field($_POST['action'] ?? ''),
+ 'ip' => sanitize_text_field($_POST['ip'] ?? '')
+ ];
+
+ $result = $this->security->get_logs($filters, $page);
+
+ ob_start();
+ // Render log rows
+ foreach ($result['logs'] as $log) {
+ // ... render table rows ...
+ }
+ $html = ob_get_clean();
+
+ wp_send_json_success([
+ 'html' => $html,
+ 'pagination' => paginate_links([
+ 'total' => $result['pages'],
+ 'current' => $page
+ ])
+ ]);
+ }
+
+ public function ajax_export_logs() {
+ check_ajax_referer('wpban_admin', '_ajax_nonce');
+
+ if (!current_user_can('manage_options')) {
+ wp_die('Permission denied');
+ }
+
+ $logs = $this->security->get_logs([], 1, 10000);
+
+ header('Content-Type: text/csv');
+ header('Content-Disposition: attachment; filename="wpban-logs-' . date('Y-m-d') . '.csv"');
+
+ $output = fopen('php://output', 'w');
+ fputcsv($output, ['Time', 'IP', 'Country', 'Action', 'Reason', 'User Agent', 'Referer', 'URI']);
+
+ foreach ($logs['logs'] as $log) {
+ fputcsv($output, [
+ $log->timestamp,
+ $log->ip,
+ $log->country_code,
+ $log->action,
+ $log->reason,
+ $log->user_agent,
+ $log->referer,
+ $log->uri
+ ]);
+ }
+
+ fclose($output);
+ exit;
+ }
+
+ public function ajax_clear_logs() {
+ check_ajax_referer('wpban_admin', '_ajax_nonce');
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error('Permission denied');
+ }
+
+ global $wpdb;
+ $wpdb->query("TRUNCATE TABLE {$wpdb->prefix}wpban_logs");
+
+ wp_send_json_success(['message' => __('Logs cleared successfully!', 'wpban')]);
+ }
+
+ public function ajax_test_email() {
+ check_ajax_referer('wpban_admin', '_ajax_nonce');
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error('Permission denied');
+ }
+
+ $settings = get_option('wpban_settings', []);
+ $to = $settings['email_notifications']['recipient'] ?? get_option('admin_email');
+ $subject = sprintf('[%s] WPBan Test Email', get_bloginfo('name'));
+ $message = "This is a test email from WPBan Security.\n\n";
+ $message .= "If you received this email, your notifications are working correctly!";
+
+ $sent = wp_mail($to, $subject, $message);
+
+ if ($sent) {
+ wp_send_json_success(['message' => __('Test email sent successfully!', 'wpban')]);
+ } else {
+ wp_send_json_error(__('Failed to send test email. Please check your email settings.', 'wpban'));
+ }
+ }
+
+ public function ajax_get_country_stats() {
+ check_ajax_referer('wpban_admin', '_ajax_nonce');
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error('Permission denied');
+ }
+
+ global $wpdb;
+ $stats = $wpdb->get_results(
+ "SELECT country_code, COUNT(*) as count
+ FROM {$wpdb->prefix}wpban_logs
+ WHERE country_code IS NOT NULL
+ GROUP BY country_code
+ ORDER BY count DESC
+ LIMIT 20"
+ );
+
+ wp_send_json_success($stats);
+ }
+}
\ No newline at end of file
diff --git a/includes/class-wpban-cache.php b/includes/class-wpban-cache.php
new file mode 100644
index 0000000..c5237ef
--- /dev/null
+++ b/includes/class-wpban-cache.php
@@ -0,0 +1,82 @@
+use_transients = true;
+ }
+ }
+
+ public function get($key, $callback = null, $expiration = 3600) {
+ $settings = get_option('wpban_settings', []);
+ if (empty($settings['enable_caching'])) {
+ return $callback ? $callback() : false;
+ }
+
+ $cache_key = $this->get_cache_key($key);
+
+ if ($this->use_transients) {
+ $value = get_transient($cache_key);
+ } else {
+ $value = wp_cache_get($key, $this->cache_group);
+ }
+
+ if ($value === false && $callback) {
+ $value = $callback();
+ $this->set($key, $value, $expiration);
+ }
+
+ return $value;
+ }
+
+ public function set($key, $value, $expiration = 3600) {
+ $settings = get_option('wpban_settings', []);
+ if (empty($settings['enable_caching'])) {
+ return false;
+ }
+
+ $cache_key = $this->get_cache_key($key);
+
+ if ($this->use_transients) {
+ return set_transient($cache_key, $value, $expiration);
+ } else {
+ return wp_cache_set($key, $value, $this->cache_group, $expiration);
+ }
+ }
+
+ public function delete($key) {
+ $cache_key = $this->get_cache_key($key);
+
+ if ($this->use_transients) {
+ return delete_transient($cache_key);
+ } else {
+ return wp_cache_delete($key, $this->cache_group);
+ }
+ }
+
+ public function clear() {
+ if ($this->use_transients) {
+ // Clear all WPBan transients
+ global $wpdb;
+ $wpdb->query(
+ "DELETE FROM {$wpdb->options}
+ WHERE option_name LIKE '_transient_wpban_%'
+ OR option_name LIKE '_transient_timeout_wpban_%'"
+ );
+ } else {
+ // Clear object cache group
+ wp_cache_delete_group($this->cache_group);
+ }
+ }
+
+ private function get_cache_key($key) {
+ return $this->use_transients ? 'wpban_' . md5($key) : $key;
+ }
+}
\ No newline at end of file
diff --git a/includes/class-wpban-core.php b/includes/class-wpban-core.php
new file mode 100644
index 0000000..33ff211
--- /dev/null
+++ b/includes/class-wpban-core.php
@@ -0,0 +1,300 @@
+cache = $cache;
+ $this->logger = $logger;
+ $this->settings = $this->get_settings();
+
+ // Check bypass first
+ add_action('init', [$this, 'check_bypass'], 1);
+
+ // Main security checks
+ add_action('init', [$this, 'check_ban'], 10);
+ add_action('init', [$this, 'check_login_restriction'], 11);
+ add_action('wp_footer', [$this, 'check_browser_restrictions']);
+
+ // Robots.txt modifications
+ add_filter('robots_txt', [$this, 'modify_robots_txt'], 10, 2);
+
+ // Performance: only load heavy checks if needed
+ if ($this->should_check_crawlers()) {
+ add_action('init', [$this, 'check_crawlers'], 12);
+ }
+ }
+
+ private function get_settings() {
+ return $this->cache->get('settings', function() {
+ return get_option('wpban_settings', [
+ 'banned_ips' => [],
+ 'banned_ranges' => [],
+ 'banned_hosts' => [],
+ 'banned_referers' => [],
+ 'banned_agents' => [],
+ 'whitelist_ips' => [],
+ 'login_allowed_ips' => [],
+ 'blocked_crawlers' => [],
+ 'browser_restrictions' => [],
+ 'ban_message' => '
Access Denied
Your access to this site has been restricted.
',
+ 'enable_logging' => true,
+ 'enable_caching' => true,
+ 'reverse_proxy' => false
+ ]);
+ });
+ }
+
+ public function check_bypass() {
+ $bypass_path = get_option('wpban_bypass_path');
+
+ // Check URL bypass
+ if (isset($_GET['wpban_bypass']) && $_GET['wpban_bypass'] === $bypass_path) {
+ setcookie(WPBAN_BYPASS_KEY, $bypass_path, time() + DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);
+ $this->bypass_cookie = true;
+ wp_redirect(remove_query_arg('wpban_bypass'));
+ exit;
+ }
+
+ // Check cookie bypass
+ if (isset($_COOKIE[WPBAN_BYPASS_KEY]) && $_COOKIE[WPBAN_BYPASS_KEY] === $bypass_path) {
+ $this->bypass_cookie = true;
+ }
+ }
+
+ public function check_ban() {
+ if ($this->bypass_cookie) {
+ return;
+ }
+
+ $ip = wpban_get_ip($this->settings['reverse_proxy']);
+ $checks = [
+ 'ip' => $this->is_ip_banned($ip),
+ 'host' => $this->is_host_banned($ip),
+ 'referer' => $this->is_referer_banned(),
+ 'agent' => $this->is_agent_banned()
+ ];
+
+ foreach ($checks as $type => $banned) {
+ if ($banned && !$this->is_whitelisted($ip)) {
+ $this->logger->log($ip, 'banned', $type . '_ban');
+ $this->show_ban_message();
+ }
+ }
+ }
+
+ private function is_ip_banned($ip) {
+ // Check exact IPs and wildcards
+ $banned_ips = $this->cache->get('banned_ips_compiled', function() {
+ $patterns = [];
+ foreach ($this->settings['banned_ips'] as $pattern) {
+ $patterns[] = $this->compile_wildcard_pattern($pattern);
+ }
+ return $patterns;
+ });
+
+ foreach ($banned_ips as $pattern) {
+ if (preg_match($pattern, $ip)) {
+ return true;
+ }
+ }
+
+ // Check IP ranges
+ foreach ($this->settings['banned_ranges'] as $range) {
+ if ($this->ip_in_range($ip, $range)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function is_host_banned($ip) {
+ if (empty($this->settings['banned_hosts'])) {
+ return false;
+ }
+
+ $hostname = $this->cache->get('hostname_' . $ip, function() use ($ip) {
+ return gethostbyaddr($ip);
+ }, 3600); // Cache for 1 hour
+
+ foreach ($this->settings['banned_hosts'] as $pattern) {
+ if (wpban_match_wildcard($pattern, $hostname)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function is_referer_banned() {
+ $referer = $_SERVER['HTTP_REFERER'] ?? '';
+ if (empty($referer) || empty($this->settings['banned_referers'])) {
+ return false;
+ }
+
+ foreach ($this->settings['banned_referers'] as $pattern) {
+ if (wpban_match_wildcard($pattern, $referer)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function is_agent_banned() {
+ $agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
+ if (empty($agent) || empty($this->settings['banned_agents'])) {
+ return false;
+ }
+
+ foreach ($this->settings['banned_agents'] as $pattern) {
+ if (wpban_match_wildcard($pattern, $agent)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function is_whitelisted($ip) {
+ foreach ($this->settings['whitelist_ips'] as $pattern) {
+ if (wpban_match_wildcard($pattern, $ip) || $this->ip_in_range($ip, $pattern)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public function check_login_restriction() {
+ if ($this->bypass_cookie || empty($this->settings['login_allowed_ips'])) {
+ return;
+ }
+
+ $login_files = ['wp-login.php', 'wp-register.php'];
+ $current_file = basename($_SERVER['SCRIPT_FILENAME']);
+
+ if (in_array($current_file, $login_files)) {
+ $ip = wpban_get_ip($this->settings['reverse_proxy']);
+ $allowed = false;
+
+ foreach ($this->settings['login_allowed_ips'] as $pattern) {
+ if (wpban_match_wildcard($pattern, $ip) || $this->ip_in_range($ip, $pattern)) {
+ $allowed = true;
+ break;
+ }
+ }
+
+ if (!$allowed) {
+ $this->logger->log($ip, 'blocked', 'login_restriction');
+ wp_redirect(home_url());
+ exit;
+ }
+ }
+ }
+
+ public function check_browser_restrictions() {
+ if ($this->bypass_cookie || empty($this->settings['browser_restrictions'])) {
+ return;
+ }
+
+ $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
+
+ // WeChat/QQ browser check
+ if (isset($this->settings['browser_restrictions']['wechat_qq']) &&
+ $this->settings['browser_restrictions']['wechat_qq']['enabled'] &&
+ (strpos($ua, 'MQQBrowser') !== false || strpos($ua, 'MicroMessenger') !== false)) {
+
+ $this->logger->log(wpban_get_ip($this->settings['reverse_proxy']), 'blocked', 'browser_restriction');
+ $this->show_browser_block_message($this->settings['browser_restrictions']['wechat_qq']);
+ }
+ }
+
+ public function check_crawlers() {
+ if ($this->bypass_cookie || empty($this->settings['blocked_crawlers'])) {
+ return;
+ }
+
+ $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
+ $ip = wpban_get_ip($this->settings['reverse_proxy']);
+
+ foreach ($this->settings['blocked_crawlers'] as $crawler) {
+ if (stripos($ua, $crawler) !== false) {
+ $this->logger->log($ip, 'blocked', 'crawler_block', $crawler);
+ http_response_code(403);
+ exit('Access denied for crawlers');
+ }
+ }
+ }
+
+ public function modify_robots_txt($output, $public) {
+ if (!empty($this->settings['blocked_crawlers'])) {
+ $output .= "\n# wpban Rules\n";
+ foreach ($this->settings['blocked_crawlers'] as $crawler) {
+ $output .= "User-agent: $crawler\n";
+ $output .= "Disallow: /\n\n";
+ }
+ }
+ return $output;
+ }
+
+ private function should_check_crawlers() {
+ // Performance optimization: only check if crawlers are configured
+ return !empty($this->settings['blocked_crawlers']);
+ }
+
+ private function compile_wildcard_pattern($pattern) {
+ $pattern = preg_quote($pattern, '/');
+ $pattern = str_replace('\*', '.*', $pattern);
+ return '/^' . $pattern . '$/i';
+ }
+
+ private function ip_in_range($ip, $range) {
+ if (strpos($range, '/') !== false) {
+ // CIDR notation
+ list($subnet, $bits) = explode('/', $range);
+ $ip_long = ip2long($ip);
+ $subnet_long = ip2long($subnet);
+ $mask = -1 << (32 - $bits);
+ return ($ip_long & $mask) == ($subnet_long & $mask);
+ } elseif (strpos($range, '-') !== false) {
+ // Range notation
+ list($start, $end) = explode('-', $range);
+ $ip_long = ip2long($ip);
+ return ($ip_long >= ip2long(trim($start)) && $ip_long <= ip2long(trim($end)));
+ }
+ return false;
+ }
+
+ private function show_ban_message() {
+ $message = $this->settings['ban_message'];
+ $message = str_replace(
+ ['%IP%', '%DATE%', '%SITE%'],
+ [wpban_get_ip($this->settings['reverse_proxy']), date('Y-m-d H:i:s'), get_bloginfo('name')],
+ $message
+ );
+
+ wp_die($message, 'Access Denied', ['response' => 403]);
+ }
+
+ private function show_browser_block_message($settings) {
+ ?>
+
+
+ table_name = $wpdb->prefix . 'wpban_logs';
+ }
+
+ public function log($ip, $action, $reason, $details = '') {
+ $settings = get_option('wpban_settings', []);
+ if (empty($settings['enable_logging'])) {
+ return;
+ }
+
+ global $wpdb;
+
+ $data = [
+ 'ip' => $ip,
+ 'user_agent' => substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 500),
+ 'referer' => substr($_SERVER['HTTP_REFERER'] ?? '', 0, 500),
+ 'uri' => substr($_SERVER['REQUEST_URI'] ?? '', 0, 500),
+ 'action' => $action,
+ 'reason' => $reason . ($details ? ' - ' . $details : ''),
+ 'timestamp' => current_time('mysql')
+ ];
+
+ $wpdb->insert($this->table_name, $data);
+
+ // Clean old logs (keep last 30 days)
+ $this->clean_old_logs();
+ }
+
+ public function get_logs($filters = []) {
+ global $wpdb;
+
+ $where = [];
+ $where_values = [];
+
+ if (!empty($filters['date'])) {
+ $where[] = "DATE(timestamp) = %s";
+ $where_values[] = $filters['date'];
+ }
+
+ if (!empty($filters['action'])) {
+ $where[] = "action = %s";
+ $where_values[] = $filters['action'];
+ }
+
+ if (!empty($filters['ip'])) {
+ $where[] = "ip = %s";
+ $where_values[] = $filters['ip'];
+ }
+
+ $where_clause = $where ? 'WHERE ' . implode(' AND ', $where) : '';
+ $limit = isset($filters['limit']) ? intval($filters['limit']) : 100;
+
+ $query = "SELECT * FROM {$this->table_name} {$where_clause} ORDER BY timestamp DESC LIMIT %d";
+ $where_values[] = $limit;
+
+ return $wpdb->get_results($wpdb->prepare($query, $where_values));
+ }
+
+ public function get_stats() {
+ global $wpdb;
+
+ $settings = get_option('wpban_settings', []);
+
+ // Total blocks
+ $total_blocks = $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name}");
+
+ // Unique IPs
+ $unique_ips = $wpdb->get_var("SELECT COUNT(DISTINCT ip) FROM {$this->table_name}");
+
+ // Today's blocks
+ $today = current_time('Y-m-d');
+ $today_blocks = $wpdb->get_var($wpdb->prepare(
+ "SELECT COUNT(*) FROM {$this->table_name} WHERE DATE(timestamp) = %s",
+ $today
+ ));
+
+ // Count active rules
+ $active_rules = 0;
+ $rule_types = ['banned_ips', 'banned_ranges', 'banned_hosts', 'banned_referers', 'banned_agents', 'blocked_crawlers'];
+ foreach ($rule_types as $type) {
+ if (!empty($settings[$type])) {
+ $active_rules += count($settings[$type]);
+ }
+ }
+
+ return [
+ 'total_blocks' => $total_blocks,
+ 'unique_ips' => $unique_ips,
+ 'today_blocks' => $today_blocks,
+ 'active_rules' => $active_rules
+ ];
+ }
+
+ public function clear_logs() {
+ global $wpdb;
+ $wpdb->query("TRUNCATE TABLE {$this->table_name}");
+ }
+
+ private function clean_old_logs() {
+ global $wpdb;
+
+ // Run cleanup only 1% of the time to avoid performance impact
+ if (mt_rand(1, 100) > 1) {
+ return;
+ }
+
+ $days_to_keep = 30;
+ $cutoff_date = date('Y-m-d H:i:s', strtotime("-{$days_to_keep} days"));
+
+ $wpdb->query($wpdb->prepare(
+ "DELETE FROM {$this->table_name} WHERE timestamp < %s",
+ $cutoff_date
+ ));
+ }
+}
\ No newline at end of file
diff --git a/includes/class-wpban-security.php b/includes/class-wpban-security.php
new file mode 100644
index 0000000..9f5b22a
--- /dev/null
+++ b/includes/class-wpban-security.php
@@ -0,0 +1,719 @@
+load_settings();
+ $this->ip_address = wpban_get_ip($this->settings['reverse_proxy'] ?? false);
+
+ // Core hooks
+ add_action('init', [$this, 'check_bypass'], 1);
+ add_action('init', [$this, 'check_rate_limit'], 5);
+ add_action('init', [$this, 'check_geo_block'], 6);
+ add_action('init', [$this, 'check_ban'], 10);
+ add_action('init', [$this, 'check_login_restriction'], 11);
+ add_action('init', [$this, 'check_crawlers'], 12);
+ add_action('wp_footer', [$this, 'check_browser_restrictions']);
+ add_filter('robots_txt', [$this, 'modify_robots_txt'], 10, 2);
+
+ // Login protection
+ add_action('wp_login_failed', [$this, 'handle_login_failed']);
+ add_filter('authenticate', [$this, 'check_login_attempt'], 30, 3);
+ }
+
+ private function load_settings() {
+ $this->settings = wp_cache_get('settings', $this->cache_group);
+ if ($this->settings === false) {
+ $this->settings = get_option('wpban_settings', []);
+ wp_cache_set('settings', $this->settings, $this->cache_group, 3600);
+ }
+ }
+
+ public function check_bypass() {
+ $bypass_path = get_option('wpban_bypass_path');
+
+ // URL bypass
+ if (isset($_GET['wpban_bypass']) && $_GET['wpban_bypass'] === $bypass_path) {
+ setcookie(WPBAN_BYPASS_KEY, $bypass_path, time() + DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);
+ $this->bypass_active = true;
+ $this->log('bypass', 'bypass_activated');
+ wp_redirect(remove_query_arg('wpban_bypass'));
+ exit;
+ }
+
+ // Cookie bypass
+ if (isset($_COOKIE[WPBAN_BYPASS_KEY]) && $_COOKIE[WPBAN_BYPASS_KEY] === $bypass_path) {
+ $this->bypass_active = true;
+ }
+ }
+
+ public function check_rate_limit() {
+ if ($this->bypass_active || empty($this->settings['rate_limits'])) {
+ return;
+ }
+
+ global $wpdb;
+ $table = $wpdb->prefix . 'wpban_rate_limits';
+ $limits = $this->settings['rate_limits'];
+
+ // Determine action type
+ $action = 'general';
+ if (strpos($_SERVER['REQUEST_URI'], '/wp-login.php') !== false) {
+ $action = 'login';
+ } elseif (strpos($_SERVER['REQUEST_URI'], '/wp-json/') !== false) {
+ $action = 'api';
+ }
+
+ // Get limit settings
+ $window = 60; // seconds
+ $max_requests = $limits['requests_per_minute'] ?? 60;
+
+ if ($action === 'login') {
+ $window = 3600;
+ $max_requests = $limits['login_per_hour'] ?? 5;
+ } elseif ($action === 'api') {
+ $max_requests = $limits['api_per_minute'] ?? 30;
+ }
+
+ $window_start = date('Y-m-d H:i:s', time() - $window);
+
+ // Check current count
+ $current = $wpdb->get_row($wpdb->prepare(
+ "SELECT count, window_start FROM $table WHERE ip = %s AND action = %s AND window_start > %s",
+ $this->ip_address, $action, $window_start
+ ));
+
+ if ($current) {
+ if ($current->count >= $max_requests) {
+ $this->log('blocked', 'rate_limit', $action);
+ $this->send_notification('rate_limit', [
+ 'action' => $action,
+ 'count' => $current->count
+ ]);
+ wp_die(__('Too many requests. Please try again later.', 'wpban'), 429);
+ }
+
+ // Increment counter
+ $wpdb->query($wpdb->prepare(
+ "UPDATE $table SET count = count + 1, last_attempt = %s WHERE ip = %s AND action = %s",
+ current_time('mysql'), $this->ip_address, $action
+ ));
+ } else {
+ // Create new record
+ $wpdb->insert($table, [
+ 'ip' => $this->ip_address,
+ 'action' => $action,
+ 'count' => 1,
+ 'window_start' => current_time('mysql'),
+ 'last_attempt' => current_time('mysql')
+ ]);
+ }
+ }
+
+ public function check_geo_block() {
+ if ($this->bypass_active || empty($this->settings['geo_blocking']['enabled'])) {
+ return;
+ }
+
+ $country = $this->get_country_code($this->ip_address);
+ $blocked_countries = $this->settings['geo_blocking']['blocked_countries'] ?? [];
+ $allowed_countries = $this->settings['geo_blocking']['allowed_countries'] ?? [];
+
+ // If allowed list is set, only allow those countries
+ if (!empty($allowed_countries) && !in_array($country, $allowed_countries)) {
+ $this->log('blocked', 'geo_block', $country);
+ $this->send_notification('geo_block', ['country' => $country]);
+ wp_die(__('Access denied from your location.', 'wpban'), 403);
+ }
+
+ // Check blocked countries
+ if (in_array($country, $blocked_countries)) {
+ $this->log('blocked', 'geo_block', $country);
+ $this->send_notification('geo_block', ['country' => $country]);
+ wp_die(__('Access denied from your location.', 'wpban'), 403);
+ }
+ }
+
+ public function check_ban() {
+ if ($this->bypass_active) {
+ return;
+ }
+
+ $checks = [
+ 'ip' => $this->is_ip_banned(),
+ 'host' => $this->is_host_banned(),
+ 'referer' => $this->is_referer_banned(),
+ 'agent' => $this->is_agent_banned()
+ ];
+
+ foreach ($checks as $type => $banned) {
+ if ($banned && !$this->is_whitelisted()) {
+ $this->log('banned', $type . '_ban');
+ $this->show_ban_message();
+ }
+ }
+ }
+
+ public function check_login_restriction() {
+ if ($this->bypass_active || empty($this->settings['login_allowed_ips'])) {
+ return;
+ }
+
+ $login_files = ['wp-login.php', 'wp-register.php', 'xmlrpc.php'];
+ $current_file = basename($_SERVER['SCRIPT_FILENAME']);
+
+ if (in_array($current_file, $login_files)) {
+ $allowed = false;
+ foreach ($this->settings['login_allowed_ips'] as $pattern) {
+ if (wpban_match_pattern($pattern, $this->ip_address)) {
+ $allowed = true;
+ break;
+ }
+ }
+
+ if (!$allowed) {
+ $this->log('blocked', 'login_restriction');
+ wp_redirect(home_url());
+ exit;
+ }
+ }
+ }
+
+ public function check_crawlers() {
+ if ($this->bypass_active || empty($this->settings['blocked_crawlers'])) {
+ return;
+ }
+
+ $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
+ foreach ($this->settings['blocked_crawlers'] as $crawler) {
+ if (stripos($ua, $crawler) !== false) {
+ $this->log('blocked', 'crawler_block', $crawler);
+ http_response_code(403);
+ exit('Access denied');
+ }
+ }
+ }
+
+ public function check_browser_restrictions() {
+ if ($this->bypass_active || empty($this->settings['browser_restrictions'])) {
+ return;
+ }
+
+ $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
+
+ if (isset($this->settings['browser_restrictions']['wechat_qq']) &&
+ $this->settings['browser_restrictions']['wechat_qq']['enabled'] &&
+ (strpos($ua, 'MQQBrowser') !== false || strpos($ua, 'MicroMessenger') !== false)) {
+
+ $this->log('blocked', 'browser_restriction', 'WeChat/QQ');
+ $this->show_browser_block_message($this->settings['browser_restrictions']['wechat_qq']);
+ }
+ }
+
+ public function handle_login_failed($username) {
+ $this->log('failed_login', 'login_attempt', $username);
+
+ // Check for brute force
+ global $wpdb;
+ $table = $wpdb->prefix . 'wpban_logs';
+ $count = $wpdb->get_var($wpdb->prepare(
+ "SELECT COUNT(*) FROM $table WHERE ip = %s AND action = 'failed_login' AND timestamp > %s",
+ $this->ip_address,
+ date('Y-m-d H:i:s', strtotime('-1 hour'))
+ ));
+
+ if ($count >= 10) {
+ $this->send_notification('brute_force', [
+ 'attempts' => $count,
+ 'username' => $username
+ ]);
+ }
+ }
+
+ public function check_login_attempt($user, $username, $password) {
+ if (empty($username) || empty($password)) {
+ return $user;
+ }
+
+ // Additional login security checks can be added here
+ return $user;
+ }
+
+ public function modify_robots_txt($output, $public) {
+ if (!empty($this->settings['blocked_crawlers'])) {
+ $output .= "\n# WPBan Security Rules\n";
+ foreach ($this->settings['blocked_crawlers'] as $crawler) {
+ $output .= "User-agent: $crawler\n";
+ $output .= "Disallow: /\n\n";
+ }
+ }
+ return $output;
+ }
+
+ // Helper methods
+ private function is_ip_banned() {
+ foreach ($this->settings['banned_ips'] ?? [] as $pattern) {
+ if (wpban_match_pattern($pattern, $this->ip_address)) {
+ return true;
+ }
+ }
+
+ foreach ($this->settings['banned_ranges'] ?? [] as $range) {
+ if (wpban_ip_in_range($this->ip_address, $range)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function is_host_banned() {
+ if (empty($this->settings['banned_hosts'])) {
+ return false;
+ }
+
+ $hostname = $this->get_cached_data('hostname_' . $this->ip_address, function() {
+ return gethostbyaddr($this->ip_address);
+ }, 3600);
+
+ foreach ($this->settings['banned_hosts'] as $pattern) {
+ if (wpban_match_pattern($pattern, $hostname)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function is_referer_banned() {
+ $referer = $_SERVER['HTTP_REFERER'] ?? '';
+ if (empty($referer) || empty($this->settings['banned_referers'])) {
+ return false;
+ }
+
+ foreach ($this->settings['banned_referers'] as $pattern) {
+ if (wpban_match_pattern($pattern, $referer)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function is_agent_banned() {
+ $agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
+ if (empty($agent) || empty($this->settings['banned_agents'])) {
+ return false;
+ }
+
+ foreach ($this->settings['banned_agents'] as $pattern) {
+ if (wpban_match_pattern($pattern, $agent)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function is_whitelisted() {
+ foreach ($this->settings['whitelist_ips'] ?? [] as $pattern) {
+ if (wpban_match_pattern($pattern, $this->ip_address) ||
+ wpban_ip_in_range($this->ip_address, $pattern)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private function get_country_code($ip) {
+ global $wpdb;
+ $table = $wpdb->prefix . 'wpban_geo_cache';
+
+ // Check cache first
+ $cached = $wpdb->get_var($wpdb->prepare(
+ "SELECT country_code FROM $table WHERE ip = %s",
+ $ip
+ ));
+
+ if ($cached) {
+ return $cached;
+ }
+
+ // Use IP geolocation service
+ $country = $this->lookup_country($ip);
+
+ // Cache result
+ if ($country) {
+ $wpdb->replace($table, [
+ 'ip' => $ip,
+ 'country_code' => $country['code'],
+ 'country_name' => $country['name'],
+ 'city' => $country['city'] ?? null,
+ 'cached_at' => current_time('mysql')
+ ]);
+ }
+
+ return $country['code'] ?? 'XX';
+ }
+
+ private function lookup_country($ip) {
+ // Try multiple services
+ $services = [
+ 'ipapi' => "http://ip-api.com/json/{$ip}?fields=status,country,countryCode,city",
+ 'ipinfo' => "https://ipinfo.io/{$ip}/json"
+ ];
+
+ foreach ($services as $name => $url) {
+ $response = wp_remote_get($url, ['timeout' => 2]);
+
+ if (!is_wp_error($response)) {
+ $data = json_decode(wp_remote_retrieve_body($response), true);
+
+ if ($name === 'ipapi' && $data['status'] === 'success') {
+ return [
+ 'code' => $data['countryCode'],
+ 'name' => $data['country'],
+ 'city' => $data['city']
+ ];
+ } elseif ($name === 'ipinfo' && isset($data['country'])) {
+ return [
+ 'code' => $data['country'],
+ 'name' => $data['country'],
+ 'city' => $data['city'] ?? null
+ ];
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private function log($action, $reason, $details = '') {
+ if (empty($this->settings['enable_logging'])) {
+ return;
+ }
+
+ global $wpdb;
+ $country = $action === 'geo_block' ? $details : $this->get_country_code($this->ip_address);
+
+ $wpdb->insert($wpdb->prefix . 'wpban_logs', [
+ 'ip' => $this->ip_address,
+ 'country_code' => $country,
+ 'user_agent' => substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 500),
+ 'referer' => substr($_SERVER['HTTP_REFERER'] ?? '', 0, 500),
+ 'uri' => substr($_SERVER['REQUEST_URI'] ?? '', 0, 500),
+ 'action' => $action,
+ 'reason' => $reason . ($details ? ' - ' . $details : ''),
+ 'timestamp' => current_time('mysql')
+ ]);
+ }
+
+ private function send_notification($type, $data = []) {
+ if (empty($this->settings['email_notifications']['enabled']) ||
+ !in_array($type, $this->settings['email_notifications']['events'] ?? [])) {
+ return;
+ }
+
+ // Check threshold
+ global $wpdb;
+ $count = $wpdb->get_var($wpdb->prepare(
+ "SELECT COUNT(*) FROM {$wpdb->prefix}wpban_logs
+ WHERE ip = %s AND timestamp > %s",
+ $this->ip_address,
+ date('Y-m-d H:i:s', strtotime('-1 hour'))
+ ));
+
+ if ($count < ($this->settings['email_notifications']['threshold'] ?? 10)) {
+ return;
+ }
+
+ // Send email
+ $to = $this->settings['email_notifications']['recipient'];
+ $subject = sprintf(__('[%s] Security Alert: %s', 'wpban'), get_bloginfo('name'), $type);
+ $message = $this->format_notification_email($type, $data);
+
+ wp_mail($to, $subject, $message);
+ }
+
+ private function format_notification_email($type, $data) {
+ $site = get_bloginfo('name');
+ $time = current_time('mysql');
+
+ $message = "Security alert on {$site}\n\n";
+ $message .= "Event: {$type}\n";
+ $message .= "Time: {$time}\n";
+ $message .= "IP Address: {$this->ip_address}\n";
+ $message .= "Country: " . $this->get_country_code($this->ip_address) . "\n";
+
+ if (!empty($data)) {
+ $message .= "\nDetails:\n";
+ foreach ($data as $key => $value) {
+ $message .= "- {$key}: {$value}\n";
+ }
+ }
+
+ $message .= "\nView logs: " . admin_url('admin.php?page=wpban-logs');
+
+ return $message;
+ }
+
+ private function show_ban_message() {
+ $message = $this->settings['ban_message'] ?? '
Access Denied
';
+ $message = str_replace(
+ ['%IP%', '%DATE%', '%SITE%'],
+ [$this->ip_address, current_time('mysql'), get_bloginfo('name')],
+ $message
+ );
+
+ wp_die($message, 'Access Denied', ['response' => 403]);
+ }
+
+ private function show_browser_block_message($settings) {
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ cache_group);
+
+ if ($value === false) {
+ $value = $callback();
+ wp_cache_set($key, $value, $this->cache_group, $expiration);
+ }
+
+ return $value;
+ }
+
+ // Public methods for admin
+ public function get_logs($filters = [], $page = 1, $per_page = 50) {
+ global $wpdb;
+ $table = $wpdb->prefix . 'wpban_logs';
+
+ $where = [];
+ $where_values = [];
+
+ if (!empty($filters['date_from'])) {
+ $where[] = "timestamp >= %s";
+ $where_values[] = $filters['date_from'] . ' 00:00:00';
+ }
+
+ if (!empty($filters['date_to'])) {
+ $where[] = "timestamp <= %s";
+ $where_values[] = $filters['date_to'] . ' 23:59:59';
+ }
+
+ if (!empty($filters['action'])) {
+ $where[] = "action = %s";
+ $where_values[] = $filters['action'];
+ }
+
+ if (!empty($filters['ip'])) {
+ $where[] = "ip LIKE %s";
+ $where_values[] = '%' . $wpdb->esc_like($filters['ip']) . '%';
+ }
+
+ if (!empty($filters['country'])) {
+ $where[] = "country_code = %s";
+ $where_values[] = $filters['country'];
+ }
+
+ $where_clause = $where ? 'WHERE ' . implode(' AND ', $where) : '';
+
+ // Get total count
+ $count_query = "SELECT COUNT(*) FROM $table $where_clause";
+ $total = $wpdb->get_var($wpdb->prepare($count_query, $where_values));
+
+ // Get logs with pagination
+ $offset = ($page - 1) * $per_page;
+ $query = "SELECT * FROM $table $where_clause ORDER BY timestamp DESC LIMIT %d OFFSET %d";
+ $where_values[] = $per_page;
+ $where_values[] = $offset;
+
+ $logs = $wpdb->get_results($wpdb->prepare($query, $where_values));
+
+ return [
+ 'logs' => $logs,
+ 'total' => $total,
+ 'pages' => ceil($total / $per_page),
+ 'current_page' => $page
+ ];
+ }
+
+ public function get_stats() {
+ global $wpdb;
+ $prefix = $wpdb->prefix;
+
+ $stats = [
+ 'total_blocks' => $wpdb->get_var("SELECT COUNT(*) FROM {$prefix}wpban_logs"),
+ 'unique_ips' => $wpdb->get_var("SELECT COUNT(DISTINCT ip) FROM {$prefix}wpban_logs"),
+ 'today_blocks' => $wpdb->get_var($wpdb->prepare(
+ "SELECT COUNT(*) FROM {$prefix}wpban_logs WHERE DATE(timestamp) = %s",
+ current_time('Y-m-d')
+ )),
+ 'active_rules' => $this->count_active_rules(),
+ 'top_countries' => $wpdb->get_results(
+ "SELECT country_code, COUNT(*) as count
+ FROM {$prefix}wpban_logs
+ WHERE country_code IS NOT NULL
+ GROUP BY country_code
+ ORDER BY count DESC
+ LIMIT 10"
+ ),
+ 'recent_blocks' => $wpdb->get_results(
+ "SELECT * FROM {$prefix}wpban_logs
+ ORDER BY timestamp DESC
+ LIMIT 10"
+ )
+ ];
+
+ return $stats;
+ }
+
+ private function count_active_rules() {
+ $count = 0;
+ $rule_types = [
+ 'banned_ips', 'banned_ranges', 'banned_hosts',
+ 'banned_referers', 'banned_agents', 'blocked_crawlers'
+ ];
+
+ foreach ($rule_types as $type) {
+ $count += count($this->settings[$type] ?? []);
+ }
+
+ return $count;
+ }
+
+ public function clear_cache() {
+ wp_cache_delete_group($this->cache_group);
+ }
+
+ public function get_templates() {
+ return [
+ 'basic' => [
+ 'name' => __('Basic Protection', 'wpban'),
+ 'description' => __('Essential security for most WordPress sites', 'wpban'),
+ 'settings' => [
+ 'banned_agents' => ['*bot*', '*crawler*', '*spider*'],
+ 'enable_logging' => true,
+ 'rate_limits' => [
+ 'requests_per_minute' => 60,
+ 'login_per_hour' => 5
+ ]
+ ]
+ ],
+ 'strict' => [
+ 'name' => __('Strict Security', 'wpban'),
+ 'description' => __('Maximum protection with geo-blocking and rate limiting', 'wpban'),
+ 'settings' => [
+ 'banned_agents' => ['*bot*', '*crawler*', '*spider*', '*scraper*', 'curl*', 'wget*'],
+ 'blocked_crawlers' => ['GPTBot', 'ChatGPT-User', 'ClaudeBot', 'CCBot'],
+ 'rate_limits' => [
+ 'requests_per_minute' => 30,
+ 'login_per_hour' => 3,
+ 'api_per_minute' => 10
+ ],
+ 'geo_blocking' => [
+ 'enabled' => true,
+ 'blocked_countries' => []
+ ]
+ ]
+ ],
+ 'content' => [
+ 'name' => __('Content Protection', 'wpban'),
+ 'description' => __('Protect content from AI and scraping', 'wpban'),
+ 'settings' => [
+ 'blocked_crawlers' => array_keys(wpban_get_crawler_list()['ai']),
+ 'banned_agents' => ['*GPT*', '*Claude*', '*AI*Bot*'],
+ 'rate_limits' => [
+ 'requests_per_minute' => 30
+ ]
+ ]
+ ],
+ 'performance' => [
+ 'name' => __('Performance Mode', 'wpban'),
+ 'description' => __('Balanced security with minimal performance impact', 'wpban'),
+ 'settings' => [
+ 'enable_logging' => false,
+ 'rate_limits' => [
+ 'requests_per_minute' => 120,
+ 'login_per_hour' => 10
+ ]
+ ]
+ ]
+ ];
+ }
+}
\ No newline at end of file
diff --git a/includes/class-wpban-templates.php b/includes/class-wpban-templates.php
new file mode 100644
index 0000000..4c6b5e0
--- /dev/null
+++ b/includes/class-wpban-templates.php
@@ -0,0 +1,131 @@
+init_templates();
+ }
+
+ private function init_templates() {
+ $this->templates = [
+ 'basic' => [
+ 'name' => __('Basic Protection', 'wpban'),
+ 'description' => __('Essential security for most WordPress sites', 'wpban'),
+ 'settings' => [
+ 'banned_agents' => ['*bot*', '*crawler*', '*spider*'],
+ 'blocked_crawlers' => [],
+ 'enable_logging' => true,
+ 'enable_caching' => true,
+ 'ban_message' => '
Access Denied
Your access to this site has been restricted.
'
+ ]
+ ],
+ 'strict' => [
+ 'name' => __('Strict Security', 'wpban'),
+ 'description' => __('Maximum protection with login restrictions', 'wpban'),
+ 'settings' => [
+ 'banned_agents' => ['*bot*', '*crawler*', '*spider*', '*scraper*', 'curl*', 'wget*'],
+ 'blocked_crawlers' => ['GPTBot', 'ChatGPT-User', 'ClaudeBot', 'CCBot', 'PerplexityBot', 'Bytespider'],
+ 'browser_restrictions' => [
+ 'wechat_qq' => [
+ 'enabled' => true,
+ 'title' => __('Browser Not Supported', 'wpban'),
+ 'message' => __('Please use Chrome, Firefox, or Safari to access this site.', 'wpban'),
+ 'button_text' => __('Copy Link', 'wpban')
+ ]
+ ],
+ 'enable_logging' => true,
+ 'enable_caching' => true
+ ]
+ ],
+ 'content' => [
+ 'name' => __('Content Protection', 'wpban'),
+ 'description' => __('Protect original content from AI crawlers', 'wpban'),
+ 'settings' => [
+ 'blocked_crawlers' => [
+ 'GPTBot', 'ChatGPT-User', 'ClaudeBot', 'Claude-Web', 'anthropic-ai',
+ 'CCBot', 'PerplexityBot', 'Bytespider', 'FacebookBot', 'Meta-ExternalAgent',
+ 'cohere-ai', 'AI2Bot', 'Ai2Bot-Dolma', 'Google-Extended', 'ImagesiftBot'
+ ],
+ 'banned_agents' => ['*GPT*', '*Claude*', '*AI*Bot*'],
+ 'enable_logging' => true,
+ 'enable_caching' => true
+ ]
+ ],
+ 'membership' => [
+ 'name' => __('Membership Site', 'wpban'),
+ 'description' => __('Ideal for private or membership websites', 'wpban'),
+ 'settings' => [
+ 'banned_agents' => ['*bot*', '*crawler*', '*spider*', '*scraper*'],
+ 'blocked_crawlers' => array_merge(
+ wpban_get_crawler_list()['ai'],
+ wpban_get_crawler_list()['seo']
+ ),
+ 'banned_referers' => ['*google.*', '*bing.*', '*yahoo.*', '*baidu.*'],
+ 'enable_logging' => true,
+ 'enable_caching' => true
+ ]
+ ],
+ 'development' => [
+ 'name' => __('Development Mode', 'wpban'),
+ 'description' => __('Minimal restrictions for development sites', 'wpban'),
+ 'settings' => [
+ 'enable_logging' => true,
+ 'enable_caching' => false,
+ 'ban_message' => '
Site Under Development
This site is currently under development.
'
+ ]
+ ]
+ ];
+ }
+
+ public function get_all_templates() {
+ return $this->templates;
+ }
+
+ public function get_template($id) {
+ return isset($this->templates[$id]) ? $this->templates[$id] : null;
+ }
+
+ public function apply_template($template_id) {
+ $template = $this->get_template($template_id);
+ if (!$template) {
+ return false;
+ }
+
+ // Get current settings
+ $current_settings = get_option('wpban_settings', []);
+
+ // Merge template settings with defaults
+ $new_settings = array_merge(
+ [
+ 'banned_ips' => [],
+ 'banned_ranges' => [],
+ 'banned_hosts' => [],
+ 'banned_referers' => [],
+ 'banned_agents' => [],
+ 'whitelist_ips' => [],
+ 'login_allowed_ips' => [],
+ 'blocked_crawlers' => [],
+ 'browser_restrictions' => [],
+ 'ban_message' => '
Access Denied
Your access to this site has been restricted.
',
+ 'enable_logging' => true,
+ 'enable_caching' => true,
+ 'reverse_proxy' => false
+ ],
+ $template['settings']
+ );
+
+ // Preserve some user settings
+ $preserve_fields = ['whitelist_ips', 'reverse_proxy'];
+ foreach ($preserve_fields as $field) {
+ if (isset($current_settings[$field])) {
+ $new_settings[$field] = $current_settings[$field];
+ }
+ }
+
+ return $new_settings;
+ }
+}
\ No newline at end of file
diff --git a/includes/functions.php b/includes/functions.php
index eebd1ab..cf7dab8 100644
--- a/includes/functions.php
+++ b/includes/functions.php
@@ -1,44 +1,395 @@
0]);
- if ($options['reverse_proxy']) {
- if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
- $ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
- } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
- $ip = $_SERVER['HTTP_X_REAL_IP'];
- } elseif (!empty($_SERVER['HTTP_CLIENT_IP'])) {
- $ip = $_SERVER['HTTP_CLIENT_IP'];
+
+ if ($use_proxy) {
+ $proxy_headers = [
+ 'HTTP_CF_CONNECTING_IP', // Cloudflare
+ 'HTTP_X_FORWARDED_FOR', // Standard proxy
+ 'HTTP_X_REAL_IP', // Nginx proxy
+ 'HTTP_CLIENT_IP', // Some proxies
+ 'HTTP_X_FORWARDED', // Some proxies
+ 'HTTP_X_CLUSTER_CLIENT_IP', // Some proxies
+ 'HTTP_FORWARDED_FOR', // Some proxies
+ 'HTTP_FORWARDED' // RFC 7239
+ ];
+
+ foreach ($proxy_headers as $header) {
+ if (!empty($_SERVER[$header])) {
+ $ips = explode(',', $_SERVER[$header]);
+ $ip = trim($ips[0]);
+
+ // Validate IP
+ if (filter_var($ip, FILTER_VALIDATE_IP)) {
+ break;
+ }
+ }
}
}
- return sanitize_text_field($ip);
+
+ // Final validation
+ return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '0.0.0.0';
}
-function preg_match_wildcard($pattern, $subject) {
- $pattern = preg_quote($pattern, '#');
- $pattern = str_replace('\*', '.*', $pattern);
- return preg_match("#^$pattern$#i", $subject);
+/**
+ * Match pattern with wildcard support
+ */
+function wpban_match_pattern($pattern, $string) {
+ // Convert wildcard pattern to regex
+ $pattern = str_replace(
+ ['*', '?', '[', ']', '(', ')', '{', '}', '^', '$', '+', '.', '\\'],
+ ['.*', '.', '\[', '\]', '\(', '\)', '\{', '\}', '\^', '\$', '\+', '\.', '\\\\'],
+ $pattern
+ );
+
+ return preg_match('/^' . $pattern . '$/i', $string);
}
-function ban_anything_ip_in_range($ip, $ranges) {
- foreach ($ranges as $range) {
- if (strpos($range, '/') !== false) {
- list($subnet, $bits) = explode('/', $range);
- $ip_long = ip2long($ip);
- $subnet_long = ip2long($subnet);
- $mask = -1 << (32 - (int)$bits);
- if (($ip_long & $mask) === ($subnet_long & $mask)) {
- return true;
- }
- } elseif ($ip === $range) {
+/**
+ * Check if IP is in range (CIDR or range notation)
+ */
+function wpban_ip_in_range($ip, $range) {
+ if (strpos($range, '/') !== false) {
+ // CIDR notation
+ list($subnet, $bits) = explode('/', $range);
+ if ($bits < 0 || $bits > 32) {
+ return false;
+ }
+
+ $ip_long = ip2long($ip);
+ $subnet_long = ip2long($subnet);
+
+ if ($ip_long === false || $subnet_long === false) {
+ return false;
+ }
+
+ $mask = -1 << (32 - $bits);
+ return ($ip_long & $mask) == ($subnet_long & $mask);
+
+ } elseif (strpos($range, '-') !== false) {
+ // Range notation (192.168.1.1-192.168.1.255)
+ list($start, $end) = explode('-', $range);
+ $ip_long = ip2long($ip);
+ $start_long = ip2long(trim($start));
+ $end_long = ip2long(trim($end));
+
+ if ($ip_long === false || $start_long === false || $end_long === false) {
+ return false;
+ }
+
+ return ($ip_long >= $start_long && $ip_long <= $end_long);
+ }
+
+ // Check if it's a pattern
+ return wpban_match_pattern($range, $ip);
+}
+
+/**
+ * Get crawler list
+ */
+function wpban_get_crawler_list() {
+ return [
+ 'ai' => [
+ // OpenAI
+ 'GPTBot' => ['description' => __('OpenAI GPT Web Crawler', 'wpban')],
+ 'ChatGPT-User' => ['description' => __('ChatGPT Browser Tool', 'wpban')],
+ 'OAI-SearchBot' => ['description' => __('OpenAI Search Bot', 'wpban')],
+
+ // Anthropic
+ 'ClaudeBot' => ['description' => __('Anthropic Claude Bot', 'wpban')],
+ 'Claude-Web' => ['description' => __('Claude Web Browser', 'wpban')],
+ 'anthropic-ai' => ['description' => __('Anthropic AI Crawler', 'wpban')],
+
+ // Google
+ 'Google-Extended' => ['description' => __('Google Bard/Gemini Bot', 'wpban')],
+
+ // Microsoft
+ 'Bingbot' => ['description' => __('Microsoft Bing AI', 'wpban')],
+
+ // Meta
+ 'FacebookBot' => ['description' => __('Meta/Facebook Bot', 'wpban')],
+ 'Meta-ExternalAgent' => ['description' => __('Meta External Agent', 'wpban')],
+ 'Meta-ExternalFetcher' => ['description' => __('Meta External Fetcher', 'wpban')],
+
+ // Others
+ 'CCBot' => ['description' => __('Common Crawl Dataset', 'wpban')],
+ 'PerplexityBot' => ['description' => __('Perplexity AI Search', 'wpban')],
+ 'YouBot' => ['description' => __('You.com Search Bot', 'wpban')],
+ 'Bytespider' => ['description' => __('ByteDance Spider', 'wpban')],
+ 'cohere-ai' => ['description' => __('Cohere AI Bot', 'wpban')],
+ 'Diffbot' => ['description' => __('Diffbot Crawler', 'wpban')],
+ 'Amazonbot' => ['description' => __('Amazon Alexa Bot', 'wpban')],
+ 'Applebot-Extended' => ['description' => __('Apple AI Bot', 'wpban')],
+ 'AI2Bot' => ['description' => __('Allen Institute AI', 'wpban')],
+ 'Ai2Bot-Dolma' => ['description' => __('AI2 Dolma Dataset', 'wpban')],
+ 'Omgilibot' => ['description' => __('Webz.io Bot', 'wpban')],
+ 'webzio' => ['description' => __('Webz.io Crawler', 'wpban')],
+ 'ImagesiftBot' => ['description' => __('Image Analysis Bot', 'wpban')],
+ 'PetalBot' => ['description' => __('Huawei Petal Search', 'wpban')],
+ ],
+ 'seo' => [
+ // Major Search Engines
+ 'Googlebot' => ['description' => __('Google Search (DO NOT BLOCK)', 'wpban')],
+ 'Bingbot' => ['description' => __('Bing Search (DO NOT BLOCK)', 'wpban')],
+ 'Slurp' => ['description' => __('Yahoo Search', 'wpban')],
+ 'DuckDuckBot' => ['description' => __('DuckDuckGo Search', 'wpban')],
+ 'Baiduspider' => ['description' => __('Baidu Search (China)', 'wpban')],
+ 'YandexBot' => ['description' => __('Yandex Search (Russia)', 'wpban')],
+
+ // SEO Tools
+ 'AhrefsBot' => ['description' => __('Ahrefs SEO Tool', 'wpban')],
+ 'SemrushBot' => ['description' => __('Semrush SEO Tool', 'wpban')],
+ 'MJ12bot' => ['description' => __('Majestic SEO Tool', 'wpban')],
+ 'DotBot' => ['description' => __('Moz SEO Tool', 'wpban')],
+ 'Screaming Frog' => ['description' => __('SEO Spider Tool', 'wpban')],
+
+ // Other
+ 'ia_archiver' => ['description' => __('Internet Archive', 'wpban')],
+ 'facebookexternalhit' => ['description' => __('Facebook Link Preview', 'wpban')],
+ 'LinkedInBot' => ['description' => __('LinkedIn Preview', 'wpban')],
+ 'WhatsApp' => ['description' => __('WhatsApp Link Preview', 'wpban')],
+ 'Twitterbot' => ['description' => __('Twitter Link Preview', 'wpban')],
+ ]
+ ];
+}
+
+/**
+ * Get country list
+ */
+function wpban_get_country_list() {
+ return [
+ 'AF' => __('Afghanistan', 'wpban'),
+ 'AL' => __('Albania', 'wpban'),
+ 'DZ' => __('Algeria', 'wpban'),
+ 'AR' => __('Argentina', 'wpban'),
+ 'AM' => __('Armenia', 'wpban'),
+ 'AU' => __('Australia', 'wpban'),
+ 'AT' => __('Austria', 'wpban'),
+ 'AZ' => __('Azerbaijan', 'wpban'),
+ 'BD' => __('Bangladesh', 'wpban'),
+ 'BY' => __('Belarus', 'wpban'),
+ 'BE' => __('Belgium', 'wpban'),
+ 'BR' => __('Brazil', 'wpban'),
+ 'BG' => __('Bulgaria', 'wpban'),
+ 'CA' => __('Canada', 'wpban'),
+ 'CL' => __('Chile', 'wpban'),
+ 'CN' => __('China', 'wpban'),
+ 'CO' => __('Colombia', 'wpban'),
+ 'HR' => __('Croatia', 'wpban'),
+ 'CZ' => __('Czech Republic', 'wpban'),
+ 'DK' => __('Denmark', 'wpban'),
+ 'EG' => __('Egypt', 'wpban'),
+ 'EE' => __('Estonia', 'wpban'),
+ 'FI' => __('Finland', 'wpban'),
+ 'FR' => __('France', 'wpban'),
+ 'GE' => __('Georgia', 'wpban'),
+ 'DE' => __('Germany', 'wpban'),
+ 'GR' => __('Greece', 'wpban'),
+ 'HK' => __('Hong Kong', 'wpban'),
+ 'HU' => __('Hungary', 'wpban'),
+ 'IS' => __('Iceland', 'wpban'),
+ 'IN' => __('India', 'wpban'),
+ 'ID' => __('Indonesia', 'wpban'),
+ 'IR' => __('Iran', 'wpban'),
+ 'IQ' => __('Iraq', 'wpban'),
+ 'IE' => __('Ireland', 'wpban'),
+ 'IL' => __('Israel', 'wpban'),
+ 'IT' => __('Italy', 'wpban'),
+ 'JP' => __('Japan', 'wpban'),
+ 'KZ' => __('Kazakhstan', 'wpban'),
+ 'KE' => __('Kenya', 'wpban'),
+ 'KR' => __('South Korea', 'wpban'),
+ 'LV' => __('Latvia', 'wpban'),
+ 'LT' => __('Lithuania', 'wpban'),
+ 'MY' => __('Malaysia', 'wpban'),
+ 'MX' => __('Mexico', 'wpban'),
+ 'MA' => __('Morocco', 'wpban'),
+ 'NL' => __('Netherlands', 'wpban'),
+ 'NZ' => __('New Zealand', 'wpban'),
+ 'NG' => __('Nigeria', 'wpban'),
+ 'NO' => __('Norway', 'wpban'),
+ 'PK' => __('Pakistan', 'wpban'),
+ 'PE' => __('Peru', 'wpban'),
+ 'PH' => __('Philippines', 'wpban'),
+ 'PL' => __('Poland', 'wpban'),
+ 'PT' => __('Portugal', 'wpban'),
+ 'RO' => __('Romania', 'wpban'),
+ 'RU' => __('Russia', 'wpban'),
+ 'SA' => __('Saudi Arabia', 'wpban'),
+ 'RS' => __('Serbia', 'wpban'),
+ 'SG' => __('Singapore', 'wpban'),
+ 'SK' => __('Slovakia', 'wpban'),
+ 'SI' => __('Slovenia', 'wpban'),
+ 'ZA' => __('South Africa', 'wpban'),
+ 'ES' => __('Spain', 'wpban'),
+ 'SE' => __('Sweden', 'wpban'),
+ 'CH' => __('Switzerland', 'wpban'),
+ 'TW' => __('Taiwan', 'wpban'),
+ 'TH' => __('Thailand', 'wpban'),
+ 'TR' => __('Turkey', 'wpban'),
+ 'UA' => __('Ukraine', 'wpban'),
+ 'AE' => __('United Arab Emirates', 'wpban'),
+ 'GB' => __('United Kingdom', 'wpban'),
+ 'US' => __('United States', 'wpban'),
+ 'UZ' => __('Uzbekistan', 'wpban'),
+ 'VE' => __('Venezuela', 'wpban'),
+ 'VN' => __('Vietnam', 'wpban'),
+ ];
+}
+
+/**
+ * Sanitize and validate IP address
+ */
+function wpban_sanitize_ip($ip) {
+ // Remove any whitespace
+ $ip = trim($ip);
+
+ // Check if it's a valid IP
+ if (filter_var($ip, FILTER_VALIDATE_IP)) {
+ return $ip;
+ }
+
+ // Check if it's a pattern with wildcards
+ if (strpos($ip, '*') !== false) {
+ // Replace wildcards with 0 for validation
+ $test_ip = str_replace('*', '0', $ip);
+ if (filter_var($test_ip, FILTER_VALIDATE_IP)) {
+ return $ip;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Format bytes to human readable
+ */
+function wpban_format_bytes($bytes, $precision = 2) {
+ $units = ['B', 'KB', 'MB', 'GB', 'TB'];
+
+ $bytes = max($bytes, 0);
+ $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
+ $pow = min($pow, count($units) - 1);
+
+ $bytes /= (1 << (10 * $pow));
+
+ return round($bytes, $precision) . ' ' . $units[$pow];
+}
+
+/**
+ * Get user's country flag emoji
+ */
+function wpban_country_flag($country_code) {
+ if (strlen($country_code) !== 2) {
+ return '';
+ }
+
+ $country_code = strtoupper($country_code);
+ $flag = '';
+
+ // Convert country code to flag emoji
+ for ($i = 0; $i < strlen($country_code); $i++) {
+ $flag .= mb_chr(ord($country_code[$i]) - ord('A') + 0x1F1E6, 'UTF-8');
+ }
+
+ return $flag;
+}
+
+/**
+ * Log security event (helper function)
+ */
+function wpban_log($action, $reason, $details = '') {
+ if (isset($GLOBALS['wpban_security'])) {
+ $GLOBALS['wpban_security']->log($action, $reason, $details);
+ }
+}
+
+/**
+ * Check if current user is whitelisted
+ */
+function wpban_is_current_user_whitelisted() {
+ $settings = get_option('wpban_settings', []);
+ $ip = wpban_get_ip($settings['reverse_proxy'] ?? false);
+
+ foreach ($settings['whitelist_ips'] ?? [] as $pattern) {
+ if (wpban_match_pattern($pattern, $ip) || wpban_ip_in_range($ip, $pattern)) {
return true;
}
}
+
return false;
}
+
+/**
+ * Get attack type label
+ */
+function wpban_get_attack_type_label($type) {
+ $labels = [
+ 'ip_ban' => __('IP Ban', 'wpban'),
+ 'rate_limit' => __('Rate Limit', 'wpban'),
+ 'geo_block' => __('Geo Block', 'wpban'),
+ 'crawler_block' => __('Crawler Block', 'wpban'),
+ 'failed_login' => __('Failed Login', 'wpban'),
+ 'brute_force' => __('Brute Force', 'wpban'),
+ 'banned' => __('Banned', 'wpban'),
+ 'blocked' => __('Blocked', 'wpban'),
+ ];
+
+ return $labels[$type] ?? $type;
+}
+
+/**
+ * Export settings
+ */
+function wpban_export_settings() {
+ $settings = get_option('wpban_settings', []);
+ $bypass_path = get_option('wpban_bypass_path', '');
+
+ $export = [
+ 'version' => WPBAN_VERSION,
+ 'timestamp' => current_time('timestamp'),
+ 'site_url' => get_site_url(),
+ 'settings' => $settings,
+ 'bypass_path' => $bypass_path
+ ];
+
+ return json_encode($export, JSON_PRETTY_PRINT);
+}
+
+/**
+ * Import settings
+ */
+function wpban_import_settings($json) {
+ $data = json_decode($json, true);
+
+ if (!$data || !isset($data['settings'])) {
+ return false;
+ }
+
+ // Validate version compatibility
+ if (version_compare($data['version'], '4.0', '<')) {
+ return false;
+ }
+
+ // Import settings
+ update_option('wpban_settings', $data['settings']);
+
+ // Optionally import bypass path
+ if (!empty($data['bypass_path'])) {
+ update_option('wpban_bypass_path', $data['bypass_path']);
+ }
+
+ // Clear cache
+ if (isset($GLOBALS['wpban_security'])) {
+ $GLOBALS['wpban_security']->clear_cache();
+ }
+
+ return true;
+}
\ No newline at end of file
diff --git a/languages/wpban.pot b/languages/wpban.pot
new file mode 100644
index 0000000..2fff37b
--- /dev/null
+++ b/languages/wpban.pot
@@ -0,0 +1,638 @@
+# wpban Pro Language Template
+# Copyright (C) 2024 WPBan
+# This file is distributed under the GPLv2 or later.
+msgid ""
+msgstr ""
+"Project-Id-Version: wpban Pro 5.0\n"
+"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wpban\n"
+"Last-Translator: FULL NAME
\n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"POT-Creation-Date: 2024-01-01T00:00:00+00:00\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"X-Generator: WP-CLI 2.9.0\n"
+"X-Domain: wpban\n"
+
+#. Plugin Name of the plugin
+msgid "wpban Pro"
+msgstr ""
+
+#. Plugin URI of the plugin
+msgid "https://wpban.com/"
+msgstr ""
+
+#. Description of the plugin
+msgid "Advanced WordPress security with geo-blocking, rate limiting, and intelligent threat detection."
+msgstr ""
+
+#. Author of the plugin
+msgid "WPBan"
+msgstr ""
+
+#. Author URI of the plugin
+msgid "https://wpban.com"
+msgstr ""
+
+#: includes/class-wpban-admin.php:30
+msgid "WPBan Security"
+msgstr ""
+
+#: includes/class-wpban-admin.php:38
+msgid "Settings"
+msgstr ""
+
+#: includes/class-wpban-admin.php:46
+msgid "Security Logs"
+msgstr ""
+
+#: includes/class-wpban-admin.php:47
+msgid "Logs"
+msgstr ""
+
+#: includes/class-wpban-admin.php:54
+msgid "Tools"
+msgstr ""
+
+#: includes/class-wpban-admin.php:80
+msgid "Are you sure you want to clear all logs?"
+msgstr ""
+
+#: includes/class-wpban-admin.php:81
+msgid "This will replace your current settings. Continue?"
+msgstr ""
+
+#: includes/class-wpban-admin.php:82
+msgid "Test email sent!"
+msgstr ""
+
+#: includes/class-wpban-admin.php:83
+msgid "An error occurred"
+msgstr ""
+
+#: includes/class-wpban-admin.php:95
+msgid "WPBan Security Dashboard"
+msgstr ""
+
+#: includes/class-wpban-admin.php:101
+msgid "Total Blocks"
+msgstr ""
+
+#: includes/class-wpban-admin.php:115
+msgid "Unique IPs"
+msgstr ""
+
+#: includes/class-wpban-admin.php:120
+msgid "Today's Blocks"
+msgstr ""
+
+#: includes/class-wpban-admin.php:125
+msgid "Active Rules"
+msgstr ""
+
+#: includes/class-wpban-admin.php:134
+msgid "Quick Actions"
+msgstr ""
+
+#: includes/class-wpban-admin.php:136
+msgid "Emergency Bypass URL:"
+msgstr ""
+
+#: includes/class-wpban-admin.php:142
+msgid "Copy"
+msgstr ""
+
+#: includes/class-wpban-admin.php:145
+msgid "Save this URL to access your site if you get locked out."
+msgstr ""
+
+#: includes/class-wpban-admin.php:150
+msgid "Configure Settings"
+msgstr ""
+
+#: includes/class-wpban-admin.php:153
+msgid "View Logs"
+msgstr ""
+
+#: includes/class-wpban-admin.php:160
+msgid "Top Blocked Countries"
+msgstr ""
+
+#: includes/class-wpban-admin.php:187
+msgid "Security Templates"
+msgstr ""
+
+#: includes/class-wpban-admin.php:188
+msgid "Quickly apply pre-configured security settings."
+msgstr ""
+
+#: includes/class-wpban-admin.php:195
+msgid "Apply"
+msgstr ""
+
+#: includes/class-wpban-admin.php:204
+msgid "Recent Activity"
+msgstr ""
+
+#: includes/class-wpban-admin.php:209 includes/class-wpban-admin.php:468
+msgid "Time"
+msgstr ""
+
+#: includes/class-wpban-admin.php:210 includes/class-wpban-admin.php:469
+msgid "IP"
+msgstr ""
+
+#: includes/class-wpban-admin.php:211 includes/class-wpban-admin.php:470
+msgid "Country"
+msgstr ""
+
+#: includes/class-wpban-admin.php:212 includes/class-wpban-admin.php:471
+msgid "Action"
+msgstr ""
+
+#: includes/class-wpban-admin.php:213 includes/class-wpban-admin.php:472
+msgid "Details"
+msgstr ""
+
+#: includes/class-wpban-admin.php:236
+msgid "WPBan Security Settings"
+msgstr ""
+
+#: includes/class-wpban-admin.php:243
+msgid "General"
+msgstr ""
+
+#: includes/class-wpban-admin.php:244
+msgid "IP Rules"
+msgstr ""
+
+#: includes/class-wpban-admin.php:245
+msgid "Advanced"
+msgstr ""
+
+#: includes/class-wpban-admin.php:246
+msgid "Rate Limiting"
+msgstr ""
+
+#: includes/class-wpban-admin.php:247
+msgid "Geo Blocking"
+msgstr ""
+
+#: includes/class-wpban-admin.php:248
+msgid "Crawlers"
+msgstr ""
+
+#: includes/class-wpban-admin.php:249
+msgid "Notifications"
+msgstr ""
+
+#: includes/class-wpban-admin.php:256
+msgid "Enable Logging"
+msgstr ""
+
+#: includes/class-wpban-admin.php:261
+msgid "Log all security events"
+msgstr ""
+
+#: includes/class-wpban-admin.php:266
+msgid "Reverse Proxy"
+msgstr ""
+
+#: includes/class-wpban-admin.php:271
+msgid "Server is behind a reverse proxy (Cloudflare, nginx, etc.)"
+msgstr ""
+
+#: includes/class-wpban-admin.php:276
+msgid "Ban Message"
+msgstr ""
+
+#: includes/class-wpban-admin.php:287
+msgid "Variables: %IP%, %DATE%, %SITE%"
+msgstr ""
+
+#: includes/class-wpban-admin.php:295
+msgid "Banned IPs"
+msgstr ""
+
+#: includes/class-wpban-admin.php:300
+msgid "One per line. Use * for wildcards (e.g., 192.168.*.*)"
+msgstr ""
+
+#: includes/class-wpban-admin.php:304
+msgid "IP Ranges"
+msgstr ""
+
+#: includes/class-wpban-admin.php:309
+msgid "CIDR: 192.168.0.0/24 or Range: 192.168.0.1-192.168.0.255"
+msgstr ""
+
+#: includes/class-wpban-admin.php:313
+msgid "Whitelist IPs"
+msgstr ""
+
+#: includes/class-wpban-admin.php:318
+msgid "These IPs will never be blocked"
+msgstr ""
+
+#: includes/class-wpban-admin.php:322
+msgid "Login Protection"
+msgstr ""
+
+#: includes/class-wpban-admin.php:327
+msgid "Only these IPs can access wp-login.php (leave empty to allow all)"
+msgstr ""
+
+#: includes/class-wpban-admin.php:336
+msgid "Banned Hosts"
+msgstr ""
+
+#: includes/class-wpban-admin.php:341
+msgid "e.g., *.badhost.com"
+msgstr ""
+
+#: includes/class-wpban-admin.php:345
+msgid "Banned Referers"
+msgstr ""
+
+#: includes/class-wpban-admin.php:352
+msgid "Banned User Agents"
+msgstr ""
+
+#: includes/class-wpban-admin.php:359
+msgid "Browser Restrictions"
+msgstr ""
+
+#: includes/class-wpban-admin.php:365
+msgid "Block WeChat/QQ Browsers"
+msgstr ""
+
+#: includes/class-wpban-admin.php:375
+msgid "Rate Limiting Settings"
+msgstr ""
+
+#: includes/class-wpban-admin.php:376
+msgid "Protect against floods and brute force attacks by limiting request rates."
+msgstr ""
+
+#: includes/class-wpban-admin.php:380
+msgid "General Requests"
+msgstr ""
+
+#: includes/class-wpban-admin.php:385
+msgid "requests per minute"
+msgstr ""
+
+#: includes/class-wpban-admin.php:389
+msgid "Login Attempts"
+msgstr ""
+
+#: includes/class-wpban-admin.php:394
+msgid "attempts per hour"
+msgstr ""
+
+#: includes/class-wpban-admin.php:398
+msgid "API Requests"
+msgstr ""
+
+#: includes/class-wpban-admin.php:410
+msgid "Geographic Blocking"
+msgstr ""
+
+#: includes/class-wpban-admin.php:414
+msgid "Enable Geo Blocking"
+msgstr ""
+
+#: includes/class-wpban-admin.php:419
+msgid "Block or allow access based on country"
+msgstr ""
+
+#: includes/class-wpban-admin.php:424
+msgid "Block Mode"
+msgstr ""
+
+#: includes/class-wpban-admin.php:429
+msgid "Block selected countries"
+msgstr ""
+
+#: includes/class-wpban-admin.php:434
+msgid "Allow only selected countries"
+msgstr ""
+
+#: includes/class-wpban-admin.php:439
+msgid "Countries"
+msgstr ""
+
+#: includes/class-wpban-admin.php:454
+msgid "Hold Ctrl/Cmd to select multiple countries"
+msgstr ""
+
+#: includes/class-wpban-admin.php:463
+msgid "Select All AI"
+msgstr ""
+
+#: includes/class-wpban-admin.php:466
+msgid "Select All SEO"
+msgstr ""
+
+#: includes/class-wpban-admin.php:469
+msgid "Deselect All"
+msgstr ""
+
+#: includes/class-wpban-admin.php:479
+msgid "AI Crawlers"
+msgstr ""
+
+#: includes/class-wpban-admin.php:494
+msgid "SEO Crawlers"
+msgstr ""
+
+#: includes/class-wpban-admin.php:496
+msgid "⚠️ Blocking SEO crawlers may affect your search engine rankings!"
+msgstr ""
+
+#: includes/class-wpban-admin.php:516
+msgid "Enable Email Notifications"
+msgstr ""
+
+#: includes/class-wpban-admin.php:521
+msgid "Send email alerts for security events"
+msgstr ""
+
+#: includes/class-wpban-admin.php:526
+msgid "Recipient Email"
+msgstr ""
+
+#: includes/class-wpban-admin.php:532
+msgid "Send Test Email"
+msgstr ""
+
+#: includes/class-wpban-admin.php:537
+msgid "Alert Threshold"
+msgstr ""
+
+#: includes/class-wpban-admin.php:542
+msgid "Send alert after this many blocks from same IP"
+msgstr ""
+
+#: includes/class-wpban-admin.php:546
+msgid "Alert Events"
+msgstr ""
+
+#: includes/class-wpban-admin.php:549
+msgid "Rate limit exceeded"
+msgstr ""
+
+#: includes/class-wpban-admin.php:550
+msgid "Geographic block"
+msgstr ""
+
+#: includes/class-wpban-admin.php:551
+msgid "Brute force attempt"
+msgstr ""
+
+#: includes/class-wpban-admin.php:552
+msgid "Crawler blocked"
+msgstr ""
+
+#: includes/class-wpban-admin.php:571
+msgid "Save Settings"
+msgstr ""
+
+#: includes/class-wpban-admin.php:597
+msgid "From date"
+msgstr ""
+
+#: includes/class-wpban-admin.php:600
+msgid "To date"
+msgstr ""
+
+#: includes/class-wpban-admin.php:603
+msgid "All Actions"
+msgstr ""
+
+#: includes/class-wpban-admin.php:604
+msgid "Banned"
+msgstr ""
+
+#: includes/class-wpban-admin.php:605
+msgid "Blocked"
+msgstr ""
+
+#: includes/class-wpban-admin.php:606
+msgid "Failed Login"
+msgstr ""
+
+#: includes/class-wpban-admin.php:607
+msgid "Bypass Used"
+msgstr ""
+
+#: includes/class-wpban-admin.php:611
+msgid "IP Address"
+msgstr ""
+
+#: includes/class-wpban-admin.php:614
+msgid "Country Code"
+msgstr ""
+
+#: includes/class-wpban-admin.php:616
+msgid "Filter"
+msgstr ""
+
+#: includes/class-wpban-admin.php:617
+msgid "Reset"
+msgstr ""
+
+#: includes/class-wpban-admin.php:620
+msgid "Export CSV"
+msgstr ""
+
+#: includes/class-wpban-admin.php:624
+msgid "Clear Logs"
+msgstr ""
+
+#: includes/class-wpban-admin.php:638
+msgid "User Agent"
+msgstr ""
+
+#: includes/class-wpban-admin.php:639
+msgid "URI"
+msgstr ""
+
+#: includes/class-wpban-admin.php:645
+msgid "No logs found."
+msgstr ""
+
+#: includes/class-wpban-admin.php:688
+msgid "WPBan Tools"
+msgstr ""
+
+#: includes/class-wpban-admin.php:694
+msgid "Import/Export Settings"
+msgstr ""
+
+#: includes/class-wpban-admin.php:695
+msgid "Backup your settings or migrate to another site."
+msgstr ""
+
+#: includes/class-wpban-admin.php:697
+msgid "Export"
+msgstr ""
+
+#: includes/class-wpban-admin.php:700
+msgid "Download Settings"
+msgstr ""
+
+#: includes/class-wpban-admin.php:704
+msgid "Import"
+msgstr ""
+
+#: includes/class-wpban-admin.php:714
+msgid "Import Settings"
+msgstr ""
+
+#: includes/class-wpban-admin.php:722
+msgid "Database Maintenance"
+msgstr ""
+
+#: includes/class-wpban-admin.php:723
+msgid "Optimize your WPBan database tables."
+msgstr ""
+
+#: includes/class-wpban-admin.php:736
+msgid "Total Log Entries"
+msgstr ""
+
+#: includes/class-wpban-admin.php:740
+msgid "Database Size"
+msgstr ""
+
+#: includes/class-wpban-admin.php:747
+msgid "Optimize Tables"
+msgstr ""
+
+#: includes/class-wpban-admin.php:751
+msgid "Clear Logs Older Than 30 Days"
+msgstr ""
+
+#: includes/class-wpban-admin.php:758
+msgid "System Information"
+msgstr ""
+
+#: includes/class-wpban-admin.php:829
+msgid "Settings saved successfully!"
+msgstr ""
+
+#: includes/class-wpban-admin.php:844
+msgid "Invalid template"
+msgstr ""
+
+#: includes/class-wpban-admin.php:853
+msgid "Template applied successfully!"
+msgstr ""
+
+#: includes/class-wpban-admin.php:928
+msgid "Logs cleared successfully!"
+msgstr ""
+
+#: includes/class-wpban-admin.php:949
+msgid "Test email sent successfully!"
+msgstr ""
+
+#: includes/class-wpban-admin.php:951
+msgid "Failed to send test email. Please check your email settings."
+msgstr ""
+
+#: includes/class-wpban-security.php:109
+msgid "Too many requests. Please try again later."
+msgstr ""
+
+#: includes/class-wpban-security.php:144
+#: includes/class-wpban-security.php:152
+msgid "Access denied from your location."
+msgstr ""
+
+#: includes/class-wpban-security.php:415
+msgid "Basic Protection"
+msgstr ""
+
+#: includes/class-wpban-security.php:416
+msgid "Essential security for most WordPress sites"
+msgstr ""
+
+#: includes/class-wpban-security.php:427
+msgid "Strict Security"
+msgstr ""
+
+#: includes/class-wpban-security.php:428
+msgid "Maximum protection with geo-blocking and rate limiting"
+msgstr ""
+
+#: includes/class-wpban-security.php:443
+msgid "Content Protection"
+msgstr ""
+
+#: includes/class-wpban-security.php:444
+msgid "Protect content from AI and scraping"
+msgstr ""
+
+#: includes/class-wpban-security.php:454
+msgid "Performance Mode"
+msgstr ""
+
+#: includes/class-wpban-security.php:455
+msgid "Balanced security with minimal performance impact"
+msgstr ""
+
+#: includes/functions.php:149
+msgid "OpenAI GPT Web Crawler"
+msgstr ""
+
+#: includes/functions.php:150
+msgid "ChatGPT Browser Tool"
+msgstr ""
+
+#: includes/functions.php:230
+msgid "Google Search (DO NOT BLOCK)"
+msgstr ""
+
+#: includes/functions.php:231
+msgid "Bing Search (DO NOT BLOCK)"
+msgstr ""
+
+#. Add all country names
+#: includes/functions.php:264
+msgid "Afghanistan"
+msgstr ""
+
+#: includes/functions.php:265
+msgid "Albania"
+msgstr ""
+
+#: includes/functions.php:327
+msgid "United States"
+msgstr ""
+
+#: includes/functions.php:416
+msgid "IP Ban"
+msgstr ""
+
+#: includes/functions.php:417
+msgid "Rate Limit"
+msgstr ""
+
+#: includes/functions.php:418
+msgid "Geo Block"
+msgstr ""
+
+#: includes/functions.php:419
+msgid "Crawler Block"
+msgstr ""
+
+#: includes/functions.php:420
+msgid "Failed Login"
+msgstr ""
+
+#: includes/functions.php:421
+msgid "Brute Force"
+msgstr ""
\ No newline at end of file
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..e833a9a
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1,236 @@
+=== wpban Pro ===
+Contributors: wpban
+Tags: security, firewall, ban, geo-blocking, rate-limiting, crawler-blocking, brute-force, ip-blocking
+Requires at least: 6.7.2
+Tested up to: 6.7.2
+Stable tag: 5.0
+Requires PHP: 7.4
+License: GPLv2 or later
+License URI: https://www.gnu.org/licenses/gpl-2.0.html
+
+Advanced WordPress security plugin with geo-blocking, rate limiting, AI crawler blocking, and intelligent threat detection.
+
+== Description ==
+
+wpban Pro is a comprehensive security solution for WordPress that protects your site from various threats including malicious bots, brute force attacks, content scrapers, and unauthorized access attempts.
+
+= Key Features =
+
+**🛡️ IP Management**
+* Ban IPs with wildcard support (e.g., 192.168.*.*)
+* IP range blocking (CIDR and range notation)
+* Whitelist trusted IPs
+* Automatic reverse proxy detection
+
+**🌍 Geographic Blocking**
+* Block or allow specific countries
+* Real-time IP geolocation
+* Cached country lookups for performance
+* Whitelist/blacklist modes
+
+**⚡ Rate Limiting**
+* Protect against DDoS and flood attacks
+* Separate limits for general requests, login attempts, and API calls
+* Automatic temporary bans for violators
+* Customizable thresholds
+
+**🤖 Crawler Control**
+* Block 40+ AI crawlers (GPTBot, ClaudeBot, etc.)
+* Control SEO crawler access
+* Protect content from AI training datasets
+* robots.txt integration
+
+**📊 Advanced Logging**
+* Detailed security event logs
+* Filter by date, action, IP, or country
+* Export logs to CSV
+* Automatic log rotation
+
+**📧 Email Notifications**
+* Real-time security alerts
+* Customizable alert thresholds
+* Multiple event types
+* Test email functionality
+
+**🚪 Login Protection**
+* Restrict wp-login.php access by IP
+* Brute force detection
+* Failed login tracking
+* Emergency bypass URL
+
+**🎯 Security Templates**
+* Quick setup with pre-configured templates
+* Basic, Strict, Content Protection, and Performance modes
+* One-click application
+* Customizable settings
+
+**🔧 Additional Features**
+* Browser restrictions (block WeChat/QQ)
+* User agent filtering
+* Referer blocking
+* Host-based banning
+* Import/export settings
+* Database optimization tools
+
+= Performance Optimized =
+
+* Smart caching system
+* Optimized database queries with indexes
+* Minimal performance impact
+* Lazy loading of features
+
+= Emergency Access =
+
+Never get locked out! WPBan provides an emergency bypass URL that allows you to access your site even if your IP gets banned accidentally.
+
+== Installation ==
+
+1. Upload the `wpban` folder to `/wp-content/plugins/`
+2. Activate the plugin through the 'Plugins' menu in WordPress
+3. Go to 'WPBan Security' in your admin menu
+4. Choose a security template or configure settings manually
+5. Save your emergency bypass URL in a safe place
+
+= Minimum Requirements =
+
+* WordPress 6.7.2 or higher
+* PHP 7.4 or higher
+* MySQL 5.7 or higher
+
+== Frequently Asked Questions ==
+
+= Will this plugin slow down my website? =
+
+No. WPBan is designed with performance in mind. It uses intelligent caching, optimized database queries, and only loads features when needed.
+
+= What happens if I accidentally ban myself? =
+
+Use the emergency bypass URL provided in the dashboard. This special URL allows you to access your site and disable the ban. Always save this URL in a secure location.
+
+= Can I block entire countries? =
+
+Yes! WPBan includes geographic blocking that allows you to block or exclusively allow specific countries. The plugin uses free IP geolocation services for this feature.
+
+= Will blocking SEO crawlers hurt my rankings? =
+
+Yes, blocking major search engine crawlers (Googlebot, Bingbot) will negatively impact your SEO. The plugin shows warnings for these critical crawlers. Only block SEO crawlers if you have a specific reason.
+
+= How do I protect against AI content scraping? =
+
+Use the "Content Protection" template which blocks major AI crawlers, or manually select AI crawlers to block in the Crawlers settings. The plugin blocks access and adds robots.txt rules.
+
+= Can I import/export settings? =
+
+Yes! Go to Tools > Import/Export to backup your settings or migrate them to another site.
+
+= How long are logs kept? =
+
+Logs are automatically cleaned after 30 days to prevent database bloat. You can export logs before they're deleted or manually clear them at any time.
+
+= Does it work with Cloudflare? =
+
+Yes! Enable the "Reverse Proxy" option in General settings to properly detect visitor IPs when using Cloudflare or other proxy services.
+
+== Screenshots ==
+
+1. Dashboard - Overview of security statistics and quick actions
+2. Security Templates - One-click security configurations
+3. IP Rules - Manage banned IPs, ranges, and whitelists
+4. Rate Limiting - Configure request limits
+5. Geographic Blocking - Block or allow countries
+6. Crawler Management - Control bot access
+7. Security Logs - Detailed event tracking
+8. Email Notifications - Real-time alerts
+
+== Changelog ==
+
+= 5.0 =
+* Added geographic blocking with real-time IP geolocation
+* Implemented advanced rate limiting system
+* Added email notifications for security events
+* Improved logging with country tracking and pagination
+* Added import/export functionality
+* Optimized database performance with indexes
+* Added emergency bypass URL feature
+* Improved UI with WordPress native design
+* Added security templates for quick setup
+* Fixed array structure issue in templates
+
+= 4.0 =
+* Complete rewrite with improved architecture
+* Added caching system
+* Enhanced performance
+* Better code organization
+
+= 3.3 =
+* Initial public release
+* Basic IP blocking functionality
+* Crawler blocking
+* Simple logging
+
+== Upgrade Notice ==
+
+= 5.0 =
+Major update with geographic blocking, rate limiting, and email notifications. Backup your settings before upgrading.
+
+== Advanced Usage ==
+
+= Custom Templates =
+
+You can create custom security templates by hooking into the `wpban_templates` filter:
+
+`
+add_filter('wpban_templates', function($templates) {
+ $templates['custom'] = [
+ 'name' => 'My Custom Template',
+ 'description' => 'Custom security configuration',
+ 'settings' => [
+ 'banned_ips' => ['1.2.3.4'],
+ 'rate_limits' => [
+ 'requests_per_minute' => 45
+ ]
+ ]
+ ];
+ return $templates;
+});
+`
+
+= Custom Country Detection =
+
+Integrate with premium GeoIP services:
+
+`
+add_filter('wpban_ip_country', function($country, $ip) {
+ // Your custom country detection logic
+ return $detected_country;
+}, 10, 2);
+`
+
+= Whitelist Specific Pages =
+
+Exclude certain pages from security checks:
+
+`
+add_filter('wpban_skip_checks', function($skip) {
+ if (is_page('special-page')) {
+ return true;
+ }
+ return $skip;
+});
+`
+
+== Support ==
+
+For support, feature requests, or bug reports, please visit our [support forum](https://wordpress.org/support/plugin/wpban/) or [GitHub repository](https://github.com/wpban/wpban).
+
+== Privacy Policy ==
+
+This plugin stores:
+* Security logs containing IP addresses, user agents, and geographic data
+* Your security configuration settings
+
+This plugin may connect to external services:
+* IP geolocation APIs (ip-api.com, ipinfo.io) for country detection
+* These services only receive IP addresses for lookup
+
+All data is stored locally in your WordPress database and is not shared with third parties.
\ No newline at end of file
diff --git a/wpban-anything.php b/wpban-anything.php
deleted file mode 100644
index 488670b..0000000
--- a/wpban-anything.php
+++ /dev/null
@@ -1,51 +0,0 @@
- 'ids']) as $blog_id) {
- switch_to_blog($blog_id);
- array_walk($options, 'delete_option');
- restore_current_blog();
- }
- } else {
- array_walk($options, 'delete_option');
- }
-}
diff --git a/wpban.php b/wpban.php
new file mode 100644
index 0000000..7be2009
--- /dev/null
+++ b/wpban.php
@@ -0,0 +1,183 @@
+get_charset_collate();
+
+ // Create logs table with improved indexes
+ $table_logs = $wpdb->prefix . 'wpban_logs';
+ $sql_logs = "CREATE TABLE $table_logs (
+ id bigint(20) NOT NULL AUTO_INCREMENT,
+ ip varchar(45) NOT NULL,
+ country_code varchar(2) DEFAULT NULL,
+ user_agent text,
+ referer text,
+ uri text,
+ action varchar(50) NOT NULL,
+ reason text,
+ timestamp datetime DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (id),
+ KEY ip_timestamp (ip, timestamp),
+ KEY action_timestamp (action, timestamp),
+ KEY country_timestamp (country_code, timestamp)
+ ) $charset_collate;";
+
+ // Create rate limit table
+ $table_rate = $wpdb->prefix . 'wpban_rate_limits';
+ $sql_rate = "CREATE TABLE $table_rate (
+ ip varchar(45) NOT NULL,
+ action varchar(50) NOT NULL,
+ count int(11) DEFAULT 1,
+ window_start datetime NOT NULL,
+ last_attempt datetime DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (ip, action),
+ KEY window_start (window_start)
+ ) $charset_collate;";
+
+ // Create geo cache table
+ $table_geo = $wpdb->prefix . 'wpban_geo_cache';
+ $sql_geo = "CREATE TABLE $table_geo (
+ ip varchar(45) NOT NULL,
+ country_code varchar(2) NOT NULL,
+ country_name varchar(100),
+ city varchar(100),
+ cached_at datetime DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (ip),
+ KEY cached_at (cached_at)
+ ) $charset_collate;";
+
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
+ dbDelta($sql_logs);
+ dbDelta($sql_rate);
+ dbDelta($sql_geo);
+
+ // Set default options
+ $default_settings = [
+ 'rate_limits' => [
+ 'requests_per_minute' => 60,
+ 'login_per_hour' => 5,
+ 'api_per_minute' => 30
+ ],
+ 'geo_blocking' => [
+ 'enabled' => false,
+ 'blocked_countries' => [],
+ 'allowed_countries' => []
+ ],
+ 'email_notifications' => [
+ 'enabled' => false,
+ 'recipient' => get_option('admin_email'),
+ 'threshold' => 10,
+ 'events' => ['rate_limit', 'geo_block', 'brute_force']
+ ]
+ ];
+
+ $existing = get_option('wpban_settings', []);
+ update_option('wpban_settings', array_merge($default_settings, $existing));
+
+ // Generate bypass URL if not exists
+ if (!get_option('wpban_bypass_path')) {
+ update_option('wpban_bypass_path', wp_generate_password(16, false));
+ }
+
+ // Schedule cron jobs
+ if (!wp_next_scheduled('wpban_cleanup')) {
+ wp_schedule_event(time(), 'daily', 'wpban_cleanup');
+ }
+
+ if (!wp_next_scheduled('wpban_geo_update')) {
+ wp_schedule_event(time(), 'weekly', 'wpban_geo_update');
+ }
+}
+
+// Load required files
+require_once WPBAN_DIR . 'includes/functions.php';
+require_once WPBAN_DIR . 'includes/class-wpban-security.php';
+require_once WPBAN_DIR . 'includes/class-wpban-admin.php';
+
+// Initialize plugin
+add_action('plugins_loaded', 'wpban_init');
+function wpban_init() {
+ load_plugin_textdomain('wpban', false, dirname(plugin_basename(__FILE__)) . '/languages/');
+
+ // Initialize security module
+ $GLOBALS['wpban_security'] = new WPBan_Security();
+
+ if (is_admin()) {
+ new WPBan_Admin();
+ }
+}
+
+// Cron jobs
+add_action('wpban_cleanup', 'wpban_cleanup_old_data');
+function wpban_cleanup_old_data() {
+ global $wpdb;
+
+ // Clean old logs (keep 30 days)
+ $wpdb->query($wpdb->prepare(
+ "DELETE FROM {$wpdb->prefix}wpban_logs WHERE timestamp < %s",
+ date('Y-m-d H:i:s', strtotime('-30 days'))
+ ));
+
+ // Clean old rate limit records
+ $wpdb->query($wpdb->prepare(
+ "DELETE FROM {$wpdb->prefix}wpban_rate_limits WHERE window_start < %s",
+ date('Y-m-d H:i:s', strtotime('-24 hours'))
+ ));
+
+ // Clean old geo cache (keep 7 days)
+ $wpdb->query($wpdb->prepare(
+ "DELETE FROM {$wpdb->prefix}wpban_geo_cache WHERE cached_at < %s",
+ date('Y-m-d H:i:s', strtotime('-7 days'))
+ ));
+}
+
+// Cleanup on uninstall
+register_uninstall_hook(__FILE__, 'wpban_uninstall');
+function wpban_uninstall() {
+ global $wpdb;
+
+ // Remove all options
+ $options = [
+ 'wpban_settings', 'wpban_templates', 'wpban_bypass_path',
+ 'wpban_cache_data', 'wpban_stats', 'wpban_geo_db_version'
+ ];
+
+ foreach ($options as $option) {
+ delete_option($option);
+ }
+
+ // Remove database tables
+ $tables = ['wpban_logs', 'wpban_rate_limits', 'wpban_geo_cache'];
+ foreach ($tables as $table) {
+ $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}{$table}");
+ }
+
+ // Clear scheduled events
+ wp_clear_scheduled_hook('wpban_cleanup');
+ wp_clear_scheduled_hook('wpban_geo_update');
+
+ // Clear cache
+ wp_cache_delete_group('wpban');
+}