PluginProbe ʕ •ᴥ•ʔ
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) / 9.5.10.1
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) v9.5.10.1
9.5.11 9.5.10.1 9.5.10 trunk 9.4.0 9.4.1 9.4.2 9.4.3 9.5.0 9.5.0.1 9.5.0.2 9.5.1 9.5.2 9.5.2.2 9.5.2.3 9.5.3 9.5.3.1 9.5.3.2 9.5.4 9.5.5 9.5.6 9.5.7 9.5.8 9.5.9
really-simple-ssl / functions.php
really-simple-ssl Last commit date
assets 1 month ago core 1 month ago languages 1 month ago lets-encrypt 1 month ago lib 1 month ago mailer 1 month ago modal 1 month ago placeholders 1 month ago progress 1 month ago security 1 month ago settings 1 month ago testssl 1 month ago upgrade 1 month ago .wp-env.json 1 month ago SECURITY.md 1 month ago class-admin.php 1 month ago class-cache.php 1 month ago class-certificate.php 1 month ago class-front-end.php 1 month ago class-installer.php 1 month ago class-mixed-content-fixer.php 1 month ago class-multisite.php 1 month ago class-server.php 1 month ago class-site-health.php 1 month ago class-wp-cli.php 1 month ago compatibility.php 1 month ago force-deactivate.txt 1 month ago functions.php 1 month ago index.php 1 month ago readme.txt 1 month ago rector.php 1 month ago rlrsssl-really-simple-ssl.php 1 month ago rsssl-auto-loader.php 1 month ago sbom.json.gz 1 month ago ssl-test-page.php 1 month ago system-status.php 1 month ago uninstall.php 1 month ago upgrade.php 1 month ago
functions.php
553 lines
1 <?php
2 defined( 'ABSPATH' ) or die();
3 /**
4 * Only functions also required on front-end here
5 */
6
7 /**
8 * Get a Really Simple Security option by name
9 *
10 * @param string $name The name of the option to retrieve.
11 * @param mixed $default_value The default value to return if the option does not exist.
12 *
13 * @return mixed
14 */
15
16 if (!function_exists('rsssl_get_option')) {
17 function rsssl_get_option( string $name, $default_value = false ) {
18 $name = sanitize_title( $name );
19 if ( is_multisite() && rsssl_is_networkwide_active() ) {
20 $options = get_site_option( 'rsssl_options', [] );
21 } else {
22 $options = get_option( 'rsssl_options', [] );
23 }
24
25 //fallback, will be removed after 6.2
26 //because we only check if the option is not saved in the new style, this if should normally never get executed.
27 if (
28 ! isset( $options[ $name ] ) &&
29 ( 'ssl_enabled' === $name || 'redirect' === $name || 'mixed_content_fixer' === $name || 'dismiss_all_notices' === $name )
30 ) {
31 $options = rsssl_get_legacy_option( $options, $name );
32 }
33
34 $value = $options[ $name ] ?? false;
35 if ( false === $value && false !== $default_value ) {
36 $value = $default_value;
37 }
38
39 if ( 1 === $value ) {
40 $value = true;
41 }
42
43 return apply_filters( "rsssl_option_$name", $value, $name );
44 }
45 }
46
47 /**
48 * Check if we should treat the plugin as networkwide or not.
49 * Note that this function returns false for single sites! Always use icw is_multisite()
50 *
51 * @return bool
52 */
53 if (!function_exists('rsssl_is_networkwide_active')) {
54 function rsssl_is_networkwide_active() {
55 if ( ! is_multisite() ) {
56 return false;
57 }
58 if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
59 require_once ABSPATH . '/wp-admin/includes/plugin.php';
60 }
61
62 if ( is_plugin_active_for_network( rsssl_plugin ) ) {
63 return true;
64 }
65
66 return false;
67 }
68 }
69
70 /**
71 * if the option is does not exist in our new array, check if it's available in the old option. If so, use that one
72 * @deprecated to be used until 6.2, as fallback for failed upgrades in some specific edge case situations
73 * @param array|bool $options
74 * @param string $name
75 *
76 * @return array
77 */
78 if (!function_exists('rsssl_get_legacy_option')) {
79 function rsssl_get_legacy_option( $options, string $name ): array {
80 $old_options = is_multisite() ? get_site_option( 'rlrsssl_network_options' ) : get_option( 'rlrsssl_options' );
81 $options = [];
82
83 if ( $old_options ) {
84 if ( 'ssl_enabled' === $name && isset( $old_options['ssl_enabled'] ) ) {
85 $options['ssl_enabled'] = $old_options['ssl_enabled'];
86 } elseif ( 'dismiss_all_notices' === $name && isset( $old_options['dismiss_all_notices'] ) ) {
87 $options['dismiss_all_notices'] = $old_options['dismiss_all_notices'];
88 } elseif ( 'dismiss_all_notices' === $name && isset( $old_options['dismiss_all_notices'] ) ) {
89 $options['dismiss_all_notices'] = $old_options['dismiss_all_notices'];
90 } elseif ( 'mixed_content_fixer' === $name && isset( $old_options['autoreplace_insecure_links'] ) ) {
91 $options['mixed_content_fixer'] = $old_options['autoreplace_insecure_links'];
92 } elseif ( 'redirect' === $name ) {
93 if ( isset( $old_options['htaccess_redirect'] ) && $old_options['htaccess_redirect'] ) {
94 $options['redirect'] = 'htaccess';
95 } elseif ( isset( $old_options['wp_redirect'] ) && $old_options['wp_redirect'] ) {
96 $options['redirect'] = 'wp_redirect';
97 }
98 }
99 }
100 return $options;
101 }
102 }
103
104 if (!function_exists('rsssl_check_if_email_essential_feature')) {
105 function rsssl_check_if_email_essential_feature() {
106 $essential_features = array(
107 'limit_login_attempts' => rsssl_get_option( 'enable_limited_login_attempts' ) == 1,//phpcs:ignore
108 'login_protection_enabled' => rsssl_get_option( 'login_protection_enabled' ) == 1,//phpcs:ignore
109 );
110
111 // Check if the current feature is in the essential features array
112 foreach ( $essential_features as $feature => $is_essential ) {
113 if ( $is_essential ) {
114 return true;
115 }
116 }
117
118 return false;
119 }
120 }
121
122 /**
123 * Retrieves the path to a template file.
124 *
125 * @param string $template The name of the template to retrieve.
126 * @param string $path (Optional) The path to look for the template file. If not specified, the default path will be used.
127 *
128 * @return string The full path to the template file.
129 * @throws \RuntimeException Throws a runtime exception if the template file cannot be found.
130 */
131 if (!function_exists('rsssl_get_template')) {
132 function rsssl_get_template( string $template, string $path = '' ): string {
133 // Define the path in the theme where templates can be overridden.
134 $theme_template_path = get_stylesheet_directory() . '/really-simple-ssl-templates/' . $template;
135
136 // Check if the theme has an override for the template.
137 if ( file_exists( $theme_template_path ) ) {
138 return $theme_template_path;
139 }
140 // If $path is not set, use the default path
141 if ( $path === '' ) {
142 $path = rsssl_path . 'templates/'; // Remember this only works in free version, for pro we need to add the $path parameter/argument
143 } else {
144 // Ensure the path ends with a slash
145 $path = trailingslashit( $path );
146 }
147
148 // Full path to the template file
149 $full_path = $path . $template;
150
151 // Check if the template exists in the specified path.
152 if ( ! file_exists( $full_path ) ) {
153 throw new \RuntimeException( 'Template not found: ' . $full_path );
154 }
155
156 return $full_path;
157 }
158 }
159
160 /**
161 * Loads a template file and includes it.
162 *
163 * @param string $template The name of the template to load.
164 * @param array $vars (Optional) An associative array of variables to make available in the template scope.
165 * @param string $path (Optional) The path to look for the template file. If not specified, the default path will be used.
166 *
167 * @return void
168 * @throws Exception Throws an exception if the template file cannot be found.
169 */
170 if (!function_exists('rsssl_load_template')) {
171 function rsssl_load_template( string $template, array $vars = array(), string $path = '' ) {
172 // Extract variables to be available in the template scope.
173 if ( is_array( $vars ) ) {
174 extract( $vars );
175 }
176
177 // Get the template file, checking for theme overrides.
178 $template_file = rsssl_get_template( $template, $path );
179
180 // Include the template file.
181 include $template_file;
182 }
183 }
184
185 /**
186 * Determines the path to WordPress configuration file (wp-config.php)
187 *
188 * This function attempts to locate the wp-config.php file in the following order:
189 * 1. Checks for a filtered path via 'rsssl_wpconfig_path' filter
190 * 2. Looks in the WordPress installation root directory (ABSPATH)
191 * 3. Looks in the parent directory of the WordPress installation
192 *
193 * @return string The full path to wp-config.php if found, empty string otherwise
194 *
195 * @filter rsssl_wpconfig_path Allows modification of the wp-config.php path
196 *
197 * @example
198 * // Get wp-config.php path
199 * $config_path = rsssl_wpconfig_path();
200 *
201 * // Filter example
202 * add_filter('rsssl_wpconfig_path', function($path) {
203 * return '/custom/path/to/wp-config.php';
204 * });
205 */
206 if ( ! function_exists( 'rsssl_wpconfig_path' ) ) {
207 function rsssl_wpconfig_path(): string {
208 // Allow the wp-config.php path to be overridden via a filter.
209 $filtered_path = apply_filters( 'rsssl_wpconfig_path', '' );
210
211 // If a filtered path is provided and valid, use it.
212 if ( ! empty( $filtered_path ) && file_exists( $filtered_path ) ) {
213 return $filtered_path;
214 }
215
216 // Default behavior to locate wp-config.php
217 $location_of_wp_config = ABSPATH;
218 if ( ! file_exists( ABSPATH . 'wp-config.php' ) && file_exists( dirname( ABSPATH ) . '/wp-config.php' ) ) {
219 $location_of_wp_config = dirname( ABSPATH );
220 }
221
222 $location_of_wp_config = trailingslashit( $location_of_wp_config );
223 $wpconfig_path = $location_of_wp_config . 'wp-config.php';
224
225 // Check if the file exists and return the path if valid.
226 if ( file_exists( $wpconfig_path ) ) {
227 return $wpconfig_path;
228 }
229
230 // Return an empty string if no valid wp-config.php path is found.
231 return '';
232 }
233 }
234 /**
235 * @return void
236 *
237 * Set encryption keys
238 */
239 if ( ! function_exists('rsssl_set_encryption_key')) {
240 function rsssl_set_encryption_key(): void {
241
242 // Return if the key has already been defined
243 if ( defined( 'RSSSL_KEY' ) ) {
244 return;
245 }
246
247 $key = get_site_option( 'rsssl_main_key' );
248 $wp_config_path = rsssl_wpconfig_path();
249
250 // If we have a DB key, and the wp-config.php is not writable, return
251 if ( $key && ! is_writable( $wp_config_path ) ) {
252 return;
253 }
254
255 $new_generated = false;
256
257 // If we don't have a key, generate one
258 if ( ! $key ) {
259 // Ensure wp_generate_password() is available
260 if ( ! function_exists( 'wp_generate_password' ) ) {
261 require_once ABSPATH . WPINC . '/pluggable.php';
262 }
263 $new_generated = true;
264 $key = wp_generate_password( 64, false );
265 }
266
267 if ( is_writable( $wp_config_path ) ) {
268 // Add the key to the wp-config file
269 $rule = "//Begin Really Simple Security key\n";
270 $rule .= "define('RSSSL_KEY', '" . $key . "');\n";
271 $rule .= "//END Really Simple Security key\n";
272 $insert_after = '<?php';
273
274 $contents = file_get_contents( $wp_config_path );
275 $pos = strpos( $contents, $insert_after );
276 if ( false !== $pos && strpos( $contents, 'RSSSL_KEY' ) === false ) {
277 $contents = substr_replace( $contents, $rule, $pos + 1 + strlen( $insert_after ), 0 );
278 file_put_contents( $wp_config_path, $contents, LOCK_EX );
279
280 // Define the constant for the current request. wp-config.php
281 // won't be re-parsed until next request.
282 if ( ! defined( 'RSSSL_KEY' ) ) {
283 define( 'RSSSL_KEY', $key );
284 }
285 }
286
287 // If the wp-config was just set to writable, we can delete the key from the database now.
288 delete_site_option( 'rsssl_main_key' );
289 } elseif ( $new_generated ) {
290 // If we can't write to the wp-config file, store the key in the database
291 // When wp-config is set to writable, auto upgrade to constant
292 update_site_option( 'rsssl_main_key', $key, false );
293 }
294
295 update_site_option( 'rsssl_encryption_keys_set', true );
296 }
297 rsssl_set_encryption_key();
298 }
299
300 if ( ! function_exists( 'rsssl_deactivate_alternate' ) ) {
301 /**
302 * Deactivate the alternate version if active. This function is included in
303 * both the pro and free plugin and should be used to deactivate the
304 * alternate version upon activation.
305 * @param string $target The target plugin to deactivate
306 */
307 function rsssl_deactivate_alternate(string $target = 'free') {
308
309 // we use this to ensure the base function doesn't load, as the active
310 // plugins function does not update yet. See RSSSL() in main plugin file
311 if ( ! defined('RSSSL_DEACTIVATING_ALTERNATE')) {
312 define( "RSSSL_DEACTIVATING_ALTERNATE", true );
313 }
314
315 include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
316 $alternate_plugin_path = '';
317
318 switch ($target) {
319 case 'free':
320 $alternate_plugin_path = 'really-simple-ssl/rlrsssl-really-simple-ssl.php';
321 break;
322 case 'pro':
323 $alternate_plugin_path = 'really-simple-ssl-pro/really-simple-ssl-pro.php';
324 break;
325 case 'multisite':
326 $alternate_plugin_path = 'really-simple-ssl-pro-multisite/really-simple-ssl-pro-multisite.php';
327 break;
328 }
329
330 // If no valid target or alternate path, return early
331 if (empty($alternate_plugin_path)) {
332 return;
333 }
334
335 if ( is_plugin_active( $alternate_plugin_path ) ) {
336
337 # Get current options
338 $is_network_active = is_multisite() && is_plugin_active_for_network( $alternate_plugin_path );
339 if ( $is_network_active ) {
340 $options = get_site_option( 'rsssl_options', [] );
341 } else {
342 $options = get_option( 'rsssl_options', [] );
343 }
344
345 # Store original values we need to preserve
346 $ssl_enabled_was_active = isset( $options['ssl_enabled'] ) && $options['ssl_enabled'];
347 $delete_data_on_uninstall_was_enabled = isset( $options['delete_data_on_uninstall'] ) && $options['delete_data_on_uninstall'];
348
349 # Temporarily disable delete_data_on_uninstall to prevent data loss during deactivation
350 if ( $delete_data_on_uninstall_was_enabled ) {
351 $options['delete_data_on_uninstall'] = false;
352
353 # Save this change before deactivation to prevent data loss
354 if ( $is_network_active ) {
355 update_site_option( 'rsssl_options', $options );
356 } else {
357 update_option( 'rsssl_options', $options );
358 }
359 }
360
361 update_option('rsssl_free_deactivated', true);
362
363 if ( function_exists('deactivate_plugins' ) ) {
364 deactivate_plugins( $alternate_plugin_path );
365 }
366
367 // Ensure the function exists to prevent fatal errors in case of
368 // direct access
369 // Delete plugins based on environment and target
370 if (function_exists( 'delete_plugins' ) && function_exists('request_filesystem_credentials' ) ) {
371 // Always delete free plugin
372 if ($target === 'free') {
373 delete_plugins( array( $alternate_plugin_path ) );
374 }
375 // Delete multisite plugin on non-multisite environments
376 else if ($target === 'multisite' && !is_multisite()) {
377 delete_plugins( array( $alternate_plugin_path ) );
378 }
379 // Delete pro plugin on multisite environments
380 else if ($target === 'pro' && is_multisite()) {
381 delete_plugins( array( $alternate_plugin_path ) );
382 }
383 }
384
385 # Re-read options after plugin operations to get current state
386 if ( $is_network_active ) {
387 $options = get_site_option( 'rsssl_options', [] );
388 } else {
389 $options = get_option( 'rsssl_options', [] );
390 }
391
392 # Restore preserved settings
393 if ( $ssl_enabled_was_active ) {
394 $options['ssl_enabled'] = true;
395 }
396 if ( $delete_data_on_uninstall_was_enabled ) {
397 $options['delete_data_on_uninstall'] = true;
398 }
399
400 # Save all option changes at once
401 if ( $is_network_active ) {
402 update_site_option( 'rsssl_options', $options );
403 } else {
404 update_option( 'rsssl_options', $options );
405 }
406
407 // Delete free translations files from /wp-content/languages/plugins where files contain really-simple-ssl
408 if ($target === 'free' && defined( 'WP_CONTENT_DIR' ) ) {
409 $languages_plugins_dir = WP_CONTENT_DIR . '/languages/plugins';
410 if ( is_dir( $languages_plugins_dir ) && is_writable( $languages_plugins_dir ) ) {
411 $files = scandir( $languages_plugins_dir );
412 foreach ( $files as $file ) {
413 if ( is_file( $languages_plugins_dir . '/' . $file ) &&
414 strpos( $file, 'really-simple-ssl' ) === 0 ) {
415 @unlink( $languages_plugins_dir . '/' . $file );
416 }
417 }
418 }
419 }
420 }
421 }
422 }
423
424 /**
425 * Handle resending the verification e-mail.
426 */
427 if ( ! function_exists('rsssl_resend_verification_email') ) {
428 function rsssl_resend_verification_email() {
429 if ( ! rsssl_user_can_manage() ) {
430 return;
431 }
432
433 if ( ! isset( $_POST['rsssl_resend_email_nonce'] ) || ! wp_verify_nonce( $_POST['rsssl_resend_email_nonce'], 'rsssl_resend_verification_email_nonce' ) ) {
434 wp_die();
435 }
436
437 $mailer = new rsssl_mailer();
438 $mailer->send_verification_mail();
439
440 wp_send_json_success();
441 }
442 }
443
444 /**
445 * Handle the force confirm email action.
446 */
447 if ( ! function_exists('rsssl_handle_force_confirm_email') ) {
448
449 function rsssl_handle_force_confirm_email(): void {
450 if ( ! rsssl_user_can_manage() ) {
451 return;
452 }
453
454 if ( ! isset( $_POST['rsssl_force_email_action_nonce'] ) || ! wp_verify_nonce( $_POST['rsssl_force_email_action_nonce'], 'rsssl_force_confirm_email_nonce' ) ) {
455 wp_die();
456 }
457
458 update_option( 'rsssl_email_verification_status', 'completed', false );
459 wp_send_json_success();
460 }
461 }
462
463 /**
464 * Add JavaScript for email verification and re-send buttons.
465 */
466 if ( ! function_exists('rsssl_generate_email_verification_buttons_js') ) {
467 function rsssl_generate_email_verification_buttons_js(): void
468 {
469 if (!rsssl_user_can_manage()) {
470 return;
471 }
472
473 ?>
474 <script type="text/javascript">
475 jQuery(document).ready(function($) {
476 // Force confirm button handler
477 $(document).on('click', '#rsssl-force-confirm', function(e) {
478 e.preventDefault();
479 var button = $(this);
480 button.prop('disabled', true);
481
482 $.ajax({
483 url: ajaxurl,
484 type: 'POST',
485 data: {
486 action: 'rsssl_force_confirm_email',
487 rsssl_force_email_action_nonce: '<?php echo wp_create_nonce('rsssl_force_confirm_email_nonce'); ?>'
488 },
489 success: function(response) {
490 if (response.success) {
491 location.reload();
492 }
493 }
494 });
495 });
496
497 // Resend verification email button handler
498 $(document).on('click', '#rsssl-resend-verification', function(e) {
499 e.preventDefault();
500 var button = $(this);
501 button.prop('disabled', true);
502 button.text('<?php echo esc_js(__('Sending...', 'really-simple-ssl')); ?>');
503
504 $.ajax({
505 url: ajaxurl,
506 type: 'POST',
507 data: {
508 action: 'rsssl_resend_verification_email',
509 rsssl_resend_email_nonce: '<?php echo wp_create_nonce('rsssl_resend_verification_email_nonce'); ?>'
510 },
511 success: function(response) {
512 if (response.success) {
513 button.text('<?php echo esc_js(__('Email sent!', 'really-simple-ssl')); ?>');
514 setTimeout(function() {
515 button.text('<?php echo esc_js(__('Resend verification email', 'really-simple-ssl')); ?>');
516 button.prop('disabled', false);
517 }, 3000);
518 } else {
519 button.text('<?php echo esc_js(__('Failed to send', 'really-simple-ssl')); ?>');
520 setTimeout(function() {
521 button.text('<?php echo esc_js(__('Resend verification email', 'really-simple-ssl')); ?>');
522 button.prop('disabled', false);
523 }, 3000);
524 }
525 },
526 error: function() {
527 button.text('<?php echo esc_js(__('Error occurred', 'really-simple-ssl')); ?>');
528 setTimeout(function() {
529 button.text('<?php echo esc_js(__('Resend verification email', 'really-simple-ssl')); ?>');
530 button.prop('disabled', false);
531 }, 3000);
532 }
533 });
534 });
535 });
536 </script>
537 <?php
538 }
539 }
540 if ( ! function_exists('rsssl_free_active') ) {
541 if ( ! function_exists( 'rsssl_free_active' ) ) {
542 function rsssl_free_active() {
543 if ( function_exists( 'rsssl_activation_check' ) ) {
544 return true;
545 }
546
547 include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
548 $free_plugin_path = 'really-simple-ssl/rlrsssl-really-simple-ssl.php';
549
550 return is_plugin_active( $free_plugin_path );
551 }
552 }
553 }