<?php

namespace WPSecurityNinja\Plugin;

if (!function_exists('add_action')) {
	die('Please don\'t open this file directly!');
}

if (defined('ABSPATH') && !class_exists(__NAMESPACE__ . '\SecNin_Rename_WP_Login')) {

	class SecNin_Rename_WP_Login
	{
		const DEFAULT_SLUG = 'my-login';
		private static $wp_login_php;

		public static $default_login_url = 'my-login';

		private static function basename()
		{
			// Set in security-ninja.php
			return WF_SN_PLUGIN_BASENAME;
		}

		private static function path()
		{
			return trailingslashit(dirname(__FILE__));
		}

		/**
		 * use_trailing_slashes.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Friday, October 22nd, 2021.
		 * @access	private static
		 * @return	string
		 */
		private static function use_trailing_slashes()
		{
			return '/' === substr(get_option('permalink_structure'), -1, 1);
		}

		/**
		 * user_trailingslashit.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Friday, October 22nd, 2021.
		 * @access	private static
		 * @param	mixed	$string	
		 * @return	mixed
		 */
		private static function user_trailingslashit($string)
		{
			return self::use_trailing_slashes() ? trailingslashit($string) : untrailingslashit($string);
		}

		/**
		 * forbidden_slugs.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Sunday, October 24th, 2021.
		 * @access	public static
		 * @return	mixed
		 */
		public static function forbidden_slugs()
		{
			$wp = new \WP;
			return array_merge($wp->public_query_vars, $wp->private_query_vars, array('wp-admin', 'wp-login'));
		}
		/**
		 * wp_template_loader.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Friday, October 22nd, 2021.
		 * @version	v1.0.1	Monday, August 4th, 2025.
		 * @access	private static
		 * @return	void
		 */
		private static function wp_template_loader()
		{
			// Show proper error message instead of loading theme template
			// This provides better security by not revealing the custom login URL
			wp_die(
				__('Access Denied: Unauthorized access to the admin area is prohibited.', 'security-ninja'),
				__('Access Denied', 'security-ninja'),
				array('response' => 403)
			);
		}

		/**
		 * new_login_slug.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Monday, October 18th, 2021.
		 * @access	private
		 * @return	void
		 */
		public static function new_login_slug()
		{
			$settings = wf_sn_cf::get_options();

			if ($settings['change_login_url'] && $settings['new_login_url']) {
				return $settings['new_login_url'];
			}
			return false;
		}

		/**
		 * new_login_url.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Monday, October 18th, 2021.
		 * @access	public
		 * @param	mixed	$scheme	Default: null
		 * @return	void
		 */
		public static function new_login_url($scheme = null)
		{
			if (get_option('permalink_structure')) {
				return self::user_trailingslashit(home_url('/', $scheme) . self::new_login_slug());
			} else {
				return home_url('/', $scheme) . '?' . self::new_login_slug();
			}
		}

		/**
		 * __construct.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Monday, October 18th, 2021.
		 * @access	public
		 * @return	void
		 */
		public function __construct()
		{

			add_action('admin_notices', array(__NAMESPACE__ . '\\SecNin_Rename_WP_Login', 'admin_notices'));
			add_action('network_admin_notices', array(__NAMESPACE__ . '\\SecNin_Rename_WP_Login', 'admin_notices'));
			if (is_multisite() && !function_exists('is_plugin_active_for_network')) {
				require_once ABSPATH . '/wp-admin/includes/plugin.php';
			}
			if (SecNin_Rename_WP_Login::is_active()) {
				add_action('init', array(__NAMESPACE__ . '\\SecNin_Rename_WP_Login', 'plugins_loaded'), 1);
				add_action('wp_loaded', array(__NAMESPACE__ . '\\SecNin_Rename_WP_Login', 'wp_loaded'));
				add_filter('site_url', array(__NAMESPACE__ . '\\SecNin_Rename_WP_Login', 'site_url'), 10, 4);
				add_filter('network_site_url', array(__NAMESPACE__ . '\\SecNin_Rename_WP_Login', 'network_site_url'), 10, 3);
				add_filter('wp_redirect', array(__NAMESPACE__ . '\\SecNin_Rename_WP_Login', 'wp_redirect'), 10, 2);
				add_filter('site_option_welcome_email', array(__NAMESPACE__ . '\\SecNin_Rename_WP_Login', 'welcome_email'));
				remove_action('template_redirect', 'wp_redirect_admin_locations', 1000);
				
				// Early gate for wp-admin to prevent core redirect to wp-login.php
				add_action('init', array(__NAMESPACE__ . '\\SecNin_Rename_WP_Login', 'admin_gate_init'), 0);
				
				// Add wp-admin access control to block unauthorized access
				// Use template_redirect hook to run after authentication is established
				add_action('template_redirect', array(__NAMESPACE__ . '\\SecNin_Rename_WP_Login', 'admin_access_control'), 1);
			}
		}

		/**
		 * Admin access control - blocks unauthorized wp-admin access
		 * 
		 * @author  Lars Koudal
		 * @since   v0.0.1
		 * @version v1.0.0  Monday, August 4th, 2025.
		 * @version v1.0.1  Monday, August 4th, 2025.
		 * @version v1.0.2  Monday, August 4th, 2025.
		 * @access  public static
		 * @return  void
		 */
		public static function admin_access_control()
		{
			// Check if we're accessing wp-admin and user is not authenticated
			// Also check if we're not doing AJAX, cron, or REST API requests
			if (is_admin() && !wp_doing_ajax() && !wp_doing_cron() && !defined('REST_REQUEST')) {
				
				// Use a more robust authentication check
				$current_user = wp_get_current_user();
				$is_authenticated = $current_user->exists() && $current_user->ID > 0;
				
				// Also check for valid auth cookies as a backup
				if (!$is_authenticated) {
					$auth_cookie = wp_parse_auth_cookie('', 'logged_in');
					if ($auth_cookie && !empty($auth_cookie['username'])) {
						$is_authenticated = true;
					}
				}
				
				// Only block if user is not authenticated
				if (!$is_authenticated) {
					
					$current_user_ip = \WPSecurityNinja\Plugin\Wf_sn_cf::get_user_ip();
					$ua_string = '';
					if (isset($_SERVER['HTTP_USER_AGENT'])) {
						$ua_string = sanitize_text_field($_SERVER['HTTP_USER_AGENT']);
					}
					
					$request_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
					
					// Check if visitor is blocked (country blocks, IP bans, etc.) before showing admin protection
					// This ensures redirect URL setting works for blocked visitors accessing wp-admin
					if (class_exists(__NAMESPACE__ . '\Wf_sn_cf')) {
						$cf_options = \WPSecurityNinja\Plugin\Wf_sn_cf::get_options();
						
						// Only check blocking if firewall is active
						if (isset($cf_options['active']) && 1 === (int) $cf_options['active']) {
							$reason = \WPSecurityNinja\Plugin\Wf_sn_cf::is_banned_ip($current_user_ip);
							
							if ($reason) {
								// Check if this is a country-based ban
								$is_country_ban = (strpos($reason, 'Country is blocked') !== false);
								
								// Special handling for country bans with login-only mode
								// If "Only block these countries from login functionality" is enabled:
								// - Country-blocked users can view the site
								// - But they should see admin protection message (NOT redirect) when accessing wp-admin
								if ($is_country_ban && isset($cf_options['countryblock_loginonly']) && $cf_options['countryblock_loginonly']) {
									// Country blocking is login-only mode - show admin protection message, don't redirect
									// This allows them to view the site but blocks wp-admin access
									// Fall through to show admin protection message below
								} else {
									// For country bans without login-only mode, or other ban types (IP bans, global blocks, etc.)
									// Block and redirect via kill_request() which respects redirect URL setting
									$should_block = false;
									
									if ($is_country_ban) {
										// Country ban without login-only mode - block from entire site
										$should_block = true;
									} else {
										// For other ban types (IP bans, global blocks, etc.), check if 'global' setting is enabled
										if (!isset($cf_options['global']) || !$cf_options['global']) {
											// If global is OFF, only block from login-related pages
											// /wp-admin/ is a login-related request, so block it
											$should_block = true;
										} else {
											// Global is ON, block all requests including /wp-admin/
											$should_block = true;
										}
									}
									
									if ($should_block) {
										\WPSecurityNinja\Plugin\Wf_sn_cf::update_blocked_count($current_user_ip);
										
										$data = [
											'user_agent' => $ua_string,
											'ip' => $current_user_ip,
											'reason' => $reason,
											'request_uri' => $request_uri,
										];
										
										// Log the event BEFORE calling kill_request() (following existing pattern)
										wf_sn_el_modules::log_event('security_ninja', 'do_init_action', 'Blocked.', $data, null, $current_user_ip);
										
										wp_clear_auth_cookie();
										\WPSecurityNinja\Plugin\Wf_sn_cf::kill_request($current_user_ip, $reason, null, true);
										exit();
									}
								}
							}
						}
					}
					
					// If not blocked, show admin protection message
					$extraarr = array(
						'ip' => $current_user_ip,
						'user_agent' => $ua_string,
						'request_uri' => $request_uri,
					);

					// Log the unauthorized access attempt
					wf_sn_el_modules::log_event('security_ninja', 'attempted_access_to_wp_admin_url', 'User tried to visit wp-admin URL', $extraarr);

					// Show generic error message without revealing the custom login URL
					wp_die(
						__('Access Denied: Unauthorized access to the admin area is prohibited.', 'security-ninja'),
						__('Access Denied', 'security-ninja'),
						array('response' => 403)
					);
				}
			}
		}

		/**
		 * admin_gate_init - block unauthenticated access to wp-admin early (before core redirect)
		 *
		 * @author	Lars Koudal
		 * @since	v0.0.1
		 * @version	v1.0.0	Wednesday, August 27th, 2025.
		 * @access	public static
		 * @return	void
		 */
		public static function admin_gate_init()
		{
			if (!self::is_active()) return;
			// Skip AJAX/CRON/REST/CLI
			if (wp_doing_ajax() || wp_doing_cron() || (defined('REST_REQUEST') && REST_REQUEST) || (defined('WP_CLI') && WP_CLI)) return;
			
			// Detect direct admin requests early
			$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
			if ($uri && strpos($uri, '/wp-admin/') === 0) {
				if (!is_user_logged_in()) {
					$current_user_ip = \WPSecurityNinja\Plugin\Wf_sn_cf::get_user_ip();
					$ua_string = '';
					if (isset($_SERVER['HTTP_USER_AGENT'])) {
						$ua_string = sanitize_text_field($_SERVER['HTTP_USER_AGENT']);
					}
					
					// Check if visitor is blocked (country blocks, IP bans, etc.) before showing admin protection
					// This ensures redirect URL setting works for blocked visitors accessing wp-admin
					if (class_exists(__NAMESPACE__ . '\Wf_sn_cf')) {
						$cf_options = \WPSecurityNinja\Plugin\Wf_sn_cf::get_options();
						
						// Only check blocking if firewall is active
						if (isset($cf_options['active']) && 1 === (int) $cf_options['active']) {
							$reason = \WPSecurityNinja\Plugin\Wf_sn_cf::is_banned_ip($current_user_ip);
							
							if ($reason) {
								// Check if this is a country-based ban
								$is_country_ban = (strpos($reason, 'Country is blocked') !== false);
								
								// Special handling for country bans with login-only mode
								// If "Only block these countries from login functionality" is enabled:
								// - Country-blocked users can view the site
								// - But they should see admin protection message (NOT redirect) when accessing wp-admin
								if ($is_country_ban && isset($cf_options['countryblock_loginonly']) && $cf_options['countryblock_loginonly']) {
									// Country blocking is login-only mode - show admin protection message, don't redirect
									// This allows them to view the site but blocks wp-admin access
									// Fall through to show admin protection message below
								} else {
									// For country bans without login-only mode, or other ban types (IP bans, global blocks, etc.)
									// Block and redirect via kill_request() which respects redirect URL setting
									$should_block = false;
									
									if ($is_country_ban) {
										// Country ban without login-only mode - block from entire site
										$should_block = true;
									} else {
										// For other ban types (IP bans, global blocks, etc.), check if 'global' setting is enabled
										if (!isset($cf_options['global']) || !$cf_options['global']) {
											// If global is OFF, only block from login-related pages
											// /wp-admin/ is a login-related request, so block it
											$should_block = true;
										} else {
											// Global is ON, block all requests including /wp-admin/
											$should_block = true;
										}
									}
									
									if ($should_block) {
										\WPSecurityNinja\Plugin\Wf_sn_cf::update_blocked_count($current_user_ip);
										
										$data = [
											'user_agent' => $ua_string,
											'ip' => $current_user_ip,
											'reason' => $reason,
											'request_uri' => $uri,
										];
										
										// Log the event BEFORE calling kill_request() (following existing pattern)
										wf_sn_el_modules::log_event('security_ninja', 'do_init_action', 'Blocked.', $data, null, $current_user_ip);
										
										wp_clear_auth_cookie();
										\WPSecurityNinja\Plugin\Wf_sn_cf::kill_request($current_user_ip, $reason, null, true);
										exit();
									}
								}
							}
						}
					}
					
					// If not blocked, show admin protection message
					$extraarr = array(
						'ip' => $current_user_ip,
						'user_agent' => $ua_string,
						'request_uri' => $uri,
					);
					wf_sn_el_modules::log_event('security_ninja', 'attempted_access_to_wp_admin_url', 'User tried to visit wp-admin URL', $extraarr);
					wp_die(
						__('Access Denied: Unauthorized access to the admin area is prohibited.', 'security-ninja'),
						__('Access Denied', 'security-ninja'),
						array('response' => 403)
					);
				}
			}
		}

		/**
		 * Checks if the rename login module is activated
		 * Via cloud-firewall module settings
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Tuesday, November 16th, 2021.	
		 * @version	v1.0.1	Saturday, February 19th, 2022.
		 * @access	private static
		 * @return	boolean
		 */
		private static function is_active()
		{
			if (!class_exists(__NAMESPACE__ . '\wf_sn_cf')) return false;

			$fw_settings = wf_sn_cf::get_options();

			// Return false if firewall in general are turned off
			if (!isset($fw_settings['active'])) return false;
			if (0 === intval($fw_settings['active'])) return false;

			// Checks the rename login URL feature
			if (!isset($fw_settings['change_login_url'])) return false;
			if (0 === intval($fw_settings['change_login_url'])) return false;
			if (1 === intval($fw_settings['change_login_url'])) return true;

			// Default state
			return false;
		}

		/**
		 * admin_notices.
		 *
		 * @author	Lars Koudal
		 * @since	v0.0.1
		 * @version	v1.0.0	Friday, March 11th, 2022.
		 * @access	public static
		 * @return	void
		 */
		public static function admin_notices()
		{
			global $pagenow;
			if (!self::is_active()) return;
			if (\PAnD::is_admin_notice_active('wf-sn-notice-newlogin-forever')) {
				if (!is_network_admin() && $pagenow === 'options-permalink.php' && isset($_GET['settings-updated'])) {
					echo '<div data-dismissible="wf-sn-notice-newlogin-forever" class="secnin-notice notice notice-success is-dismissible"><p>' . sprintf(__('Your login page is now here: %s. Bookmark this page!', 'security-ninja'), '<strong><a href="' . esc_url(self::new_login_url()) . '">' . esc_url(self::new_login_url()) . '</a></strong>') . '</p></div>';
				}
			}
		}

		/**
		 * plugins_loaded.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Monday, October 18th, 2021.
		 * @version	v1.0.1	Monday, August 4th, 2025.
		 * @access	public
		 * @return	void
		 */
		public static function plugins_loaded()
		{
			global $pagenow;
			if (
				!is_multisite() && (
					strpos($_SERVER['REQUEST_URI'], 'wp-signup') !== false ||
					strpos($_SERVER['REQUEST_URI'], 'wp-activate') !== false
				)
			) {
				wp_die('Not activated.');
			}

			$request = parse_url($_SERVER['REQUEST_URI']);
			$path = isset($request['path']) ? $request['path'] : '';

			// Only intercept direct access to wp-login.php, not WordPress core redirects
			// Check if this is a direct request to wp-login.php (not a redirect)
			$is_direct_access = (
				strpos($_SERVER['REQUEST_URI'], 'wp-login.php') !== false ||
				(!empty($path) && untrailingslashit($path) === site_url('wp-login', 'relative'))
			);
			
			// Don't intercept if this is admin, AJAX, cron, or if there's a redirect happening
			if ($is_direct_access && !is_admin() && !wp_doing_ajax() && !wp_doing_cron() && !defined('DOING_AUTOSAVE')) {
				// Check if this might be a WordPress core redirect (has referer from wp-admin)
				$referer = wp_get_referer();
				if ($referer && strpos($referer, 'wp-admin') !== false) {
					// This is likely a WordPress core redirect from wp-admin to wp-login.php
					// Don't intercept it - let WordPress handle the redirect
					return;
				}
				
				// This is a direct access to wp-login.php - intercept it
				self::$wp_login_php = true;
				$_SERVER['REQUEST_URI'] = self::user_trailingslashit('/' . str_repeat('-/', 10));
				$pagenow = 'index.php';
			} elseif (
				!empty($path) && 
				(untrailingslashit($path) === home_url(self::new_login_slug(), 'relative') || (
					!get_option('permalink_structure') &&
					isset($_GET[self::new_login_slug()]) &&
					empty($_GET[self::new_login_slug()])
				))
			) {
				$pagenow = 'wp-login.php';
			}
		}

		/**
		 * wp_loaded.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Monday, October 18th, 2021.
		 * @access	public
		 * @return	void
		 */
		public static function wp_loaded()
		{

			global $pagenow;

			$request = parse_url($_SERVER['REQUEST_URI']);

			if (
				$pagenow === 'wp-login.php' &&
				$request['path'] !== self::user_trailingslashit($request['path']) &&
				get_option('permalink_structure')
			) {
				wp_safe_redirect(self::user_trailingslashit(self::new_login_url()) . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''));
				die;
			} elseif (self::$wp_login_php) {
				if (
					($referer = wp_get_referer()) &&
					strpos($referer, 'wp-activate.php') !== false &&
					($referer = parse_url($referer)) &&
					!empty($referer['query'])
				) {
					parse_str($referer['query'], $referer);

					if (
						!empty($referer['key']) &&
						($result = wpmu_activate_signup($referer['key'])) &&
						is_wp_error($result) && (
							$result->get_error_code() === 'already_active' ||
							$result->get_error_code() === 'blog_taken'
						)
					) {
						wp_safe_redirect(self::new_login_url() . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''));
						die;
					}
				}

				$current_user_ip = \WPSecurityNinja\Plugin\Wf_sn_cf::get_user_ip();
				$ua_string = '';
				if (isset($_SERVER['HTTP_USER_AGENT'])) {
					$ua_string = sanitize_text_field($_SERVER['HTTP_USER_AGENT']);
				}
				$extraarr = array(
					'ip' => $current_user_ip,
					'user_agent' => $ua_string,
				);

				wf_sn_el_modules::log_event('security_ninja', 'attempted_access_to_wplogin_php', 'User tried to visit wp-login.php', $extraarr);

				self::wp_template_loader();
			} elseif ($pagenow === 'wp-login.php') {
				global $error, $interim_login, $action, $user_login;

				@require_once ABSPATH . 'wp-login.php';

				die;
			}
		}

		/**
		 * site_url.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Monday, May 2nd, 2022.
		 * @access	public static
		 * @param	mixed	$url    	
		 * @param	mixed	$path   	
		 * @param	mixed	$scheme 	
		 * @param	mixed	$blog_id	
		 * @return	mixed
		 */
		public static function site_url($url, $path, $scheme, $blog_id)
		{
			return self::filter_wp_login_php($url, $scheme);
		}

		/**
		 * network_site_url.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Monday, May 2nd, 2022.
		 * @access	public static
		 * @param	mixed	$url   	
		 * @param	mixed	$path  	
		 * @param	mixed	$scheme	
		 * @return	mixed
		 */
		public static function network_site_url($url, $path, $scheme)
		{
			return self::filter_wp_login_php($url, $scheme);
		}

		/**
		 * wp_redirect.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Monday, May 2nd, 2022.
		 * @version	v1.0.1	Monday, August 4th, 2025.
		 * @access	public static
		 * @param	mixed	$location	
		 * @param	mixed	$status  	
		 * @return	mixed
		 */
		public static function wp_redirect($location, $status)
		{
			// Only filter wp-login.php URLs, not wp-admin redirects
			// This prevents redirect loops and maintains security through obscurity
			if (strpos($location, 'wp-login.php') !== false) {
				return self::filter_wp_login_php($location);
			}
			
			return $location;
		}

		/**
		 * filter_wp_login_php.
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Monday, October 18th, 2021.
		 * @access	public
		 * @param	mixed	$url   	
		 * @param	mixed	$scheme	Default: null
		 * @return	mixed
		 */
		public static function filter_wp_login_php($url, $scheme = null)
		{
			if (strpos($url, 'wp-login.php') !== false) {
				if (is_ssl()) {
					$scheme = 'https';
				}

				$args = explode('?', $url);

				if (isset($args[1])) {
					parse_str($args[1], $args);
					$url = add_query_arg($args, self::new_login_url($scheme));
				} else {
					$url = self::new_login_url($scheme);
				}
			}

			return $url;
		}

		/**
		 * Change the welcome email on Multisite
		 *
		 * @author	Unknown
		 * @since	v0.0.1
		 * @version	v1.0.0	Monday, October 18th, 2021.
		 * @access	public
		 * @param	mixed	$value	
		 * @return	mixed
		 */
		public static function welcome_email($value)
		{
			$settings = wf_sn_cf::get_options();
			if (isset($settings['change_login_url']) && $settings['change_login_url'] && ('' <> $settings['new_login_url'])) {
				return str_replace('wp-login.php', trailingslashit(self::new_login_slug()), $value);
			}
			return $value;
		}
	}

	new SecNin_Rename_WP_Login;
}