PluginProbe ʕ •ᴥ•ʔ
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) / 9.5.1
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) v9.5.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 8 months ago languages 8 months ago lets-encrypt 8 months ago lib 8 months ago mailer 8 months ago modal 8 months ago onboarding 8 months ago placeholders 8 months ago progress 8 months ago security 8 months ago settings 8 months ago testssl 8 months ago upgrade 8 months ago .wp-env.json 8 months ago SECURITY.md 8 months ago class-admin.php 8 months ago class-cache.php 8 months ago class-certificate.php 8 months ago class-front-end.php 8 months ago class-installer.php 8 months ago class-mixed-content-fixer.php 8 months ago class-multisite.php 8 months ago class-server.php 8 months ago class-site-health.php 8 months ago class-wp-cli.php 8 months ago compatibility.php 8 months ago force-deactivate.txt 8 months ago functions.php 8 months ago index.php 8 months ago readme.txt 8 months ago rector.php 8 months ago rlrsssl-really-simple-ssl.php 8 months ago rsssl-auto-loader.php 8 months ago sbom.json.gz 8 months ago ssl-test-page.php 8 months ago system-status.php 8 months ago uninstall.php 8 months ago upgrade.php 8 months ago
functions.php
543 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 key has been set
243 if ( get_site_option( 'rsssl_encryption_keys_set' ) ) {
244 return;
245 }
246
247 $wp_config_path = rsssl_wpconfig_path();
248
249 // Check if we already have a key defined
250 if ( defined( 'RSSSL_KEY' ) ) {
251 return;
252 }
253
254 $key = get_site_option( 'rsssl_main_key' );
255 $new_generated = false;
256
257 // If we don't have a key, generate one
258 if ( ! $key ) {
259 $new_generated = true;
260 $key = wp_generate_password( 64, false );
261 }
262
263 if ( is_writable( $wp_config_path ) ) {
264 // Add the key to the wp-config file
265 $rule = "//Begin Really Simple Security key\n";
266 $rule .= "define('RSSSL_KEY', '" . $key . "');\n";
267 $rule .= "//END Really Simple Security key\n";
268 $insert_after = '<?php';
269
270 $contents = file_get_contents( $wp_config_path );
271 $pos = strpos( $contents, $insert_after );
272 if ( false !== $pos && strpos( $contents, 'RSSSL_KEY' ) === false ) {
273 $contents = substr_replace( $contents, $rule, $pos + 1 + strlen( $insert_after ), 0 );
274 file_put_contents( $wp_config_path, $contents );
275 }
276
277 // If the wp-config was just set to writable, we can delete the key from the database now.
278 delete_site_option( 'rsssl_main_key' );
279 } elseif ( $new_generated ) {
280 // If we can't write to the wp-config file, store the key in the database
281 // When wp-config is set to writable, auto upgrade to constant
282 update_site_option( 'rsssl_main_key', $key, false );
283 }
284
285 update_site_option( 'rsssl_encryption_keys_set', true );
286 }
287 rsssl_set_encryption_key();
288 }
289
290 if ( ! function_exists( 'rsssl_deactivate_alternate' ) ) {
291 /**
292 * Deactivate the alternate version if active. This function is included in
293 * both the pro and free plugin and should be used to deactivate the
294 * alternate version upon activation.
295 * @param string $target The target plugin to deactivate
296 */
297 function rsssl_deactivate_alternate(string $target = 'free') {
298
299 // we use this to ensure the base function doesn't load, as the active
300 // plugins function does not update yet. See RSSSL() in main plugin file
301 if ( ! defined('RSSSL_DEACTIVATING_ALTERNATE')) {
302 define( "RSSSL_DEACTIVATING_ALTERNATE", true );
303 }
304
305 include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
306 $alternate_plugin_path = '';
307
308 switch ($target) {
309 case 'free':
310 $alternate_plugin_path = 'really-simple-ssl/rlrsssl-really-simple-ssl.php';
311 break;
312 case 'pro':
313 $alternate_plugin_path = 'really-simple-ssl-pro/really-simple-ssl-pro.php';
314 break;
315 case 'multisite':
316 $alternate_plugin_path = 'really-simple-ssl-pro-multisite/really-simple-ssl-pro-multisite.php';
317 break;
318 }
319
320 // If no valid target or alternate path, return early
321 if (empty($alternate_plugin_path)) {
322 return;
323 }
324
325 if ( is_plugin_active( $alternate_plugin_path ) ) {
326
327 # Get current options
328 $is_network_active = is_multisite() && is_plugin_active_for_network( $alternate_plugin_path );
329 if ( $is_network_active ) {
330 $options = get_site_option( 'rsssl_options', [] );
331 } else {
332 $options = get_option( 'rsssl_options', [] );
333 }
334
335 # Store original values we need to preserve
336 $ssl_enabled_was_active = isset( $options['ssl_enabled'] ) && $options['ssl_enabled'];
337 $delete_data_on_uninstall_was_enabled = isset( $options['delete_data_on_uninstall'] ) && $options['delete_data_on_uninstall'];
338
339 # Temporarily disable delete_data_on_uninstall to prevent data loss during deactivation
340 if ( $delete_data_on_uninstall_was_enabled ) {
341 $options['delete_data_on_uninstall'] = false;
342
343 # Save this change before deactivation to prevent data loss
344 if ( $is_network_active ) {
345 update_site_option( 'rsssl_options', $options );
346 } else {
347 update_option( 'rsssl_options', $options );
348 }
349 }
350
351 update_option('rsssl_free_deactivated', true);
352
353 if ( function_exists('deactivate_plugins' ) ) {
354 deactivate_plugins( $alternate_plugin_path );
355 }
356
357 // Ensure the function exists to prevent fatal errors in case of
358 // direct access
359 // Delete plugins based on environment and target
360 if (function_exists( 'delete_plugins' ) && function_exists('request_filesystem_credentials' ) ) {
361 // Always delete free plugin
362 if ($target === 'free') {
363 delete_plugins( array( $alternate_plugin_path ) );
364 }
365 // Delete multisite plugin on non-multisite environments
366 else if ($target === 'multisite' && !is_multisite()) {
367 delete_plugins( array( $alternate_plugin_path ) );
368 }
369 // Delete pro plugin on multisite environments
370 else if ($target === 'pro' && is_multisite()) {
371 delete_plugins( array( $alternate_plugin_path ) );
372 }
373 }
374
375 # Re-read options after plugin operations to get current state
376 if ( $is_network_active ) {
377 $options = get_site_option( 'rsssl_options', [] );
378 } else {
379 $options = get_option( 'rsssl_options', [] );
380 }
381
382 # Restore preserved settings
383 if ( $ssl_enabled_was_active ) {
384 $options['ssl_enabled'] = true;
385 }
386 if ( $delete_data_on_uninstall_was_enabled ) {
387 $options['delete_data_on_uninstall'] = true;
388 }
389
390 # Save all option changes at once
391 if ( $is_network_active ) {
392 update_site_option( 'rsssl_options', $options );
393 } else {
394 update_option( 'rsssl_options', $options );
395 }
396
397 // Delete free translations files from /wp-content/languages/plugins where files contain really-simple-ssl
398 if ($target === 'free' && defined( 'WP_CONTENT_DIR' ) ) {
399 $languages_plugins_dir = WP_CONTENT_DIR . '/languages/plugins';
400 if ( is_dir( $languages_plugins_dir ) && is_writable( $languages_plugins_dir ) ) {
401 $files = scandir( $languages_plugins_dir );
402 foreach ( $files as $file ) {
403 if ( is_file( $languages_plugins_dir . '/' . $file ) &&
404 strpos( $file, 'really-simple-ssl' ) === 0 ) {
405 @unlink( $languages_plugins_dir . '/' . $file );
406 }
407 }
408 }
409 }
410 }
411 }
412 }
413
414 /**
415 * Handle resending the verification e-mail.
416 */
417 if ( ! function_exists('rsssl_resend_verification_email') ) {
418 function rsssl_resend_verification_email() {
419 if ( ! rsssl_user_can_manage() ) {
420 return;
421 }
422
423 if ( ! isset( $_POST['rsssl_resend_email_nonce'] ) || ! wp_verify_nonce( $_POST['rsssl_resend_email_nonce'], 'rsssl_resend_verification_email_nonce' ) ) {
424 wp_die();
425 }
426
427 $mailer = new rsssl_mailer();
428 $mailer->send_verification_mail();
429
430 wp_send_json_success();
431 }
432 }
433
434 /**
435 * Handle the force confirm email action.
436 */
437 if ( ! function_exists('rsssl_handle_force_confirm_email') ) {
438
439 function rsssl_handle_force_confirm_email(): void {
440 if ( ! rsssl_user_can_manage() ) {
441 return;
442 }
443
444 if ( ! isset( $_POST['rsssl_force_email_action_nonce'] ) || ! wp_verify_nonce( $_POST['rsssl_force_email_action_nonce'], 'rsssl_force_confirm_email_nonce' ) ) {
445 wp_die();
446 }
447
448 update_option( 'rsssl_email_verification_status', 'completed', false );
449 wp_send_json_success();
450 }
451 }
452
453 /**
454 * Add JavaScript for email verification and re-send buttons.
455 */
456 if ( ! function_exists('rsssl_generate_email_verification_buttons_js') ) {
457 function rsssl_generate_email_verification_buttons_js(): void
458 {
459 if (!rsssl_user_can_manage()) {
460 return;
461 }
462
463 ?>
464 <script type="text/javascript">
465 jQuery(document).ready(function($) {
466 // Force confirm button handler
467 $(document).on('click', '#rsssl-force-confirm', function(e) {
468 e.preventDefault();
469 var button = $(this);
470 button.prop('disabled', true);
471
472 $.ajax({
473 url: ajaxurl,
474 type: 'POST',
475 data: {
476 action: 'rsssl_force_confirm_email',
477 rsssl_force_email_action_nonce: '<?php echo wp_create_nonce('rsssl_force_confirm_email_nonce'); ?>'
478 },
479 success: function(response) {
480 if (response.success) {
481 location.reload();
482 }
483 }
484 });
485 });
486
487 // Resend verification email button handler
488 $(document).on('click', '#rsssl-resend-verification', function(e) {
489 e.preventDefault();
490 var button = $(this);
491 button.prop('disabled', true);
492 button.text('<?php echo esc_js(__('Sending...', 'really-simple-ssl')); ?>');
493
494 $.ajax({
495 url: ajaxurl,
496 type: 'POST',
497 data: {
498 action: 'rsssl_resend_verification_email',
499 rsssl_resend_email_nonce: '<?php echo wp_create_nonce('rsssl_resend_verification_email_nonce'); ?>'
500 },
501 success: function(response) {
502 if (response.success) {
503 button.text('<?php echo esc_js(__('Email sent!', 'really-simple-ssl')); ?>');
504 setTimeout(function() {
505 button.text('<?php echo esc_js(__('Resend verification email', 'really-simple-ssl')); ?>');
506 button.prop('disabled', false);
507 }, 3000);
508 } else {
509 button.text('<?php echo esc_js(__('Failed to send', 'really-simple-ssl')); ?>');
510 setTimeout(function() {
511 button.text('<?php echo esc_js(__('Resend verification email', 'really-simple-ssl')); ?>');
512 button.prop('disabled', false);
513 }, 3000);
514 }
515 },
516 error: function() {
517 button.text('<?php echo esc_js(__('Error occurred', 'really-simple-ssl')); ?>');
518 setTimeout(function() {
519 button.text('<?php echo esc_js(__('Resend verification email', 'really-simple-ssl')); ?>');
520 button.prop('disabled', false);
521 }, 3000);
522 }
523 });
524 });
525 });
526 </script>
527 <?php
528 }
529 }
530 if ( ! function_exists('rsssl_free_active') ) {
531 if ( ! function_exists( 'rsssl_free_active' ) ) {
532 function rsssl_free_active() {
533 if ( function_exists( 'rsssl_activation_check' ) ) {
534 return true;
535 }
536
537 include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
538 $free_plugin_path = 'really-simple-ssl/rlrsssl-really-simple-ssl.php';
539
540 return is_plugin_active( $free_plugin_path );
541 }
542 }
543 }