plugin_dir = 'really-simple-ssl-pro-multisite'; $this->plugin_filename = 'really-simple-ssl-pro-multisite.php'; } else { $this->plugin_dir = 'really-simple-ssl-pro'; $this->plugin_filename = 'really-simple-ssl-pro.php'; } } self::$_this = $this; $this->abs_path = $this->getabs_path(); $this->htaccess_file_manager = RSSSL_Htaccess_File_Manager::get_instance(); register_deactivation_hook( __DIR__ . '/' . $this->plugin_filename, array( $this, 'deactivate' ) ); add_action( 'admin_init', array( $this, 'add_privacy_info' ) ); add_action( 'admin_init', array( $this, 'maybe_dismiss_review_notice' ) ); add_action( 'rsssl_daily_cron', array( $this, 'clear_admin_notices_cache' ) ); // Clear notice cache when permalinks are updated to fix multisite issues add_action( 'update_option_permalink_structure', array( $this, 'clear_admin_notices_cache' ) ); add_action( 'update_option_rewrite_rules', array( $this, 'clear_admin_notices_cache' ) ); add_action( 'update_option_permalink_structure', array( $this, 'check_permalink_change_for_custom_login_url' ), 10, 2 ); //add the settings page for the plugin add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); add_action( 'admin_init', array( $this, 'listen_for_deactivation' ), 40 ); add_action( 'plugins_loaded', array( $this, 'maybe_redirect_old_settings_url' ), 10 ); //callbacks for the ajax dismiss buttons add_action( 'wp_ajax_rsssl_dismiss_review_notice', array( $this, 'dismiss_review_notice_callback' ) ); //handle notices add_action( 'admin_notices', array( $this, 'show_notices' ) ); //show review notice, only to free users if ( ! defined( 'rsssl_pro' ) && ! is_multisite() ) { add_action( 'admin_notices', array( $this, 'show_leave_review_notice' ) ); } //hooks only needed on settings page if ( $this->is_settings_page() ) { /** * Htaccess redirect handling */ add_action( 'rsssl_after_save_field', array( $this, 'maybe_flush_wprocket_htaccess' ), 100, 4 ); add_action( 'admin_init', array( $this, 'insert_secure_cookie_settings' ), 70 ); add_action( 'admin_init', array( $this, 'recheck_certificate' ) ); } add_action('admin_init', array($this, 'autoFixHtaccess'), 100); add_filter( 'rsssl_htaccess_security_rules', array( $this, 'add_htaccess_redirect' ) ); add_filter( 'admin_init', array( $this, 'handle_activation' ) ); add_action( 'rocket_activation', 'rsssl_wrap_htaccess' ); add_action( 'rocket_deactivation', 'rsssl_wrap_htaccess' ); add_action('rocket_after_activation', array($this, 'enableAutoFix'), 100); $plugin = rsssl_plugin; add_filter( "plugin_action_links_$plugin", array( $this, 'plugin_settings_link' ) ); add_filter( "network_admin_plugin_action_links_$plugin", array($this,'plugin_settings_link' ) ); add_filter( 'rsssl_clear_test_caches', array( $this, 'clear_404_test_cache'), 10, 1 ); add_action( 'rsssl_upgrade', array( $this, 'run_table_init_hook'), 10, 1); add_action( 'upgrader_process_complete', array( $this, 'run_table_init_hook'), 10, 1); add_action( 'wp_initialize_site', array( $this, 'run_table_init_hook'), 10, 1); add_action( "rsssl_after_save_field", array($this, 'maybe_delete_permission_detection_option'), 101, 4 ); } public static function this() { return self::$_this; } /** * Simply fixes the .htaccess file of the wordpress installation. * * @return void */ public function autoFixHtaccess() { if ( ! rsssl_user_can_manage() ) { return; } if (get_option('rsssl_upgrade_firewall', false ) == true) { do_action('rsssl_update_rules'); update_option('rsssl_upgrade_firewall', false); } } /** * Enable the auto fix for the htaccess rules * This is used to automatically fix the htaccess rules when the plugin is updated. Or when an external party * has changed the htaccess rules. * @return void */ public function enableAutoFix() { if ( ! rsssl_user_can_manage() ) { return; } update_option('rsssl_upgrade_firewall', true); } /** * On Multisite site creation, run table init hook as well. * @return void */ public function run_table_init_hook(){ //only load on front-end if it's a cron job if ( !is_admin() && !wp_doing_cron() ) { return; } if ( !wp_doing_cron() && !rsssl_user_can_manage() ) { return; } //if this is already triggered, exit. if ( defined('RSSSL_INSTALLING_TABLES') && RSSSL_INSTALLING_TABLES ) { return; } define('RSSSL_INSTALLING_TABLES', true); do_action( 'rsssl_install_tables' ); //we need to run table creation across subsites as well. if ( is_multisite() ) { $sites = get_sites(); if (count($sites)>0) { foreach ($sites as $site) { switch_to_blog($site->blog_id); do_action( 'rsssl_install_tables' ); restore_current_blog(); } } } } public function handle_activation(){ if ( !rsssl_admin_logged_in() ) { return; } if ( get_option('rsssl_activation') ) { if ( !class_exists('rsssl_le_hosts')) { require_once( rsssl_path . 'lets-encrypt/config/class-hosts.php'); } ( new rsssl_le_hosts() )->detect_host_on_activation(); $this->run_table_init_hook(); do_action('rsssl_activation'); delete_option('rsssl_activation'); } } /** * Add settings link on plugins overview page * @param array $links * * @return array */ public function plugin_settings_link($links) { //free version if ( ! rsssl_user_can_manage() || ( is_multisite() && ! is_network_admin() ) ) { return $links; } $settings_link = ''; $url = rsssl_admin_url(); //settings only on network wide activated, or no multisite at all. if ( is_multisite() && rsssl_is_networkwide_active() && is_super_admin() ) { $settings_link = '' . __( 'Settings', 'really-simple-ssl' ) . ''; } elseif ( ! is_multisite() ) { $settings_link = '' . __( 'Settings', 'really-simple-ssl' ) . ''; } array_unshift( $links, $settings_link ); //support $support = apply_filters( 'rsssl_support_link', '' . __( 'Support', 'really-simple-ssl' ) . '' ); array_unshift( $links, $support ); if ( ! defined( 'rsssl_pro' ) ) { $upgrade_link = '' . __( 'Improve security - Upgrade', 'really-simple-ssl' ) . ''; array_unshift( $links, $upgrade_link ); } // Always add the ID to deactivate link so JavaScript can find it reliably if ( isset( $links['deactivate'] ) ) { $deactivate_link_id = defined( 'rsssl_pro' ) ? 'deactivate-really-simple-security-pro' : 'deactivate-really-simple-security'; // Add the ID attribute to enable JavaScript event delegation $links['deactivate'] = preg_replace( '/here.', 'really-simple-ssl' ), 'https://really-simple-ssl.com/privacy-statement/' ); wp_add_privacy_policy_content( 'Really Simple Security', wp_kses_post( wpautop( $content, false ) ) ); } /** * Check if current day falls within required date range. * * @return bool */ public function is_bf() { if ( defined( 'rsssl_pro' ) ) { return false; } // Get current date and time in GMT as timestamp $current_date = strtotime( gmdate( 'Y-m-d H:i:s' ) ); // Define the start and end dates for the range in GMT (including specific times) $start_date = strtotime( 'November 24 2025 00:00:00 GMT' ); $end_date = strtotime( 'December 1 2025 23:59:59 GMT' ); // Check if the current date and time falls within the date range if ( $current_date >= $start_date && $current_date <= $end_date ) { return true; } return false; } /** * Initializes the admin class * * @since 2.2 * * @access public * */ public function init() { if ( ! rsssl_user_can_manage() ) { return; } if ( defined( 'RSSSL_FORCE_ACTIVATE' ) && RSSSL_FORCE_ACTIVATE ) { rsssl_update_option( 'ssl_enabled', true ); } /* * check if we're one minute past the activation. Then flush rewrite rules * this way we lower the memory impact on activation * Flush should happen on shutdown, not on init, as often happens in other plugins * https://codex.wordpress.org/Function_Reference/flush_rewrite_rules */ $activation_time = get_option( 'rsssl_flush_rewrite_rules' ); $more_than_one_minute_ago = $activation_time < strtotime( '-1 minute' ); $less_than_2_minutes_ago = $activation_time > strtotime( '-2 minute' ); if ( $more_than_one_minute_ago && $less_than_2_minutes_ago && get_option( 'rsssl_flush_rewrite_rules' ) ) { delete_option( 'rsssl_flush_rewrite_rules' ); add_action( 'shutdown', static function() { flush_rewrite_rules( false ); // soft flush – doesn’t rewrite .htaccess }); } $more_than_2_minute_ago = get_option( 'rsssl_flush_caches' ) < strtotime( '-2 minute' ); $less_than_5_minutes_ago = get_option( 'rsssl_flush_caches' ) > strtotime( '-5 minute' ); if ( $more_than_2_minute_ago && $less_than_5_minutes_ago && get_option( 'rsssl_flush_caches' ) ) { delete_option( 'rsssl_flush_caches' ); add_action( 'shutdown', array( RSSSL()->cache, 'flush' ) ); } /* Detect configuration when: - on settings page - SSL not enabled */ //when configuration detection should run again add_action('admin_init', array($this, 'detect_configuration_init'), 20); } /** * Run SSL configuration detection on the init hook * * This function is hooked to the init action with priority 20, * ensuring translations are properly loaded before running. * It performs SSL configuration detection and followup actions * including wpconfig checks and htaccess redirect tests. * * @since 6.3.7 * @access public * @return void */ public function detect_configuration_init() { if ( rsssl_get_option( 'ssl_enabled' ) || ! $this->is_settings_page() || ! defined( 'RSSSL_DOING_SYSTEM_STATUS' ) ) { return; } $this->detect_configuration(); if ( ! $this->wpconfig_ok() ) { rsssl_update_option( 'ssl_enabled', false ); } else { //when one of the used server variables was found, test if the redirect works if ( RSSSL()->server->uses_htaccess() && 'NA' !== $this->ssl_type ) { $this->htaccess_test_success(); } } } /** * Add htaccess redirect * @hooked * @param array $rules * @return [] */ public function add_htaccess_redirect( $rules ) { $rule = $this->get_redirect_rules(); if ( ! empty( $rule ) ) { $rules[] = [ 'rules' => $rule, 'identifier' => 'RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1', ]; } return $rules; } /** * Check if we're in the middle of wp rocket deactivation * * @return bool */ public function is_deactivating_wprocket() { //default deactivating $is_deactivating = isset( $_GET['action'] ) && 'deactivate' === $_GET['action'] && isset( $_GET['plugin'] ) && strpos( $_GET['plugin'], 'wp-rocket.php' ) !== false; //deactivating with modal return $is_deactivating || ( isset( $_GET['action'] ) && 'rocket_deactivation' === $_GET['action'] ); } /** * Deactivate the plugin while keeping SSL * Activated when the 'uninstall_keep_ssl' button is clicked in the settings tab * */ public function listen_for_deactivation() { if ( ! rsssl_user_can_manage() ) { return; } if ( ! isset( $_GET['token'] ) || ( ! wp_verify_nonce( $_GET['token'], 'rsssl_deactivate_plugin' ) ) ) { return; } do_action( 'rsssl_deactivate' ); rsssl_clear_scheduled_hooks(); $ssl_was_enabled = rsssl_get_option( 'ssl_enabled' ); if ( isset( $_GET['action'] ) && 'uninstall_keep_ssl' === $_GET['action'] ) { $this->deactivate_site( $ssl_was_enabled ); // Don't revert to http $this->deactivate_plugin(); } if ( isset( $_GET['action'] ) && 'uninstall_revert_ssl' === $_GET['action'] ) { // Update site url from https:// to http:// $this->deactivate_site( $ssl_was_enabled, true ); // Revert to http $this->deactivate_plugin(); } wp_redirect( admin_url( 'plugins.php' ) ); exit; } /** * @return void * * Deactivate plugin logic */ private function deactivate_plugin() { //deactivate plugin, but don't revert to http. $plugin = $this->get_current_rsssl_dirname() . '/' . $this->plugin_filename; $plugin = plugin_basename( trim( $plugin ) ); if ( is_multisite() ) { $network_current = get_site_option( 'active_sitewide_plugins', array() ); if ( is_plugin_active_for_network( $plugin ) ) { unset( $network_current[ $plugin ] ); } update_site_option( 'active_sitewide_plugins', $network_current ); //remove plugin one by one on each site $sites = get_sites(); foreach ( $sites as $site ) { switch_to_blog( $site->blog_id ); $current = get_option( 'active_plugins', array() ); $current = $this->remove_plugin_from_array( $plugin, $current ); update_option( 'active_plugins', $current ); restore_current_blog(); //switches back to previous blog, not current, so we have to do it each loop } } else { $current = get_option( 'active_plugins', array() ); $current = $this->remove_plugin_from_array( $plugin, $current ); update_option( 'active_plugins', $current ); } } /** * Remove the plugin from the active plugins array when called from listen_for_deactivation * * */ public function remove_plugin_from_array( $plugin, $current ) { $key = array_search( $plugin, $current, true ); if ( false !== $key ) { unset( $current[ $key ] ); } return $current; } /** * @return bool * * Check if this site is a Bitnami site */ public function uses_bitnami(): bool { if ( isset( $_SERVER['DOCUMENT_ROOT'] ) && $_SERVER['DOCUMENT_ROOT'] === '/opt/bitnami/wordpress' ) { return true; } return false; } /** * Check if site uses an htaccess.conf file, used in bitnami installations * * @Since 3.1 */ public function uses_htaccess_conf() { $htaccess_conf_file = dirname( ABSPATH ) . '/conf/htaccess.conf'; //conf/htaccess.conf can be outside of open basedir, return false if so $open_basedir = ini_get( 'open_basedir' ); if ( ! empty( $open_basedir ) ) { return false; } return is_file( $htaccess_conf_file ); } /** * If the user has clicked "recheck certificate, clear the cache for the certificate check. * Used in a form in the dashboard notices. * @return void */ public function recheck_certificate(): void { if ( ! rsssl_user_can_manage() ) { return; } if ( ! isset($_POST['rsssl_recheck_nonce_field']) || ! wp_verify_nonce(sanitize_text_field( wp_unslash( $_POST['rsssl_recheck_nonce_field' ])) , 'rsssl_recheck_nonce') ) { return; // nonce failed, do not proceed } if ( isset( $_POST['rsssl_recheck_certificate'] ) ) { delete_transient( 'rsssl_certinfo' ); } } /** * Activate the SSL for this site */ public function activate_ssl($data) { //skip activation if safe mode if ( defined( 'RSSSL_SAFE_MODE' ) && RSSSL_SAFE_MODE ) { return [ 'success' => true, 'site_url_changed' => false, ]; } if ( !rsssl_user_can_manage() ) { return [ 'success' => false, 'site_url_changed' => false, ]; } $safe_mode = defined( 'RSSSL_SAFE_MODE' ) && RSSSL_SAFE_MODE; $error = false; $is_rest_request = isset( $data['is_rest_request'] ); $site_url_changed = false; $wpcli = defined( 'WP_CLI' ) && WP_CLI; if ( rsssl_get_option( 'site_has_ssl' ) || get_option( 'rsssl_ssl_detection_overridden' ) || $wpcli ) { //in a configuration reverse proxy without a set server variable https, add code to wpconfig if ( $this->do_wpconfig_loadbalancer_fix || $this->no_server_variable ) { $this->wpconfig_loadbalancer_fix(); } if ( ! $safe_mode && $this->wpconfig_siteurl_not_fixed && ! $this->uses_bitnami() ) { $this->fix_siteurl_defines_in_wpconfig(); } $this->insert_secure_cookie_settings(); if ( ! $safe_mode ) { rsssl_update_option( 'redirect', 'wp_redirect' ); rsssl_update_option( 'mixed_content_fixer', true ); //flush caches when just activated ssl //flush the permalinks update_option( 'rsssl_activation_timestamp', time(), false ); if ( ! defined( 'RSSSL_NO_FLUSH' ) || ! RSSSL_NO_FLUSH ) { update_option( 'rsssl_flush_rewrite_rules', time(), false ); } update_option( 'rsssl_flush_caches', time(), false ); } rsssl_update_option( 'ssl_enabled', true ); $site_url_changed = $this->set_siteurl_to_ssl(); delete_option( 'rsssl_admin_notices' ); } else { $error = true; } //if this is true, this is a request from the network admin. We save an option to ensure we know that this part is completed if ( is_multisite() && rsssl_is_networkwide_active() ) { update_site_option( 'rsssl_network_activation_status', 'main_site_activated' ); } if ( $is_rest_request ) { return [ 'success' => ! $error, 'site_url_changed' => $site_url_changed, 'request_success' => true, ]; } return ! $error; } /** * Check if the wp config configuration is ok for SSL activation * * @return bool */ public function wpconfig_ok() { //return false; if ( ( $this->do_wpconfig_loadbalancer_fix || $this->no_server_variable || $this->wpconfig_siteurl_not_fixed ) && ! $this->wpconfig_is_writable() && ! $this->uses_bitnami() ) { $result = false; } else { $result = true; } return apply_filters( 'rsssl_wpconfig_ok_check', $result ); } /** * @param string $class * @param string $content * @param string|bool $more_info * @param string|bool $dismiss_id * * @return false|string * * @since 4.0 * Return the notice HTML * */ public function notice_html( string $css_class, string $content, $more_info = false, $logo=false, $dismiss_id = false, $dashboard_button=false ) { if ( ! rsssl_user_can_manage() ) { return ''; } $css_class .= ' notice '; $is_internal_link = strpos( $more_info, 'really-simple-ssl.com' ) === false; $target = ! $is_internal_link ? 'target="_blank"' : ''; $url = is_ssl() ? 'https://' : 'http://'; $url .= $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; $url = wp_validate_redirect( $url, apply_filters( 'wp_safe_redirect_fallback', admin_url(), 302 ) ); $url = esc_url_raw( $url ); ob_start();?>
$dismiss_id ], $url ), 'rsssl_dismiss_notice_' . $dismiss_id ); ?> rel="noopener noreferrer" href="">
wpconfig_path(); if ( empty( $wpconfig_path ) ) { return false; } if ( is_writable( $wpconfig_path ) ) { return true; } return false; } /** * Check if the uninstall file is renamed to .php * * @return string */ public function check_for_uninstall_file() { if ( file_exists( __DIR__ . '/force-deactivate.php' ) ) { return 'fail'; } return 'success'; } /** * Check to see if we are on the settings page, action hook independent * * @since 2.1 * * @access public * */ public function is_settings_page() { if ( rsssl_is_logged_in_rest() ) { return true; } if ( ! isset( $_SERVER['QUERY_STRING'] ) ) { return false; } if ( isset( $_GET['action'] ) && 'rsssl_rest_api_fallback' === $_GET['action'] ) { return true; } parse_str( $_SERVER['QUERY_STRING'], $params ); return array_key_exists( 'page', $params ) && 'really-simple-security' === $params['page']; } /** * remove https from defined siteurl and homeurl in the wpconfig, if present * * @since 2.1 * * @access public * * @return void */ public function remove_ssl_from_siteurl_in_wpconfig() { if ( ! rsssl_user_can_manage() ) { return; } $wpconfig_path = $this->wpconfig_path(); if ( ! empty( $wpconfig_path ) ) { $wpconfig = file_get_contents( $wpconfig_path ); $homeurl_pos = strpos( $wpconfig, "define('WP_HOME','https://" ); $siteurl_pos = strpos( $wpconfig, "define('WP_SITEURL','https://" ); if ( false !== $homeurl_pos || false !== $siteurl_pos ) { if ( is_writable( $wpconfig_path ) ) { $search_array = array( "define('WP_HOME','https://", "define('WP_SITEURL','https://" ); $ssl_array = array( "define('WP_HOME','http://", "define('WP_SITEURL','http://" ); //now replace these urls $wpconfig = str_replace( $search_array, $ssl_array, $wpconfig ); file_put_contents( $wpconfig_path, $wpconfig ); } } } } /** * Checks if the wp config contains any defined siteurl and homeurl * * @return void */ private function check_for_siteurl_in_wpconfig() { if ( ! rsssl_user_can_manage() ) { return; } $wpconfig_path = $this->wpconfig_path(); if ( empty( $wpconfig_path ) ) { return; } $wpconfig = file_get_contents( $wpconfig_path ); $homeurl_pattern = '/(define\(\s*\'WP_HOME\'\s*,\s*\'http\:\/\/)/'; $siteurl_pattern = '/(define\(\s*\'WP_SITEURL\'\s*,\s*\'http\:\/\/)/'; $this->wpconfig_siteurl_not_fixed = false; if ( preg_match( $homeurl_pattern, $wpconfig ) || preg_match( $siteurl_pattern, $wpconfig ) ) { $this->wpconfig_siteurl_not_fixed = true; } } /** * Runs only when siteurl or homeurl define was found in the wpconfig, with the check_for_siteurl_in_wpconfig function * and only when wpconfig is writable. * * @since 2.1 * * @access public * */ private function fix_siteurl_defines_in_wpconfig() { if ( ! rsssl_user_can_manage() ) { return; } $wpconfig_path = $this->wpconfig_path(); if ( empty( $wpconfig_path ) ) { return; } $wpconfig = file_get_contents( $wpconfig_path ); $homeurl_pattern = '/(define\(\s*\'WP_HOME\'\s*,\s*\'http\:\/\/)/'; $siteurl_pattern = '/(define\(\s*\'WP_SITEURL\'\s*,\s*\'http\:\/\/)/'; if ( preg_match( $homeurl_pattern, $wpconfig ) || preg_match( $siteurl_pattern, $wpconfig ) ) { if ( is_writable( $wpconfig_path ) ) { $wpconfig = preg_replace( $homeurl_pattern, "define('WP_HOME','https://", $wpconfig ); $wpconfig = preg_replace( $siteurl_pattern, "define('WP_SITEURL','https://", $wpconfig ); file_put_contents( $wpconfig_path, $wpconfig ); } else { //only when siteurl or homeurl is defined in wpconfig, and wpconfig is not writable is there a possible issue because we cannot edit the defined urls. $this->wpconfig_siteurl_not_fixed = true; } } } /** * Check if the wpconfig is already fixed * * @since 2.2 * * @access public * */ public function wpconfig_has_fixes() { $wpconfig_path = $this->wpconfig_path(); if ( empty( $wpconfig_path ) ) { return false; } $wpconfig = file_get_contents( $wpconfig_path ); //only one of two fixes possible. if ( strpos( $wpconfig, '//Begin Really Simple Security Load balancing fix' ) !== false ) { return true; } if ( strpos( $wpconfig, '//Begin Really Simple Security Server variable fix' ) !== false ) { return true; } return false; } /** * In case of load balancer without server https on, add fix in wp-config * * @since 2.1 * * @access public * */ public function wpconfig_loadbalancer_fix() { if ( ! rsssl_user_can_manage() ) { return; } $wpconfig_path = $this->wpconfig_path(); if ( empty( $wpconfig_path ) ) { return; } $wpconfig = file_get_contents( $wpconfig_path ); if ( strpos( $wpconfig, '//Begin Really Simple Security Server variable fix' ) !== false ) { return; } if ( strpos( $wpconfig, '//Begin Really Simple Security Load balancing fix' ) !== false ) { return; } if ( is_writable( $wpconfig_path ) ) { $rule = "\n" . '//Begin Really Simple Security Server variable fix' . "\n"; $rule .= '$_SERVER["HTTPS"] = "on";' . "\n"; $rule .= '//END Really Simple Security Server variable fix' . "\n"; $insert_after = 'wpconfig_path(); if ( empty( $wpconfig_path ) ) { return; } //check for permissions $wpconfig = file_get_contents( $wpconfig_path ); if ( ! is_writable( $wpconfig_path ) ) { return; } //remove edits $wpconfig = preg_replace( '/\/\/Begin\s?Really\s?Simple\s?SSL\s?Server\s?variable\s?fix.*?\/\/END\s?Really\s?Simple\s?SSL\s?Server\s?variable\s?fix/s', '', $wpconfig ); $wpconfig = preg_replace( "/\n+/", "\n", $wpconfig ); file_put_contents( $wpconfig_path, $wpconfig ); } /** * Changes the siteurl and homeurl to https * * @since 2.0 * * @access public * @return bool */ public function set_siteurl_to_ssl() { $site_url_changed = false; $site_url = get_option( 'siteurl' ); $home_url = get_option( 'home' ); if ( strpos( $site_url, 'https://' ) === false || strpos( $home_url, 'https://' ) === false ) { update_option( 'siteurl', str_replace( 'http://', 'https://', $site_url ) ); update_option( 'home', str_replace( 'http://', 'https://', $home_url ) ); $site_url_changed = true; } //RSSSL has it's own, more extensive mixed content fixer. update_option( 'https_migration_required', false ); return $site_url_changed; } /** * On de-activation, siteurl and homeurl are reset to http * * @since 2.0 * * @access public * */ public function remove_ssl_from_siteurl() { if ( ! rsssl_user_can_manage() ) { return; } $siteurl_no_ssl = str_replace( 'https://', 'http://', get_option( 'siteurl' ) ); $homeurl_no_ssl = str_replace( 'https://', 'http://', get_option( 'home' ) ); update_option( 'siteurl', $siteurl_no_ssl ); update_option( 'home', $homeurl_no_ssl ); } /** * Handles deactivation of this plugin * * @since 2.0 * * @access public * */ public function deactivate() { if ( ! rsssl_user_can_manage() ) { return; } if ( get_option('rsssl_free_deactivated') ) { // Deactivation call from upgrade to Pro. Do not disable features return; } if ( is_multisite() ) { RSSSL()->multisite->deactivate(); } else { $ssl_was_enabled = rsssl_get_option( 'ssl_enabled' ); $this->deactivate_site( $ssl_was_enabled ); } } /** * Deactivate SSL for the currently loaded site * * @param bool $ssl_was_enabled Whether SSL was enabled before deactivation * @param bool $revert_to_http Whether to revert URLs from https:// to http:// (default: false, keeps https://) * * @return void */ public function deactivate_site( bool $ssl_was_enabled, bool $revert_to_http = false ) { if ( ! rsssl_user_can_manage() ) { return; } $this->remove_secure_cookie_settings(); if ( $ssl_was_enabled && $revert_to_http ) { $this->remove_ssl_from_siteurl(); if ( ! is_multisite() || is_main_site() ) { $this->remove_ssl_from_siteurl_in_wpconfig(); $this->remove_wpconfig_edit(); } } // Remove htaccess security edits // Preserve redirect when staying on HTTPS, remove it when reverting to HTTP if ( ! is_multisite() || is_main_site() ) { $clear_htaccess_redirect = $revert_to_http; rsssl_remove_htaccess_security_edits( $clear_htaccess_redirect ); } do_action( 'rsssl_deactivate' ); rsssl_update_option( 'ssl_enabled', false ); } /** * remove secure cookie settings * * @since 4.0.10 * * @access public * */ public function remove_secure_cookie_settings() { if ( ! rsssl_user_can_manage() ) { return; } if ( $this->secure_cookie_settings_status() !== 'set' ) { return; } $wpconfig_path = $this->wpconfig_path(); if ( empty( $wpconfig_path ) ) { return; } if ( ! is_writable( $wpconfig_path ) ) { return; } if ( ! empty( $wpconfig_path ) ) { $wpconfig = file_get_contents( $wpconfig_path ); $wpconfig = preg_replace( '/\/\/Begin\s?Really\s?Simple\s?SSL\s?session\s?cookie\s?settings.*?\/\/END\s?Really\s?Simple\s?SSL\s?cookie\s?settings/s', '', $wpconfig ); $wpconfig = preg_replace( "/\n+/", "\n", $wpconfig ); file_put_contents( $wpconfig_path, $wpconfig ); } } /** * Checks if we are currently on SSL protocol, but extends standard wp with loadbalancer check. * * @since 2.0 * * @access public * */ public function is_ssl_extended() { $server_var = false; if ( ( isset( $_ENV['HTTPS'] ) && ( 'on' === $_ENV['HTTPS'] ) ) || ( isset( $_SERVER['HTTP_X_FORWARDED_SSL'] ) && ( strpos( $_SERVER['HTTP_X_FORWARDED_SSL'], '1' ) !== false ) ) || ( isset( $_SERVER['HTTP_X_FORWARDED_SSL'] ) && ( strpos( $_SERVER['HTTP_X_FORWARDED_SSL'], 'on' ) !== false ) ) || ( isset( $_SERVER['HTTP_CF_VISITOR'] ) && ( strpos( $_SERVER['HTTP_CF_VISITOR'], 'https' ) !== false ) ) || ( isset( $_SERVER['HTTP_CLOUDFRONT_FORWARDED_PROTO'] ) && ( strpos( $_SERVER['HTTP_CLOUDFRONT_FORWARDED_PROTO'], 'https' ) !== false ) ) || ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && ( strpos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https' ) !== false ) ) || ( isset( $_SERVER['HTTP_X_PROTO'] ) && ( strpos( $_SERVER['HTTP_X_PROTO'], 'SSL' ) !== false ) ) ) { $server_var = true; } return is_ssl() || $server_var; } /** * Checks for SSL by opening a test page in the plugin directory * * @since 2.0 * * @access public * */ public function detect_configuration() { $this->configuration_loaded = true; //if current page is on SSL, we can assume SSL is available, even when an errormsg was returned if ( $this->is_ssl_extended() ) { $site_has_ssl = true; } else { //if certificate is valid $site_has_ssl = RSSSL()->certificate->is_valid(); } if ( $site_has_ssl ) { $filecontents = $this->get_test_page_contents(); //get filecontents to check .htaccess redirection method and wpconfig fix //check the type of SSL, either by parsing the returned string, or by reading the server vars. if ( ( false !== strpos( $filecontents, '#CLOUDFRONT#' ) ) || ( isset( $_SERVER['HTTP_CLOUDFRONT_FORWARDED_PROTO'] ) && ( 'https' === $_SERVER['HTTP_CLOUDFRONT_FORWARDED_PROTO'] ) ) ) { $this->ssl_type = 'CLOUDFRONT'; } elseif ( ( false !== strpos( $filecontents, '#CLOUDFLARE#' ) ) || ( isset( $_SERVER['HTTP_CF_VISITOR'] ) && ( false !== strpos( $_SERVER['HTTP_CF_VISITOR'], 'https' ) ) ) ) { $this->ssl_type = 'CLOUDFLARE'; } elseif ( ( false !== strpos( $filecontents, '#LOADBALANCER#' ) ) || ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && ( 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) ) { $this->ssl_type = 'LOADBALANCER'; } elseif ( ( false !== strpos( $filecontents, '#HTTP_X_PROTO#' ) ) || ( isset( $_SERVER['HTTP_X_PROTO'] ) && ( 'SSL' === $_SERVER['HTTP_X_PROTO'] ) ) ) { $this->ssl_type = 'HTTP_X_PROTO'; } elseif ( ( false !== strpos( $filecontents, '#HTTP_X_FORWARDED_SSL_ON#' ) ) || ( isset( $_SERVER['HTTP_X_FORWARDED_SSL'] ) && 'on' === $_SERVER['HTTP_X_FORWARDED_SSL'] ) ) { $this->ssl_type = 'HTTP_X_FORWARDED_SSL_ON'; } elseif ( ( false !== strpos( $filecontents, '#HTTP_X_FORWARDED_SSL_1#' ) ) || ( isset( $_SERVER['HTTP_X_FORWARDED_SSL'] ) && '1' === $_SERVER['HTTP_X_FORWARDED_SSL'] ) ) { $this->ssl_type = 'HTTP_X_FORWARDED_SSL_1'; } elseif ( ( false !== strpos( $filecontents, '#SERVER-HTTPS-ON#' ) ) || ( isset( $_SERVER['HTTPS'] ) && 'on' === strtolower( $_SERVER['HTTPS'] ) ) ) { $this->ssl_type = 'SERVER-HTTPS-ON'; } elseif ( ( false !== strpos( $filecontents, '#SERVER-HTTPS-1#' ) ) || ( isset( $_SERVER['HTTPS'] ) && '1' === strtolower( $_SERVER['HTTPS'] ) ) ) { $this->ssl_type = 'SERVER-HTTPS-1'; } elseif ( ( false !== strpos( $filecontents, '#SERVERPORT443#' ) ) || ( isset( $_SERVER['SERVER_PORT'] ) && ( '443' === $_SERVER['SERVER_PORT'] ) ) ) { $this->ssl_type = 'SERVERPORT443'; } elseif ( ( false !== strpos( $filecontents, '#ENVHTTPS#' ) ) || ( isset( $_ENV['HTTPS'] ) && ( 'on' === $_ENV['HTTPS'] ) ) ) { $this->ssl_type = 'ENVHTTPS'; } elseif ( ( false !== strpos( $filecontents, '#NO KNOWN SSL CONFIGURATION DETECTED#' ) ) ) { //if we are here, SSL was detected, but without any known server variables set. //So we can use this info to set a server variable ourselves. if ( ! $this->wpconfig_has_fixes() ) { $this->no_server_variable = true; } $this->ssl_type = 'NA'; } else { //no valid response, so set to NA $this->ssl_type = 'NA'; } //check for is_ssl() if ( ( ! $this->is_ssl_extended() && ( strpos( $filecontents, '#SERVER-HTTPS-ON#' ) === false ) && ( strpos( $filecontents, '#SERVER-HTTPS-1#' ) === false ) && ( strpos( $filecontents, '#SERVERPORT443#' ) === false ) ) || ( ! is_ssl() && $this->is_ssl_extended() ) ) { //when is_ssl would return false, we should add some code to wp-config.php if ( ! $this->wpconfig_has_fixes() ) { $this->do_wpconfig_loadbalancer_fix = true; } } } $this->check_for_siteurl_in_wpconfig(); //check againt current status, to prevent unnecessary loading of fields array during update_option $current_ssl_status = rsssl_get_option( 'site_has_ssl' ); if ( (bool) $current_ssl_status !== (bool) $site_has_ssl ) { rsssl_update_option( 'site_has_ssl', $site_has_ssl ); } } /** * Test if the htaccess redirect will work * This way, no redirect loops should occur. * * @since 2.1 * * @access public * */ public function htaccess_test_success() { $test = get_transient( 'rsssl_htaccess_test_success' ); if ( ! $test ) { $filecontents = ''; $testpage_url = trailingslashit( $this->test_url() ) . 'testssl/'; switch ( $this->ssl_type ) { case 'CLOUDFRONT': $testpage_url .= 'cloudfront'; break; case 'CLOUDFLARE': $testpage_url .= 'cloudflare'; break; case 'LOADBALANCER': $testpage_url .= 'loadbalancer'; break; case 'HTTP_X_PROTO': $testpage_url .= 'serverhttpxproto'; break; case 'HTTP_X_FORWARDED_SSL_ON': $testpage_url .= 'serverhttpxforwardedsslon'; break; case 'HTTP_X_FORWARDED_SSL_1': $testpage_url .= 'serverhttpxforwardedssl1'; break; case 'SERVER-HTTPS-ON': $testpage_url .= 'serverhttpson'; break; case 'SERVER-HTTPS-1': $testpage_url .= 'serverhttps1'; break; case 'SERVERPORT443': $testpage_url .= 'serverport443'; break; case 'ENVHTTPS': $testpage_url .= 'envhttps'; break; default: $testpage_url .= 'serverhttpson'; } $testpage_url .= ( '/ssl-test-page.html' ); $response = wp_remote_get( $testpage_url ); if ( is_array( $response ) ) { $filecontents = wp_remote_retrieve_body( $response ); } if ( ! is_wp_error( $response ) && ( strpos( $filecontents, '#SSL TEST PAGE#' ) !== false ) ) { $test = 'success'; } else { //.htaccess rewrite rule seems to be giving problems. $test = 'error'; } if ( empty( $filecontents ) ) { $test = 'no-response'; } set_transient( 'rsssl_htaccess_test_success', $test, 600 ); } if ( 'no-response' === $test || 'error' === $test ) { return false; } if ( 'success' === $test ) { return true; } } /** * Get an url with which we can test the SSL connection and htaccess redirect rules. * * @since 2.0 * * @access public * */ public function test_url() { $plugin_url = str_replace( 'http://', 'https://', trailingslashit( rsssl_url ) ); $https_home_url = str_replace( 'http://', 'https://', home_url() ); //in some case we get a relative url here, so we check that. //we compare to urls replaced to https, in case one of them is still on http. if ( ( strpos( $plugin_url, 'https://' ) === false ) && ( strpos( $plugin_url, $https_home_url ) === false ) ) { //make sure we do not have a slash at the start $plugin_url = ltrim( $plugin_url, '/' ); $plugin_url = trailingslashit( home_url() ) . $plugin_url; } //for subdomains or domain mapping situations, we have to convert the plugin_url from main site to the subdomain url. if ( is_multisite() && ! is_main_site( get_current_blog_id() ) && ! RSSSL()->multisite->is_multisite_subfolder_install() ) { $mainsiteurl = trailingslashit( str_replace( 'http://', 'https://', network_site_url() ) ); $home = trailingslashit( $https_home_url ); $plugin_url = str_replace( $mainsiteurl, $home, $plugin_url ); } return $plugin_url; } /** * @return bool * * Check if the .htaccess redirect is allowed on this setup * * @since 2.0 * */ public function htaccess_redirect_allowed() { if ( ( is_multisite() && ! $this->can_apply_networkwide() ) || $this->is_subdirectory_install() ) { return false; } if ( RSSSL()->server->uses_htaccess() ) { return true; } return false; } /** * @return bool * * Checks if the htaccess contains redirect rules, either actual redirect or a rsssl marker. * * @since 2.0 * */ public function htaccess_contains_redirect_rules():bool { if ( $this->htaccess_file_manager->validate_htaccess_file_path() === false ) { return false; } $pattern = '/RewriteRule \^\(\.\*\)\$ https:\/\/%{HTTP_HOST}(\/\$1|%{REQUEST_URI}) (\[R=301,.*L\]|\[L,.*R=301\])/i'; $htaccess = $this->htaccess_file_manager->get_htaccess_content(); return preg_match( $pattern, $htaccess ); } /** * @return bool * * Checks if a 301 redirect is set * this is the case if either the wp_redirect is set, or the htaccess redirect is set. * */ public function has_301_redirect() { if ( 'htaccess' === rsssl_get_option( 'redirect' ) || 'wp_redirect' === rsssl_get_option( 'redirect' ) ) { return true; } if ( $this->htaccess_contains_redirect_rules() && RSSSL()->server->uses_htaccess() ) { return true; } return false; } /** * returns list of recommended, but not active security headers for this site * returns empty array if no .htacces file exists * Uses cURL, fallback to .htaccess check upon cURL failure * @return array * * @since 4.0 * * @access public * */ public function get_recommended_security_headers() { $used_headers = array(); $not_used_headers = array(); $check_headers = apply_filters( 'rsssl_recommended_security_headers', array( array( 'name' => 'Upgrade Insecure Requests', 'pattern' => 'upgrade-insecure-requests', ), array( 'name' => 'X-XSS protection', 'pattern' => 'X-XSS-Protection', ), array( 'name' => 'X-Content Type Options', 'pattern' => 'X-Content-Type-Options', ), array( 'name' => 'Referrer-Policy', 'pattern' => 'Referrer-Policy', ), array( 'name' => 'Permissions-Policy', 'pattern' => 'Permissions-Policy', ), array( 'name' => 'HTTP Strict Transport Security', 'pattern' => 'Strict-Transport-Security', ), ) ); // cURL check. $curl_check_done = get_transient( 'rsssl_can_use_curl_headers_check' );//no, yes or false if ( ! $curl_check_done ) { //set a default set_transient( 'rsssl_can_use_curl_headers_check', 'no', WEEK_IN_SECONDS ); if ( function_exists( 'curl_init' ) ) { $url = get_site_url(); $ch = curl_init(); $headers = []; curl_setopt( $ch, CURLOPT_URL, $url ); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); curl_setopt( $ch, CURLOPT_TIMEOUT, 3 ); //timeout in seconds curl_setopt( $ch, CURLOPT_HEADERFUNCTION, function ( $curl, $header ) use ( &$headers ) { $len = strlen( $header ); $header = explode( ':', $header, 2 ); if ( count( $header ) < 2 ) { return $len; } $headers[ strtolower( trim( $header[0] ) ) ][] = trim( $header[1] ); return $len; } ); curl_exec( $ch ); // Check if any headers have been found if ( ! empty( $headers ) && is_array( $headers ) ) { // Loop through each header and check if it's one of the recommended security headers. If so, add to used_headers array. foreach ( $headers as $name => $value ) { foreach ( $check_headers as $check_header ) { // If the pattern occurs in either the header name or value, it's a security header. if ( stripos( $name, $check_header['pattern'] ) !== false || stripos( $value[0], $check_header['pattern'] ) !== false ) { // Prevent duplicate entries if ( ! in_array( $check_header['name'], $used_headers, true ) ) { $used_headers[] = $check_header['name']; } } } } // Now check which headers are unused. Compare the used headers against the $check_headers array. foreach ( $check_headers as $header ) { if ( in_array( $header['name'], $used_headers, true ) ) { // Header is used, do not add to unused array continue; } else { // Header is not used. Add to not used array $not_used_headers[] = $header['name']; } } $curl_check_done = $not_used_headers; } else { $curl_check_done = 'no'; } } else { $curl_check_done = 'no'; } set_transient( 'rsssl_can_use_curl_headers_check', $curl_check_done, WEEK_IN_SECONDS ); } if ( 'no' === $curl_check_done ) { if ( $this->htaccess_file_manager->validate_htaccess_file_path() && RSSSL()->server->uses_htaccess() ) { $htaccess = $this->htaccess_file_manager->get_htaccess_content(); foreach ( $check_headers as $check_header ) { if ( ! preg_match( '/' . $check_header['pattern'] . '/', $htaccess, $check ) ) { $not_used_headers[] = $check_header['name']; } } } } else { $not_used_headers = $curl_check_done; } return $not_used_headers; } /** * Check if the recommended headers are enabled * * @return bool */ public function recommended_headers_enabled() { $unused_headers = $this->get_recommended_security_headers(); if ( empty( $unused_headers ) ) { return true; } return false; } /** * Regenerate the wp rocket .htaccess rules */ public function maybe_flush_wprocket_htaccess( $field_id, $field_value, $prev_value, $field_type ) { if ( 'redirect' === $field_id && $field_value !== $prev_value && rsssl_user_can_manage() ) { if ( function_exists( 'flush_rocket_htaccess' ) ) { flush_rocket_htaccess(); } if ( function_exists( 'rocket_generate_config_file' ) ) { rocket_generate_config_file(); } } } /** * Check if the mixed content fixer is functioning on the front end, by scanning the source of the homepage for the fixer comment. * @since 2.2 * @access public * @return string */ public function mixed_content_fixer_detected() { //no need to check for the mixed content fixer if it's not enabled yet. if ( ! rsssl_get_option( 'mixed_content_fixer' ) ) { return 'not-enabled'; } //it's enabled, so check if we can find it on the front-end. $status = 0; $result = get_transient( 'rsssl_mixed_content_fixer_detected' ); if ( ! $result ) { $web_source = ''; //check if the mixed content fixer is active $response = wp_remote_get( home_url() ); if ( ! is_wp_error( $response ) ) { if ( is_array( $response ) ) { $status = wp_remote_retrieve_response_code( $response ); $web_source = wp_remote_retrieve_body( $response ); } if ( 200 !== $status ) { //Could not connect to website $result = 'no-response'; } elseif ( strpos( $web_source, 'data-rsssl=' ) === false ) { //Mixed content fixer marker not found in the websource $result = 'not-found'; } else { $result = 'found'; } } if ( is_wp_error( $response ) ) { //Fallback since most errors will be cURL errors, Error encountered while retrieving the webpage. $result = 'error'; $error = $response->get_error_message(); set_transient( 'rsssl_curl_error', $error, DAY_IN_SECONDS ); if ( ! empty( $error ) && ( strpos( $error, 'cURL error' ) !== false ) ) { $result = 'curl-error'; } } set_transient( 'rsssl_mixed_content_fixer_detected', $result, 600 ); } return 'found' === $result; } /** * Create redirect rules for the .htaccess. * @since 2.1 * * @access public * * @param bool $manual * * @return string */ public function get_redirect_rules( $manual = false ) { //ensure the configuration check has run always. if ( ! $this->configuration_loaded ) { $this->detect_configuration(); } //only add the redirect rules when a known type of SSL was detected. Otherwise, we use https. $rule = ''; //if the htaccess test was successfull, and we know the redirect type, edit if ( rsssl_get_option( 'ssl_enabled' ) && 'htaccess' === rsssl_get_option( 'redirect' ) && ( $manual || $this->htaccess_test_success() ) && 'NA' !== $this->ssl_type ) { $rule .= "\n" . '' . "\n"; $rule .= 'RewriteEngine on' . "\n"; if ( 'SERVER-HTTPS-ON' === $this->ssl_type ) { $rule .= 'RewriteCond %{HTTPS} !=on [NC]' . "\n"; } elseif ( 'SERVER-HTTPS-1' === $this->ssl_type ) { $rule .= 'RewriteCond %{HTTPS} !=1' . "\n"; } elseif ( 'LOADBALANCER' === $this->ssl_type ) { $rule .= 'RewriteCond %{HTTP_USER_AGENT} !lscache_runner [NC]' . "\n"; $rule .= 'RewriteCond %{HTTP:X-Forwarded-Proto} !https' . "\n"; } elseif ( 'HTTP_X_PROTO' === $this->ssl_type ) { $rule .= 'RewriteCond %{HTTP:X-Proto} !SSL' . "\n"; } elseif ( 'CLOUDFLARE' === $this->ssl_type ) { $rule .= "RewriteCond %{HTTP:CF-Visitor} '" . '"scheme":"http"' . "'" . "\n";//some concatenation to get the quotes right. } elseif ( 'SERVERPORT443' === $this->ssl_type ) { $rule .= 'RewriteCond %{SERVER_PORT} !443' . "\n"; } elseif ( 'CLOUDFRONT' === $this->ssl_type ) { $rule .= 'RewriteCond %{HTTP:CloudFront-Forwarded-Proto} !https' . "\n"; } elseif ( 'HTTP_X_FORWARDED_SSL_ON' === $this->ssl_type ) { $rule .= 'RewriteCond %{HTTP:X-Forwarded-SSL} !on' . "\n"; } elseif ( 'HTTP_X_FORWARDED_SSL_1' === $this->ssl_type ) { $rule .= 'RewriteCond %{HTTP:X-Forwarded-SSL} !=1' . "\n"; } elseif ( 'ENVHTTPS' === $this->ssl_type ) { $rule .= 'RewriteCond %{ENV:HTTPS} !=on' . "\n"; } //fastest cache compatibility if ( class_exists( 'WpFastestCache' ) ) { $rule .= 'RewriteCond %{REQUEST_URI} !wp-content\/cache\/(all|wpfc-mobile-cache)' . "\n"; } //Exclude .well-known/acme-challenge for Let's Encrypt validation if ( $this->has_acme_challenge_directory() ) { $rule .= 'RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/' . "\n"; } $rule .= 'RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]' . "\n"; $rule .= '' . "\n"; } $rule = apply_filters( 'rsssl_htaccess_output', $rule ); return preg_replace( "/\n+/", "\n", $rule ); } /** * * @return bool * since 3.1 * Check if .well-known/acme-challenge directory exists * @access public */ public function has_acme_challenge_directory() { if ( file_exists( "$this->abs_path.well-known/acme-challenge" ) ) { return true; } return false; } /** * * @return bool * since 3.1 * Check if there are already .well-known rules in .htaccess file * @access public * */ public function has_well_known_needle() { if ( $this->htaccess_file_manager->validate_htaccess_file_path() === false ) { return false; } $htaccess = $this->htaccess_file_manager->get_htaccess_content(); $well_known_needle = 'RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/'; return strpos( $htaccess, $well_known_needle ) !== false; } /** * Shows a notice, asking users for a review. */ public function show_leave_review_notice() { if ( defined( 'rsssl_pro' ) ) { return; } if ( rsssl_get_option( 'dismiss_all_notices' ) ) { return; } // update_option('rsssl_activation_timestamp', strtotime('-2 month'), false ); // rsssl_update_option('review_notice_shown', false); //prevent showing the review on edit screen, as gutenberg removes the class which makes it editable. $screen = get_current_screen(); if ( $screen && 'post' === $screen->base ) { return; } //this user has never had the review notice yet. if ( rsssl_get_option( 'ssl_enabled' ) && ! get_option( 'rsssl_activation_timestamp' ) ) { $month = rand( 0, 11 ); $trigger_notice_date = time() + $month * MONTH_IN_SECONDS; update_option( 'rsssl_activation_timestamp', $trigger_notice_date, false ); update_option( 'rsssl_before_review_notice_user', true, false ); } $reviewNoticeHasNotBeenShownBefore = (rsssl_get_option( 'review_notice_shown' ) === false ); $activationLongerThanOneMonthAgo = ( get_option( 'rsssl_activation_timestamp' ) && (get_option( 'rsssl_activation_timestamp' ) < strtotime( '-1 month' )) ); if ( $reviewNoticeHasNotBeenShownBefore && $activationLongerThanOneMonthAgo ) { //checking legacy options, just in case. $options = get_option( 'rlrsssl_options' ); if ( is_array( $options ) && isset( $options['review_notice_shown'] ) && $options['review_notice_shown'] ) { rsssl_update_option( 'review_notice_shown', true ); return; } add_action( 'admin_print_footer_scripts', array( $this, 'insert_dismiss_review' ) ); ?>
review-logo

', '' ) ); ?>

', '' ) ); ?>

- Rogier 'later']), 'rsssl_review_notice_action_later' ); $dismiss_url = wp_nonce_url( rsssl_admin_url(['rsssl_review_notice' => 'dismiss']), 'rsssl_review_notice_action_dismiss' ); ?>
base ) { return; } //don't show admin notices on our own settings page: we have the warnings there if ( $this->is_settings_page() ) { return; } $notices = $this->get_notices_list( array( 'admin_notices' => true ) ); if ( is_array( $notices ) ) { foreach ( $notices as $id => $notice ) { $notice = $notice['output']; if ( isset( $notice['msg'] ) ) { //if there is an open status, we change error to warning. $class = 'open' === $notice['status'] ? 'warning' : 'error'; $more_info = $notice['url'] ?? false; $logo = $notice['logo'] ?? false; $dismiss_id = isset( $notice['dismissible'] ) && $notice['dismissible'] ? $id : false; $dashboard_button = $notice['dashboard_button'] ?? false; echo $this->notice_html( $class . ' ' . $id, $notice['msg'], $more_info, $logo, $dismiss_id, $dashboard_button ); } } } } /** * Helper function to check if the wpconfig needs fixing * Used in notices * * @return bool */ public function wpconfig_siteurl_not_fixed() { return $this->wpconfig_siteurl_not_fixed; } /** * Helper function to check if the wpconfig needs fixing * Used in notices * * @return bool */ public function no_server_variable() { return $this->no_server_variable; } /** * Helper function to check if a site url has to be fixed * Used in notices * * @return bool */ public function do_wpconfig_loadbalancer_fix() { return $this->do_wpconfig_loadbalancer_fix; } /** * Clear the cached admin notices list * @return void */ public function clear_admin_notices_cache() { delete_option( 'rsssl_admin_notices' ); delete_option( 'rsssl_plusone_count' ); } /** * Get array of notices * - condition: function returning boolean, if notice should be shown or not * - callback: function, returning boolean or string, with multiple possible answers, and resulting messages and icons * @param array $args * @return array */ public function get_notices_list( $args = array() ) { if ( ! rsssl_user_can_manage() ) { return []; } $icon_labels = [ 'success' => __( 'Completed', 'really-simple-ssl' ), 'warning' => __( 'Warning', 'really-simple-ssl' ), 'open' => __( 'Open', 'really-simple-ssl' ), 'premium' => __( 'Upgrade', 'really-simple-ssl' ), ]; $defaults = array( 'admin_notices' => false, 'premium_only' => false, 'dismiss_on_upgrade' => false, 'status' => [ 'open', 'warning' ], //status can be "all" (all tasks, regardless of dismissed or open), "open" (not success/completed) or "completed" ); $args = wp_parse_args( $args, $defaults ); //ensure the status is an an array $args['status'] = is_array( $args['status'] ) ? $args['status'] : [ 'open', 'warning' ]; //if we're on the settings page, we need to clear the admin notices transient, because this list won't get refreshed otherwise if ( $this->is_settings_page() && ! get_option( 'rsssl_6_notice_dismissed' ) ) { update_option( 'rsssl_6_notice_dismissed', true, false ); } if ( ! $this->is_settings_page() ) { $cached_notices = get_option( 'rsssl_admin_notices' ); if ( 'empty' === $cached_notices ) { // Clear the invalid cache entry delete_option( 'rsssl_admin_notices' ); // Don't return empty array, continue to generate fresh notices } elseif ( false !== $cached_notices && is_array($cached_notices) ) { return $cached_notices; } } $rules = $this->get_redirect_rules( true ); if ( 'NA' !== $this->ssl_type ) { $arr_search = array( '<', '>', "\n" ); $arr_replace = array( '<', '>', '
' ); $rules = str_replace( $arr_search, $arr_replace, $rules ); $rules = substr( $rules, 4, -4 ); } else { $rules = __( 'No recommended redirect rules detected.', 'really-simple-ssl' ); } $rules = '
' . $rules . '
'; $notice_defaults = array( 'condition' => array(), 'callback' => false, ); $curl_error = get_transient( 'rsssl_curl_error' ); $current_plugin_folder = $this->get_current_rsssl_dirname(); //get expiry date, if we have one. $certinfo = get_transient( 'rsssl_certinfo' ); $end_date = $certinfo['validTo_time_t'] ?? false; $expiry_date = ! empty( $end_date ) ? gmdate( get_option( 'date_format' ), $end_date ) : __( '(unknown)', 'really-simple-ssl' ); $notices = array( 'load_balancer_fix' => array( 'condition' => [ 'NOT rsssl_ssl_enabled' ], 'callback' => 'RSSSL()->admin->do_wpconfig_loadbalancer_fix', 'score' => 30, 'output' => array( 'true' => array( 'msg' => __( 'Your wp-config.php has to be edited, but is not writable.', 'really-simple-ssl' ) . ' ' . __( 'Set your wp-config.php to writable and reload this page.', 'really-simple-ssl' ) . ' ' . __( 'To safely enable SSL on your server configuration, you should add the following line of code to your wp-config.php.', 'really-simple-ssl' ) . '
//Begin Really Simple Security Server variable fix
   $_SERVER["HTTPS"] = "on";
