PluginProbe ʕ •ᴥ•ʔ
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) / 9.5.0.1
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) v9.5.0.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 9 months ago languages 9 months ago lets-encrypt 9 months ago lib 1 year ago mailer 10 months ago modal 9 months ago onboarding 10 months ago placeholders 9 months ago progress 1 year ago security 9 months ago settings 9 months ago testssl 5 years ago upgrade 9 months ago .wp-env.json 10 months ago SECURITY.md 9 months ago class-admin.php 9 months ago class-cache.php 2 years ago class-certificate.php 2 years ago class-front-end.php 1 year ago class-installer.php 10 months ago class-mixed-content-fixer.php 3 years ago class-multisite.php 1 year ago class-server.php 1 year ago class-site-health.php 1 year ago class-wp-cli.php 11 months ago compatibility.php 1 year ago force-deactivate.txt 1 year ago functions.php 10 months ago index.php 2 years ago readme.txt 9 months ago rector.php 1 year ago rlrsssl-really-simple-ssl.php 9 months ago rsssl-auto-loader.php 1 year ago sbom.json.gz 9 months ago ssl-test-page.php 2 years ago system-status.php 9 months ago uninstall.php 9 months ago upgrade.php 9 months ago
functions.php
488 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 define( "RSSSL_DEACTIVATING_ALTERNATE", true );
302
303 include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
304 $alternate_plugin_path = 'really-simple-ssl-pro/really-simple-ssl-pro.php';
305
306 if ($target === 'free') {
307 $alternate_plugin_path = 'really-simple-ssl/rlrsssl-really-simple-ssl.php';
308 }
309
310 if ( is_plugin_active( $alternate_plugin_path ) ) {
311
312 $delete_data_on_uninstall_was_enabled = false;
313
314 # Temporarily disable delete_data_on_uninstall option in rsssl_options
315 if ( is_multisite() && rsssl_is_networkwide_active() ) {
316 $options = get_site_option( 'rsssl_options', [] );
317 } else {
318 $options = get_option( 'rsssl_options', [] );
319 }
320
321 if ( isset( $options['delete_data_on_uninstall'] ) && $options['delete_data_on_uninstall'] ) {
322 $options['delete_data_on_uninstall'] = false;
323 $delete_data_on_uninstall_was_enabled = true;
324 }
325
326 if ( is_multisite() && rsssl_is_networkwide_active() ) {
327 update_site_option( 'rsssl_options', $options );
328 } else {
329 update_option( 'rsssl_options', $options );
330 }
331
332 update_option('rsssl_free_deactivated', true);
333
334 if ( function_exists('deactivate_plugins' ) ) {
335 deactivate_plugins( $alternate_plugin_path );
336 }
337
338 // Ensure the function exists to prevent fatal errors in case of
339 // direct access. Don't delete if debug enabled, for dev purposes.
340 // Also, only delete the free plugin.
341 $debug_enabled = defined('WP_DEBUG') && WP_DEBUG;
342 if ($target === 'free' && !$debug_enabled && function_exists( 'delete_plugins' ) && function_exists('request_filesystem_credentials' ) ) {
343 delete_plugins( array( $alternate_plugin_path ) );
344 }
345
346 # Now re-enable delete_data_on_uninstall if it was enabled
347 if ( $delete_data_on_uninstall_was_enabled ) {
348 $options['delete_data_on_uninstall'] = true;
349 if ( is_multisite() && rsssl_is_networkwide_active() ) {
350 update_site_option( 'rsssl_options', $options );
351 } else {
352 update_option( 'rsssl_options', $options );
353 }
354 }
355
356 $ssl_enabled = rsssl_get_option('ssl_enabled');
357 if ( $ssl_enabled ) {
358 rsssl_update_option('ssl_enabled', true);
359 }
360
361 // Delete free translations files from /wp-content/languages/plugins where files contain really-simple-ssl
362 if ($target === 'free' && defined( 'WP_CONTENT_DIR' ) ) {
363 $languages_plugins_dir = WP_CONTENT_DIR . '/languages/plugins';
364 if ( is_dir( $languages_plugins_dir ) && is_writable( $languages_plugins_dir ) ) {
365 $files = scandir( $languages_plugins_dir );
366 foreach ( $files as $file ) {
367 if ( is_file( $languages_plugins_dir . '/' . $file ) &&
368 strpos( $file, 'really-simple-ssl' ) === 0 ) {
369 @unlink( $languages_plugins_dir . '/' . $file );
370 }
371 }
372 }
373 }
374 }
375 }
376 }
377
378 /**
379 * Handle resending the verification e-mail.
380 */
381 function rsssl_resend_verification_email()
382 {
383 if (!rsssl_user_can_manage()) {
384 return;
385 }
386
387 if ( !isset($_POST['rsssl_resend_email_nonce']) || ! wp_verify_nonce( $_POST['rsssl_resend_email_nonce'], 'rsssl_resend_verification_email_nonce' ) ) {
388 wp_die();
389 }
390
391 $mailer = new rsssl_mailer();
392 $mailer->send_verification_mail();
393
394 wp_send_json_success();
395 }
396
397 /**
398 * Handle the force confirm email action.
399 */
400 function rsssl_handle_force_confirm_email(): void
401 {
402 if (!rsssl_user_can_manage()) {
403 return;
404 }
405
406 if ( !isset($_POST['rsssl_force_email_action_nonce']) || ! wp_verify_nonce( $_POST['rsssl_force_email_action_nonce'], 'rsssl_force_confirm_email_nonce' ) ) {
407 wp_die();
408 }
409
410 update_option( 'rsssl_email_verification_status', 'completed', false );
411 wp_send_json_success();
412 }
413
414 /**
415 * Add JavaScript for email verification and re-send buttons.
416 */
417 function rsssl_generate_email_verification_buttons_js(): void
418 {
419 if (!rsssl_user_can_manage()) {
420 return;
421 }
422
423 ?>
424 <script type="text/javascript">
425 jQuery(document).ready(function($) {
426 // Force confirm button handler
427 $(document).on('click', '#rsssl-force-confirm', function(e) {
428 e.preventDefault();
429 var button = $(this);
430 button.prop('disabled', true);
431
432 $.ajax({
433 url: ajaxurl,
434 type: 'POST',
435 data: {
436 action: 'rsssl_force_confirm_email',
437 rsssl_force_email_action_nonce: '<?php echo wp_create_nonce('rsssl_force_confirm_email_nonce'); ?>'
438 },
439 success: function(response) {
440 if (response.success) {
441 location.reload();
442 }
443 }
444 });
445 });
446
447 // Resend verification email button handler
448 $(document).on('click', '#rsssl-resend-verification', function(e) {
449 e.preventDefault();
450 var button = $(this);
451 button.prop('disabled', true);
452 button.text('<?php echo esc_js(__('Sending...', 'really-simple-ssl')); ?>');
453
454 $.ajax({
455 url: ajaxurl,
456 type: 'POST',
457 data: {
458 action: 'rsssl_resend_verification_email',
459 rsssl_resend_email_nonce: '<?php echo wp_create_nonce('rsssl_resend_verification_email_nonce'); ?>'
460 },
461 success: function(response) {
462 if (response.success) {
463 button.text('<?php echo esc_js(__('Email sent!', 'really-simple-ssl')); ?>');
464 setTimeout(function() {
465 button.text('<?php echo esc_js(__('Resend verification email', 'really-simple-ssl')); ?>');
466 button.prop('disabled', false);
467 }, 3000);
468 } else {
469 button.text('<?php echo esc_js(__('Failed to send', 'really-simple-ssl')); ?>');
470 setTimeout(function() {
471 button.text('<?php echo esc_js(__('Resend verification email', 'really-simple-ssl')); ?>');
472 button.prop('disabled', false);
473 }, 3000);
474 }
475 },
476 error: function() {
477 button.text('<?php echo esc_js(__('Error occurred', 'really-simple-ssl')); ?>');
478 setTimeout(function() {
479 button.text('<?php echo esc_js(__('Resend verification email', 'really-simple-ssl')); ?>');
480 button.prop('disabled', false);
481 }, 3000);
482 }
483 });
484 });
485 });
486 </script>
487 <?php
488 }