//END Really Simple Security

', 'icon' => 'warning', 'admin_notice' => true, 'plusone' => true, 'dismissible' => false, 'url' => 'knowledge-base/htaccess-wp-config-files-not-writable/', ), ), ), 'site_url_in_wpconfig' => array( 'condition' => [ 'NOT rsssl_ssl_enabled', 'NOT RSSSL()->admin->uses_bitnami' ], 'callback' => 'RSSSL()->admin->wpconfig_siteurl_not_fixed', 'score' => 30, 'output' => array( 'true' => array( 'msg' => __( 'A definition of a site url or home url was detected in your wp-config.php, but the file is not writable.', 'really-simple-ssl' ) . ' ' . __( 'Set your wp-config.php to writable and reload this page.', 'really-simple-ssl' ), 'icon' => 'warning', 'admin_notice' => true, 'plusone' => true, 'dismissible' => true, 'url' => 'knowledge-base/htaccess-wp-config-files-not-writable', ), ), ), 'deactivation_file_detected' => array( 'callback' => 'RSSSL()->admin->check_for_uninstall_file', 'score' => 30, 'output' => array( 'true' => array( 'msg' => __( "The 'force-deactivate.php' file has to be renamed to .txt. Otherwise your ssl can be deactivated by anyone on the internet.", 'really-simple-ssl' ) . ' ' . '' . __( 'Check again', 'really-simple-ssl' ) . '', 'icon' => 'warning', 'admin_notice' => true, 'plusone' => true, 'dismissible' => false, ), ), ), 'mixed_content_scan' => array( 'dismiss_on_upgrade' => true, 'condition' => array( 'rsssl_ssl_enabled' ), 'callback' => '_true_', 'score' => 5, 'output' => array( 'true' => array( 'url' => 'steps-after-activating-ssl', 'msg' => __( 'SSL is now activated. Follow the three steps in this article to check if your website is secure.', 'really-simple-ssl' ), 'icon' => 'open', 'dismissible' => true, 'plusone' => true, ), ), ), 'ssl_enabled' => array( 'callback' => 'rsssl_ssl_enabled', 'score' => 30, 'output' => array( 'true' => array( 'msg' => __( 'SSL is enabled on your site.', 'really-simple-ssl' ), 'icon' => 'success', ), 'false' => array( 'msg' => __( 'SSL is not enabled yet.', 'really-simple-ssl' ), 'title' => 'SSL', 'icon' => 'warning', 'plusone' => true, ), ), 'menu_id' => 'encryption', 'field_id' => 'redirect', ), 'ssl_detected' => array( 'condition' => array( 'NOT rsssl_ssl_detection_overridden', 'NOT RSSSL()->admin->uses_bitnami' ), 'callback' => 'rsssl_ssl_detected', 'score' => 30, 'output' => array( 'fail' => array( 'url' => 'wp-config-fix-needed', 'msg' => __( 'The wp-config.php file is not writable, and needs to be edited. Please set this file to writable.', 'really-simple-ssl' ), 'icon' => 'warning', ), 'no-ssl-detected' => array( 'title' => __( 'No SSL detected', 'really-simple-ssl' ), 'msg' => __( 'No SSL detected. Use the retry button to check again.', 'really-simple-ssl' ) . '
' . wp_nonce_field( 'rsssl_recheck_nonce', 'rsssl_recheck_nonce_field', true, false ) . '' . __( 'Install SSL certificate', 'really-simple-ssl' ) . '' . '
', 'icon' => 'warning', 'dismissible' => rsssl_get_option( 'ssl_enabled' ), ), 'no-response' => array( 'title' => __( 'Could not test certificate', 'really-simple-ssl' ), 'msg' => __( 'Automatic certificate detection is not possible on your server.', 'really-simple-ssl' ) . '
' . '' . __( 'Install SSL certificate', 'really-simple-ssl' ) . '' . '', 'icon' => 'warning', 'dismissible' => true, ), 'about-to-expire' => array( 'title' => __( 'Your SSL certificate will expire soon.', 'really-simple-ssl' ), 'msg' => // translators: %s is replaced with date. sprintf( __( 'SSL certificate will expire on %s.', 'really-simple-ssl' ), $expiry_date ) . ' ' . __( 'If your hosting provider auto-renews your certificate, no action is required. Alternatively, you have the option to generate an SSL certificate with Really Simple Security.', 'really-simple-ssl' ) . ' ' . // translators: %1$ and %2$s are replaced with the opening and closing tag with link. sprintf( __( 'Depending on your hosting provider, %1$smanual installation%2$s may be required.', 'really-simple-ssl' ), '', '' ) . '

' . __( 'Install SSL certificate', 'really-simple-ssl' ) . '' . ' 
', 'icon' => 'warning', ), ), ), 'mixed_content_fixer_detected' => array( 'condition' => array( 'rsssl_ssl_enabled' ), 'callback' => 'RSSSL()->admin->mixed_content_fixer_detected', 'score' => 10, 'output' => array( 'no-response' => array( 'url' => 'knowledge-base/how-to-fix-no-response-from-webpage-warning/', 'msg' => __( 'Really Simple Security has received no response from the webpage.', 'really-simple-ssl' ), 'icon' => 'open', 'dismissible' => true, 'plusone' => true, ), 'not-found' => array( 'url' => 'knowledge-base/how-to-check-if-the-mixed-content-fixer-is-active', 'msg' => __( 'The mixed content fixer is active, but was not detected on the frontpage.', 'really-simple-ssl' ), 'icon' => 'open', 'dismissible' => true, ), 'error' => array( 'msg' => __( 'Error occurred when retrieving the webpage.', 'really-simple-ssl' ), 'icon' => 'open', 'dismissible' => true, ), 'not-enabled' => array( 'highlight_field_id' => 'mixed_content_fixer', 'msg' => __( 'Mixed content fixer not enabled. Enable the option to fix mixed content on your site.', 'really-simple-ssl' ), 'icon' => 'open', 'dismissible' => true, ), 'curl-error' => array( 'url' => 'knowledge-base/curl-errors', 'msg' => // translators: %s is replaced with the error description. sprintf( __( "The mixed content fixer could not be detected due to a cURL error: %s. cURL errors are often caused by an outdated version of PHP or cURL and don't affect the front-end of your site. Contact your hosting provider for a fix.", 'really-simple-ssl' ), '' . $curl_error . '' ), 'icon' => 'open', 'dismissible' => true, ), ), ), 'wordpress_redirect' => array( 'condition' => array( 'rsssl_ssl_enabled', 'NOT RSSSL()->admin->htaccess_redirect_allowed' ), 'callback' => 'RSSSL()->admin->has_301_redirect', 'score' => 10, 'output' => array( 'true' => array( 'msg' => __( '301 redirect to https set.', 'really-simple-ssl' ), 'icon' => 'success', ), 'false' => array( 'msg' => __( 'No 301 redirect is set. Enable the WordPress 301 redirect in the settings to get a 301 permanent redirect.', 'really-simple-ssl' ), 'icon' => 'open', ), ), ), 'check_redirect' => array( 'condition' => array( 'rsssl_ssl_enabled', 'RSSSL()->admin->htaccess_redirect_allowed' ), 'callback' => 'RSSSL()->admin->redirect_status', 'score' => 10, 'show_with_options' => [ 'redirect', ], 'output' => array( 'htaccess-redirect-set' => array( 'title' => __( '301 .htaccess redirect', 'really-simple-ssl' ), 'msg' => __( 'The 301 redirect with .htaccess to HTTPS is now enabled.', 'really-simple-ssl' ), 'icon' => 'success', ), 'wp-redirect-to-htaccess' => array( 'highlight_field_id' => 'redirect', 'title' => __( '301 .htaccess redirect', 'really-simple-ssl' ), 'msg' => __( 'WordPress 301 redirect enabled. We recommend to enable a 301 .htaccess redirect.', 'really-simple-ssl' ), 'icon' => 'open', 'plusone' => RSSSL()->server->uses_htaccess(), 'dismissible' => true, ), 'no-redirect-set' => array( 'highlight_field_id' => 'redirect', 'msg' => __( 'Enable a .htaccess redirect or PHP redirect in the settings to create a 301 redirect.', 'really-simple-ssl' ), 'icon' => 'open', 'dismissible' => false, ), 'htaccess-rules-test-failed' => array( 'title' => __( '.htaccess redirect.', 'really-simple-ssl' ), 'url' => 'knowledge-base/manually-insert-htaccess-redirect-http-to-https', 'msg' => __( 'The .htaccess redirect rules selected by this plugin failed in the test. Set manually or dismiss to leave on PHP redirect.', 'really-simple-ssl' ) . $rules, 'icon' => 'warning', 'dismissible' => true, 'plusone' => true, ), ), ), 'elementor' => array( 'condition' => array( 'rsssl_ssl_activation_time_no_longer_then_3_days_ago' ), 'callback' => 'rsssl_uses_elementor', 'score' => 5, 'output' => array( 'true' => array( 'url' => 'knowledge-base/how-to-fix-mixed-content-in-elementor-after-moving-to-ssl', 'msg' => __( 'Your site uses Elementor. This can require some additional steps before getting the secure lock.', 'really-simple-ssl' ), 'icon' => 'open', 'dismissible' => true, ), ), ), 'divi' => array( 'condition' => array( 'rsssl_ssl_activation_time_no_longer_then_3_days_ago' ), 'callback' => 'rsssl_uses_divi', 'score' => 5, 'output' => array( 'true' => array( 'url' => 'knowledge-base/mixed-content-when-using-divi-theme/', 'msg' => __( 'Your site uses Divi. This can require some additional steps before getting the secure lock.', 'really-simple-ssl' ), 'icon' => 'open', 'dismissible' => true, ), ), ), 'recommended_security_headers_not_set' => array( 'callback' => 'RSSSL()->admin->recommended_headers_enabled', 'score' => 5, 'output' => array( 'false' => array( 'msg' => __( 'See which recommended security headers are not present on your website.', 'really-simple-ssl' ), 'icon' => 'premium', 'dismissible' => false, 'url' => add_query_arg( array( 'domain' => site_url() ), 'https://scan.really-simple-ssl.com' ), ), 'true' => array( 'msg' => __( 'Recommended security headers enabled.', 'really-simple-ssl' ), 'icon' => 'success', ), ), ), 'enable_two_fa' => array( 'callback' => 'option_login_protection_enabled', 'score' => 5, 'output' => array( 'false' => array( 'highlight_field_id' => 'login_protection_enabled', 'msg' => __( 'Implement Two-Factor Authentication or Passkey login.', 'really-simple-ssl' ), 'icon' => 'premium', 'url' => 'login-protection', ), ), ), 'enable_lla' => array( 'callback' => 'option_enable_limited_login_attempts', 'score' => 5, 'output' => array( 'false' => array( 'highlight_field_id' => 'enable_limited_login_attempts', 'msg' => __( 'Protect your login form with Limit Login Attempts.', 'really-simple-ssl' ), 'icon' => 'premium', 'url' => 'login-protection', ), ), ), 'enable_firewall' => array( 'callback' => 'option_enable_firewall', 'score' => 5, 'output' => array( 'false' => array( 'highlight_field_id' => 'enable_firewall', 'msg' => __( 'Protect your site with a performant Firewall.', 'really-simple-ssl' ), 'icon' => 'premium', 'url' => 'firewall', ), ), ), 'duplicate-ssl-plugins' => array( 'condition' => array( 'rsssl_detected_duplicate_ssl_plugin' ), 'callback' => '_true_', 'output' => array( 'true' => array( 'msg' => // translators: %s is replaced with the plugin name. sprintf( __( 'We have detected the %s plugin on your website.', 'really-simple-ssl' ), rsssl_detected_duplicate_ssl_plugin( true ) ) . ' ' . __( 'As Really Simple Security handles all the functionality this plugin provides, we recommend to disable this plugin to prevent unexpected behavior.', 'really-simple-ssl' ), 'icon' => 'warning', 'dismissible' => true, 'plusone' => true, ), ), ), 'bf_notice2023' => array( 'condition' => array( 'RSSSL()->admin->is_bf', ), 'callback' => '_true_', 'output' => array( 'true' => array( 'msg' => __( 'Black Friday sale! Get 40% Off Really Simple Security Pro', 'really-simple-ssl' ), 'icon' => 'premium', 'url' => 'pro', 'dismissible' => true, 'plusone' => true, ), ), ), 'email_verification_not_verified' => array( 'callback' => 'RSSSL()->mailer_admin->email_verification_completed', 'output' => array( 'false' => array( 'highlight_field_id' => 'notifications_email_address', 'msg' => __( "Complete email validation and enable notifications to make sure you will receive security warnings.", 'really-simple-ssl' ), 'icon' => 'open', 'admin_notice' => false, 'url' => 'instructions/email-verification', 'dismissible' => true, 'plusone' => true, ), 'true' => array( 'msg' => __( "Email address successfully verified.", 'really-simple-ssl' ), 'icon' => 'success', 'admin_notice' => false, 'url' => 'instructions/email-verification', 'dismissible' => true, 'plusone' => false, ), ), ), 'upgraded_to_nine' => array( 'condition' => array( 'rsssl_show_upgrade_to_nine_notice', ), 'callback' => '_true_', 'output' => array( 'true' => array( 'msg' => rsssl_upgrade_to_nine_notice(), 'icon' => 'open', 'admin_notice' => true, 'logo' => true, 'dashboard_button' => true, 'dismissible' => true, 'plusone' => true, ), ), ), 'pro_trial' => array( 'condition' => array( 'rsssl_show_pro_trial_notice', ), 'callback' => '_true_', 'output' => array( 'true' => array( 'msg' => rsssl_pro_trial_notice(), 'icon' => 'open', 'admin_notice' => true, 'logo' => true, 'dashboard_button' => true, 'dismissible' => true, 'plusone' => true, ), ), ), 'test_404s' => array( 'condition' => array( 'wp_option_rsssl_homepage_contains_404_resources', ), 'callback' => '_true_', 'output' => array( 'true' => array( 'msg' => __("404 errors detected on your homepage. 404 blocking is unavailable, to prevent blocking of legitimate visitors. It is strongly recommended to resolve these errors.", 'really-simple-ssl'), 'url' => '404-not-found-errors', 'icon' => 'warning', 'dismissible' => true, 'plusone' => false, 'clear_cache_id' => 'rsssl_homepage_contains_404_resources', ), ), ), ); //on multisite, don't show the notice on subsites. $notices = apply_filters( 'rsssl_notices', $notices ); foreach ( $notices as $id => $notice ) { $notices[ $id ] = wp_parse_args( $notice, $notice_defaults ); } /** * If a list of notices that should be dismissed on upgrade is requested */ if ( $args['dismiss_on_upgrade'] ) { $output = array(); foreach ( $notices as $key => $notice ) { if ( isset( $notice['dismiss_on_upgrade'] ) && $notice['dismiss_on_upgrade'] ) { $output[] = $key; } } return $output; } /** * Filter out notice that do not apply, or are dismissed */ $statuses = $args['status']; foreach ( $notices as $id => $notice ) { $func = $notice['callback']; $output = $this->validate_function( $func ); //check if all notices should be dismissed if ( isset( $notice['output'][ $output ]['dismissible'] ) && $notice['output'][ $output ]['dismissible'] && rsssl_get_option( 'dismiss_all_notices' ) ) { unset( $notices[ $id ] ); continue; } if ( ! isset( $notice['output'][ $output ] ) ) { unset( $notices[ $id ] ); continue; } if ( isset($notice['output'][ $output ]['url'] ) ) { $url = $notice['output'][ $output ]['url']; if ( strpos( $url, 'https://') ===false && strpos( $url, 'http://') === false ) { $notice['output'][ $output ]['url'] = rsssl_link($url); } } $notices[ $id ]['output'] = $notice['output'][ $output ]; $notices[ $id ]['output']['status'] = 'success' === $notices[ $id ]['output']['icon'] ? 'completed' : $notices[ $id ]['output']['icon']; if ( ! in_array( $notices[ $id ]['output']['status'], $statuses, true ) ) { unset( $notices[ $id ] ); continue; } $condition_functions = $notice['condition']; foreach ( $condition_functions as $func ) { $condition = $this->validate_function( $func, true ); if ( ! $condition ) { unset( $notices[ $id ] ); } } if ( isset( $notices[ $id ] ) ) { $notices[ $id ]['output']['label'] = $icon_labels[ $notices[ $id ]['output']['icon'] ]; } //only remove this option if it's both dismissed AND not completed. This way we keep completed notices in the list. if ( isset( $notices[ $id ] ) && get_option( 'rsssl_' . $id . '_dismissed' ) && 'completed' !== $notices[ $id ]['output']['status'] ) { unset( $notices[ $id ] ); } } //if only admin_notices are required, filter out the rest. if ( $args['admin_notices'] ) { foreach ( $notices as $id => $notice ) { if ( ! isset( $notice['output']['admin_notice'] ) || ! $notice['output']['admin_notice'] ) { unset( $notices[ $id ] ); } } } //sort notices in the order: warning, open, premium, success $warnings = array(); $open = array(); $premium = array(); $success = array(); $other = array(); foreach ( $notices as $key => $notice ) { if ( ! isset( $notice['output']['icon'] ) ) { $other[$key] = $notice; continue; } switch ( $notice['output']['icon'] ) { case 'warning': $warnings[$key] = $notice; break; case 'open': $open[$key] = $notice; break; case 'premium': $premium[$key] = $notice; break; case 'success': $success[$key] = $notice; break; default: $other[$key] = $notice; } } $notices = $warnings + $open + $premium + $success + $other; //if we only want a list of premium notices if ( $args['premium_only'] ) { foreach ( $notices as $key => $notice ) { if ( ! isset( $notice['output']['icon'] ) || 'premium' !== $notice['output']['icon'] ) { unset( $notices[ $key ] ); } } } // Check if the 'black_friday_notice' exists and move it to the top of the array if ( isset( $notices['bf_notice2023'] ) ) { // Remove the 'black_friday_notice' from its current position $black_friday_notice = array( 'bf_notice2023' => $notices['bf_notice2023'] ); unset( $notices['bf_notice2023'] ); // Add the 'black_friday_notice' back at the beginning of the array $notices = $black_friday_notice + $notices; } // // Check if the 'upgraded_to_nine' exists and move it to the top of the array // if ( isset( $notices['upgraded_to_nine'] ) ) { // // Remove the 'black_friday_notice' from its current position // $upgraded_to_nine_notice = array( 'upgraded_to_nine' => $notices['upgraded_to_nine'] ); // unset( $notices['upgraded_to_nine'] ); // // // Add the 'black_friday_notice' back at the beginning of the array // $notices = $upgraded_to_nine_notice + $notices; // } //ensure an empty list is also cached $cache_notices = empty( $notices ) ? 'empty' : $notices; //only cache if the admin_notices are retrieved. if ( $args['admin_notices'] ) { update_option( 'rsssl_admin_notices', $cache_notices ); } return $notices; } private function is_upgraded_to_6() { return get_option( 'rsssl_show_onboarding' ) && ! get_option( 'rsssl_6_notice_dismissed' ); } private function is_upgraded() { $previous_version = get_option( 'rsssl_previous_version' ); //if there's no first version yet, we assume it's not upgraded if ( ! $previous_version ) { return false; } //if the previous version is below current, we just upgraded. if ( version_compare( $previous_version, rsssl_version, '<' ) ) { return true; } return false; } /** * Get output of function, in format 'function', or 'class()->sub()->function' * @param string $func * @param bool $is_condition // if the check is a condition, which should return a boolean * @return string|bool */ private function validate_function( $func, $is_condition = false ) { if ( ! rsssl_user_can_manage() ) { return false; } $invert = false; if ( false !== strpos( $func, 'NOT ' ) ) { $func = str_replace( 'NOT ', '', $func ); $invert = true; } if ( false !== strpos( $func, 'wp_option_' ) ) { // False when: no option - option is bool false - option is string false $output = get_option( str_replace( 'wp_option_', '', $func ) ) !== false && get_option( str_replace( 'wp_option_', '', $func ) ) !== 'false'; } elseif ( false !== strpos( $func, 'option_' ) ) { $output = rsssl_get_option( str_replace( 'option_', '', $func ) ) == 1; //phpcs:ignore } elseif ( '_true_' === $func ) { $output = true; } elseif ( '_false_' === $func ) { $output = false; } else { if ( preg_match( '/(.*)\(\)\-\>(.*)->(.*)/i', $func, $matches ) ) { $base = $matches[1]; $class = $matches[2]; $function = $matches[3]; if ( property_exists($base(), $class) && $base()->{$class} && method_exists($base()->{$class}, $function) ) { $output = call_user_func(array($base()->{$class}, $function)); } else { $output = false; $this->log( $func . ' not found'); } } else { if ( !is_string($func) || !function_exists($func) ) { if ( defined('WP_DEBUG') && WP_DEBUG ) { error_log("missing function:"); error_log(print_r($func, true)); } return false; } $output = $func(); } } if ( $invert ) { $output = ! $output; } //stringyfy booleans if ( ! $is_condition ) { if ( false === $output || 0 === $output ) { $output = 'false'; } if ( true === $output || 1 === $output ) { $output = 'true'; } } return sanitize_text_field( $output ); } /** * Count the plusones. * * @return int * * @since 3.2 */ public function count_plusones() { if ( ! rsssl_user_can_manage() ) { return 0; } $cache = $this->is_settings_page() ? false : true; $count = get_option( 'rsssl_plusone_count' ); if ( ! $cache || false === $count ) { $count = 0; $notices = $this->get_notices_list(); if ( is_array( $notices ) ) { foreach ( $notices as $notice ) { $success = isset( $notice['output']['icon'] ) && 'success' === $notice['output']['icon']; if ( ! $success && isset( $notice['output']['plusone'] ) && $notice['output']['plusone'] ) { ++$count; } } } if ( 0 === $count ) { $count = 'empty'; } update_option( 'rsssl_plusone_count', $count ); } if ( 'empty' === $count ) { return 0; } return $count; } /** * Add some css for the settings page * * @since 2.0 * * @access public * */ public function enqueue_assets( $hook ) { if ( 'toplevel_page_really-simple-security' !== $hook ) { return; } //only on settings page $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min'; $rtl = is_rtl() ? 'rtl/' : ''; $url = trailingslashit( rsssl_url ) . "assets/css/{$rtl}admin{$min}.css"; $path = trailingslashit( rsssl_path ) . "assets/css/{$rtl}admin{$min}.css"; wp_enqueue_style( 'rsssl-css', $url, [ 'wp-components' ], filemtime( $path ) ); } /** * Check if wpconfig contains httponly cookie settings * * @return string */ public function secure_cookie_settings_status() { $wpconfig_path = $this->wpconfig_path(); if ( ! $wpconfig_path ) { return 'wpconfig-not-writable'; } $wpconfig = file_get_contents( $wpconfig_path ); if ( ( strpos( $wpconfig, '//Begin Really Simple Security session cookie settings' ) !== false ) || ( strpos( $wpconfig, 'cookie_httponly' ) !== false ) ) { return 'set'; } if ( ! is_writable( $wpconfig_path ) ) { return 'wpconfig-not-writable'; } return 'not-set'; } /** * Insert secure cookie settings */ public function insert_secure_cookie_settings() { if ( ! rsssl_user_can_manage() ) { return; } if ( ! $this->is_settings_page() ) { return; } //if multisite, only on network wide activated setups if ( is_multisite() && ! rsssl_is_networkwide_active() ) { return; } //only if cookie settings were not inserted yet if ( $this->secure_cookie_settings_status() !== 'set' ) { $wpconfig_path = $this->wpconfig_path(); if ( empty( $wpconfig_path ) ) { return; } $wpconfig = file_get_contents( $wpconfig_path ); if ( '' !== $wpconfig && is_writable( $wpconfig_path ) ) { $rule = "\n" . '//Begin Really Simple Security session cookie settings' . "\n"; $rule .= "@ini_set('session.cookie_httponly', true);" . "\n"; $rule .= "@ini_set('session.cookie_secure', true);" . "\n"; $rule .= "@ini_set('session.use_only_cookies', true);" . "\n"; $rule .= '//END Really Simple Security cookie settings' . "\n"; $insert_after = 'is_subdirectory_install() ) { $site_url = site_url(); $home_url = home_url(); $diff = str_replace( $home_url, '', $site_url ); $diff = trim( $diff, '/' ); $pos = strrpos( $path, $diff ); if ( false !== $pos ) { $path = substr_replace( $path, '', $pos, strlen( $diff ) ); $path = trim( $path, '/' ); $path = '/' . $path . '/'; } } return $path; } /** * Check if it's either a single site, or when multisite, network enabled. * @return bool */ public function can_apply_networkwide() { if ( ! is_multisite() ) { return true; } if ( is_multisite() && rsssl_is_networkwide_active() ) { return true; } return false; } /** * Detects if WordPress is running in a subdirectory/subfolder install. * * A subfolder install means the site is accessed via example.com/folder/ * rather than directly at the domain root (example.com). * * @return bool True if running in a subfolder, false if at domain root */ protected function is_subdirectory_install() { $url = home_url(); $parts = wp_parse_url( $url ); $path = isset( $parts['path'] ) ? trim( $parts['path'], '/' ) : ''; // If path is empty, we're at root; otherwise we're in a subdirectory return ( $path !== '' ); } /** * Retrieve the contents of the test page * * @return string * */ protected function get_test_page_contents() { $filecontents = get_transient( 'rsssl_testpage' ); if ( ! $filecontents ) { $testpage_url = trailingslashit( $this->test_url() ) . 'ssl-test-page.php'; $response = wp_remote_get( $testpage_url ); if ( is_array( $response ) ) { $filecontents = wp_remote_retrieve_body( $response ); } if ( empty( $filecontents ) ) { $filecontents = 'not-valid'; } set_transient( 'rsssl_testpage', $filecontents, DAY_IN_SECONDS ); } return $filecontents; } /** * Determine dirname to show in admin_notices() in really-simple-ssl-pro.php to show a warning when free folder has been renamed * * @return string * * since 3.1 * */ public function get_current_rsssl_dirname() { return basename( __DIR__ ); } /** * Determine the htaccess file. This can be either the regular .htaccess file, or an htaccess.conf file on bitnami installations. * * since 3.1 * * @return string */ public function htaccess_file() { if ( $this->uses_htaccess_conf() ) { $htaccess_file = realpath( dirname( ABSPATH ) . '/conf/htaccess.conf' ); } else { $htaccess_file = $this->abs_path . '.htaccess'; } return $htaccess_file; } /** * Check the current redirect status * * @return string */ public function redirect_status() { if ( ! RSSSL()->admin->has_301_redirect() ) { return 'no-redirect-set'; } if ( RSSSL()->admin->has_301_redirect() && RSSSL()->server->uses_htaccess() && RSSSL()->admin->htaccess_contains_redirect_rules() ) { return 'htaccess-redirect-set'; } if ( $this->can_apply_networkwide() && ! RSSSL()->admin->htaccess_contains_redirect_rules() && rsssl_get_option( 'redirect' ) === 'wp_redirect' ) { return 'wp-redirect-to-htaccess'; } if ( rsssl_get_option( 'redirect' ) === 'htaccess' && ! RSSSL()->admin->htaccess_test_success() && $this->can_apply_networkwide() ) { return 'htaccess-rules-test-failed'; } return 'default'; } /** * @return void * * Update branding in .htaccess, wp-config.php */ public function update_branding_in_files() { $this->maybe_update_branding_in_htaccess(); $this->maybe_update_branding_in_wp_config(); } /** * @return void * * Update branding in .htaccess. Load .htaccess if it exists and is writable, replace branding, write back */ public function maybe_update_branding_in_htaccess(): void { if ( $this->htaccess_file_manager->validate_htaccess_file_path() === false ) { return; } $htaccess = $this->htaccess_file_manager->get_htaccess_content(); if ( strpos( $htaccess, 'Really Simple SSL' ) !== false ) { $updated = str_replace( 'Really Simple SSL', 'Really Simple Security', $htaccess ); if ( $updated !== $htaccess ) { error_log( 'Updating branding in .htaccess' ); file_put_contents( $this->htaccess_file(), $updated ); } } } /** * @return void * * Update branding in .htaccess. Load .htaccess if it exists and is writable, replace branding, write back */ public function maybe_update_branding_in_wp_config() { if ( $this->wpconfig_is_writable() ) { $wp_config = file_get_contents( $this->wpconfig_path() ); if ( strpos( $wp_config, 'Really Simple SSL' ) !== false ) { str_replace( "Really Simple SSL", "Really Simple Security", $wp_config ); } file_put_contents( $this->wpconfig_path(), $wp_config ); } } /** * Delete the permission detection options when the setting is disabled. * * @param string $field_id * @param mixed $field_value * @param mixed $prev_value * @param string $field_type * * @return void */ public function maybe_delete_permission_detection_option( string $field_id, $field_value, $prev_value, $field_type ): void { if ( ! rsssl_user_can_manage() ) { return; } if ( $field_value === $prev_value ) { return; } if ( $field_id === 'permission_detection' ) { // If permission_detection option is disabled, reset options if ( ! $field_value ) { delete_option( 'rsssl_permission_check_completed' ); delete_option( 'rsssl_files_with_wrong_permissions' ); delete_option( 'rsssl_permission_check_next_index' ); } } } /** * Activates all recommended security features for Really Simple Security. * * This includes enabling the vulnerability scanner, WordPress hardening options, * login protection, mixed content fixer, and additional premium features if the * PRO version is active. It is safe to call this method from both CLI and admin contexts. * * @since 9.3.4 * */ public function activate_recommended_features() { // Activate Vulnerability Scanner rsssl_update_option( 'enable_vulnerability_scanner', true ); if (isset(RSSSL()->settingsConfigService)) { // Activate essential WordPress hardening features $recommended_hardening_fields = RSSSL()->settingsConfigService->getRecommendedHardeningSettings(); foreach ( $recommended_hardening_fields as $field ) { rsssl_update_option( $field, true ); } } // Enable Email login protection rsssl_update_option( 'login_protection_enabled', true ); // Enable Mixed Content Fixer rsssl_update_option( 'mixed_content_fixer', true ); // Check if PRO version is active, then activate premium features if ( defined( 'rsssl_pro' ) ) { // Enable Two-Factor Authentication for administrator role rsssl_update_option( 'two_fa_enabled_roles_totp', [ 'administrator' ] ); // Enable Limit Login Attempts rsssl_update_option( 'enable_limited_login_attempts', true ); // Enable firewall rsssl_update_option( 'enable_firewall', true ); rsssl_update_option( 'event_log_enabled', true ); // Enable advanced security headers $security_headers = [ 'upgrade_insecure_requests', 'x_content_type_options', 'hsts', 'x_xss_protection' => 'zero', 'x_frame_options' => 'SAMEORIGIN', 'referrer_policy' => 'strict-origin-when-cross-origin', 'csp_frame_ancestors' => 'self', ]; foreach ( $security_headers as $header_key => $header_value ) { if ( is_string( $header_key ) ) { rsssl_update_option( $header_key, $header_value ); } else { rsssl_update_option( $header_value, true ); } } // Activate password security enforcement rsssl_update_option( 'enforce_password_security_enabled', true ); rsssl_update_option( 'enable_hibp_check', true ); } do_action('rsssl_update_rules'); } /** * @param $data * * @return array * * Clear 404 test cache */ public function clear_404_test_cache( $data ) { if ( ! rsssl_user_can_manage() ) { return []; } $cache_id = sanitize_title($data['cache_id']); if ( 'rsssl_homepage_contains_404_resources' !== $cache_id ) { return []; } delete_option( 'rsssl_homepage_contains_404_resources' ); delete_option( 'rsssl_404_resources_to_check' ); $this->clear_admin_notices_cache(); return []; } /** * Method detects if permalink structure changed from non-plain to plain, * and if so: disable the custom login url feature and set a flag in the * database to trigger an admin notice by * {@see rsssl_premium_security_notices}. To trigger the admin notice * properly we also have to clear the admin notices cache. */ public function check_permalink_change_for_custom_login_url( $old_value, $new_value ) { if ( ! rsssl_user_can_manage() ) { return; } $was_plain = empty( $old_value ); $is_plain = empty( $new_value ); if ( ! $was_plain && $is_plain && rsssl_get_option( 'change_login_url_enabled' ) == 1 ) { rsssl_update_option( 'change_login_url_enabled', false ); update_option( 'rsssl_permalink_changed_to_plain', true, false ); $this->clear_admin_notices_cache(); } if ( $was_plain && ! $is_plain ) { delete_option( 'rsssl_permalink_changed_to_plain' ); $this->clear_admin_notices_cache(); } } /** * Wrapper function that delegates the onboarding reset to the global onboardingService * * @return void */ public function reset_onboarding() { if ( class_exists( '\ReallySimplePlugins\RSS\Core\Services\GlobalOnboardingService' ) ) { $globalOnboardingService = new \ReallySimplePlugins\RSS\Core\Services\GlobalOnboardingService(); $globalOnboardingService->resetOnboarding(); } } } //class closure if ( ! function_exists( 'rsssl_ssl_enabled' ) ) { function rsssl_ssl_enabled() { return boolval( rsssl_get_option( 'ssl_enabled' ) ); } } if ( ! function_exists( 'rsssl_ssl_detected' ) ) { function rsssl_ssl_detected() { if ( ! RSSSL()->admin->wpconfig_ok() ) { return apply_filters( 'rsssl_ssl_detected', 'fail' ); } $valid = RSSSL()->certificate->is_valid(); if ( ! $valid ) { if ( ! function_exists( 'stream_context_get_params' ) || RSSSL()->certificate->detection_failed() ) { return apply_filters( 'rsssl_ssl_detected', 'no-response' ); } else { return apply_filters( 'rsssl_ssl_detected', 'no-ssl-detected' ); } } else { $about_to_expire = RSSSL()->certificate->about_to_expire(); if ( ! $about_to_expire ) { return apply_filters( 'rsssl_ssl_detected', 'ssl-detected' ); } else { return apply_filters( 'rsssl_ssl_detected', 'ssl-detected' ); } } } } if ( ! function_exists( 'rsssl_uses_elementor' ) ) { function rsssl_uses_elementor() { return ( defined( 'ELEMENTOR_VERSION' ) || defined( 'ELEMENTOR_PRO_VERSION' ) ); } } if ( ! function_exists( 'rsssl_uses_divi' ) ) { function rsssl_uses_divi() { return defined( 'ET_CORE_PATH' ); } } if ( ! function_exists( 'rsssl_uses_wp_engine' ) ) { function rsssl_uses_wp_engine() { if ( function_exists( 'is_wpe' ) && is_wpe() ) { return true; } return false; } } if ( ! function_exists( 'rsssl_beta_5_addon_active' ) ) { function rsssl_beta_5_addon_active() { if ( defined( 'rsssl_beta_addon' ) && rsssl_beta_addon ) { return true; } return false; } } if ( ! function_exists( 'rsssl_ssl_activation_time_no_longer_then_3_days_ago' ) ) { function rsssl_ssl_activation_time_no_longer_then_3_days_ago() { $activation_time = get_option( 'rsssl_activation_timestamp' ); $three_days_after_activation = $activation_time + 3 * DAY_IN_SECONDS; if ( time() < $three_days_after_activation ) { return true; } else { return false; } } } if ( ! function_exists( 'rsssl_letsencrypt_wizard_url' ) ) { /** * Get link to SSL certificate generation page * * @param string $page * * @return string */ function rsssl_letsencrypt_wizard_url( $page = '' ) { if ( ! empty( $page ) ) { $page = '/' . $page; } if ( is_multisite() && ! is_main_site() ) { return add_query_arg( array( 'page' => 'really-simple-security', 'letsencrypt' => 1, ), get_admin_url( get_main_site_id(), 'options-general.php' ) ) . "#letsencrypt$page"; } else { return add_query_arg( array( 'page' => 'really-simple-security', 'letsencrypt' => 1, ), admin_url( 'options-general.php' ) ) . "#letsencrypt$page"; } } } if ( ! function_exists('rsssl_plain_permalinks_enabled')) { function rsssl_plain_permalinks_enabled() { return ! get_option( 'permalink_structure' ); } } if ( ! function_exists( 'rsssl_detected_duplicate_ssl_plugin' ) ) { /** * Duplicate functionality test * * @param string $return_name * * @return bool|string */ function rsssl_detected_duplicate_ssl_plugin( $return_name = false ) { $plugin = false; if ( defined( 'WPLE_PLUGIN_VERSION' ) ) { $plugin = 'WP Encryption'; } elseif ( defined( 'WPSSL_VER' ) ) { $plugin = 'WP Free SSL'; } elseif ( defined( 'SSL_ZEN_PLUGIN_VERSION' ) ) { $plugin = 'SSL Zen'; } elseif ( defined( 'WPSSL_VER' ) ) { $plugin = 'WP Free SSL'; } elseif ( defined( 'SSLFIX_PLUGIN_VERSION' ) ) { $plugin = 'SSL Insecure Content Fixer'; } elseif ( class_exists( 'OCSSL', false ) ) { $plugin = 'One Click SSL'; } elseif ( class_exists( 'JSM_Force_SSL', false ) ) { $plugin = "JSM's Force HTTP to HTTPS (SSL)"; } elseif ( function_exists( 'httpsrdrctn_plugin_init' ) ) { $plugin = 'Easy HTTPS (SSL) Redirection'; } elseif ( defined( 'WPSSL_VER' ) ) { $plugin = 'WP Free SSL'; } elseif ( defined( 'WPFSSL_OPTIONS_KEY' ) ) { $plugin = 'WP Force SSL'; } elseif ( defined( 'ESSL_REQUIRED_PHP_VERSION' ) ) { $plugin = 'EasySSL'; } if ( false !== $plugin && ! $return_name ) { return true; } return $plugin; } } if ( ! function_exists( 'rsssl_ssl_detection_overridden' ) ) { function rsssl_ssl_detection_overridden() { return get_option( 'rsssl_ssl_detection_overridden' ) !== false; } } if ( ! function_exists('maybe_disable_frame_ancestors_url_field' ) ) { function maybe_disable_frame_ancestors_url_field() { if ( rsssl_get_option( 'csp_frame_ancestors' ) === 'disabled' ) { return true; } return false; } } /** * Upgraded to nine notice text */ if ( ! function_exists('rsssl_upgrade_to_nine_notice' ) ) { function rsssl_upgrade_to_nine_notice() { $link = rsssl_link('our-journey-towards-really-simple-security'); $msg = sprintf( /* translators: %1$s: opening bold tag, %2$s: closing bold tag */ __( 'Really Simple SSL is now %1$sReally Simple Security!%2$s', 'really-simple-ssl' ), '', '' ) . "
" . sprintf( "%s", $link, __( "Read about our journey towards Really Simple Security", 'really-simple-ssl' ) ); if ( ! defined( 'rsssl_pro' ) ) { $link = rsssl_link( 'checkout/?edd_action=add_to_cart&download_id=860&edd_options%5Bprice_id%5D=1¤cy=EUR', 'notification', 'free', 'REALLYSIMPLESECURITY' ); $msg .= "
" . sprintf( __( "Experience all powerful features of Really Simple Security Pro using this %slimited time discount%s: %s", 'really-simple-ssl' ), '', '', "REALLYSIMPLESECURITY" ); } return $msg; } } /** * RSSSL Pro Trial Notice */ if ( ! function_exists('rsssl_pro_trial_notice' ) ) { function rsssl_pro_trial_notice() { $link = rsssl_link( 'checkout/?edd_action=add_to_cart&download_id=860&edd_options%5Bprice_id%5D=4¤cy=EUR', 'notification', 'free' ); $msg = sprintf( __( "Thank you for being a long-time user! As a token of our gratitude, we want to offer you %s6 months Really Simple Security Pro, 100%% Free!%s", 'really-simple-ssl' ), '', '', ); $msg .= "

" . __('Discover:', 'really-simple-ssl'); $msg .= "
"; $msg .= "" . __('Claim your 6 Free months', 'really-simple-ssl') . ""; return $msg; } } /** * Determine whether to show Pro trial notice * */ if (!function_exists('rsssl_show_pro_trial_notice')) { function rsssl_show_pro_trial_notice() { if (defined('rsssl_pro')) { return false; } $activation_timestamp = get_option('rsssl_activation_timestamp'); // If activation timestamp is more than one year ago, show the notice if ($activation_timestamp && time() - $activation_timestamp > YEAR_IN_SECONDS) { return true; } return false; } } /** * Do not show notice after 2024-10-01 */ if ( ! function_exists('rsssl_show_upgrade_to_nine_notice' ) ) { function rsssl_show_upgrade_to_nine_notice() { if ( time() >= strtotime('2024-10-01') ) { return false; } return true; } }