PluginProbe ʕ •ᴥ•ʔ
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) / 9.5.9
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) v9.5.9
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 / security / functions.php
really-simple-ssl / security Last commit date
includes 2 months ago server 2 months ago tests 2 months ago wordpress 2 months ago class-rsssl-htaccess-file-manager.php 2 months ago cron.php 2 months ago deactivate-integration.php 2 months ago firewall-manager.php 2 months ago functions.php 2 months ago index.php 2 months ago integrations.php 2 months ago notices.php 2 months ago security.php 2 months ago sync-settings.php 2 months ago tests.php 2 months ago
functions.php
678 lines
1 <?php
2
3 use RSSSL\Security\RSSSL_Htaccess_File_Manager;
4
5 defined( 'ABSPATH' ) or die( );
6
7 // Htaccess marker constants
8 const RSSSL_DISABLE_DIRECTORY_INDEXING_MARKER = 'Really Simple Security Disable directory indexing';
9 const RSSSL_NO_INDEX_LEGACY_MARKER = 'Really Simple Security No Index';
10 /**
11 * Back-end available only
12 */
13 if ( !function_exists('rsssl_do_fix')) {
14 /**
15 * Complete a fix for an issue, either user triggered, or automatic
16 *
17 * @param $fix
18 *
19 * @return void
20 */
21 function rsssl_do_fix( $fix ) {
22 if ( ! rsssl_user_can_manage() ) {
23 return;
24 }
25
26 if ( ! rsssl_has_fix( $fix ) && function_exists( $fix ) ) {
27 $completed[] = $fix;
28 $fix();
29 $completed = get_option( 'rsssl_completed_fixes', [] );
30 $completed[] = $fix;
31 update_option( 'rsssl_completed_fixes', $completed );
32 } else if ( $fix && ! function_exists( $fix ) ) {
33 }
34
35 }
36 }
37 if ( !function_exists('rsssl_has_fix')) {
38
39 /**
40 * Check if this has been fixed already
41 *
42 * @param $fix
43 *
44 * @return bool
45 */
46 function rsssl_has_fix( $fix ) {
47 $completed = get_option( 'rsssl_completed_fixes', [] );
48 if ( ! in_array( $fix, $completed ) ) {
49 return false;
50 }
51
52 return true;
53 }
54 }
55
56 if ( !function_exists('rsssl_admin_url')) {
57 /**
58 * Get admin url, adjusted for multisite
59 * @param array $args //query args
60 * @param string $path //hash slug for the settings pages (e.g. #dashboard)
61 * @return string
62 */
63 function rsssl_admin_url(array $args = [], string $path = ''): string {
64 $url = is_multisite() ? network_admin_url('admin.php') : admin_url('admin.php');
65 $args = wp_parse_args($args, ['page' => 'really-simple-security']);
66 return add_query_arg($args, $url) . $path;
67 }
68 }
69
70 if ( !function_exists('rsssl_maybe_clear_transients')) {
71 /**
72 * If the corresponding setting has been changed, clear the test cache and re-run it.
73 *
74 * @return void
75 */
76 function rsssl_maybe_clear_transients( $field_id, $field_value, $prev_value, $field_type ) {
77 if ( $field_id === 'mixed_content_fixer' && $field_value ) {
78 delete_transient( 'rsssl_mixed_content_fixer_detected' );
79 RSSSL()->admin->mixed_content_fixer_detected();
80 }
81
82 //expire in five minutes
83 $headers = get_transient('rsssl_can_use_curl_headers_check');
84 set_transient('rsssl_can_use_curl_headers_check', $headers, 5 * MINUTE_IN_SECONDS);
85
86 //no change
87 if ( $field_value === $prev_value ) {
88 return;
89 }
90
91 if ( $field_id === 'disable_http_methods' ) {
92 delete_option( 'rsssl_http_methods_allowed' );
93 rsssl_http_methods_allowed();
94 }
95 if ( $field_id === 'xmlrpc' ) {
96 delete_transient( 'rsssl_xmlrpc_allowed' );
97 rsssl_xmlrpc_allowed();
98 }
99 if ( $field_id === 'disable_indexing' ) {
100 delete_transient( 'rsssl_directory_indexing_status' );
101 rsssl_directory_indexing_allowed();
102 }
103 if ( $field_id === 'block_code_execution_uploads' ) {
104 delete_transient( 'rsssl_code_execution_allowed_status' );
105 rsssl_code_execution_allowed();
106 }
107 if ( $field_id === 'hide_wordpress_version' ) {
108 delete_option( 'rsssl_wp_version_detected' );
109 rsssl_src_contains_wp_version();
110 }
111 if ( $field_id === 'rename_admin_user' ) {
112 delete_transient('rsssl_admin_user_count');
113 rsssl_has_admin_user();
114 }
115
116 }
117
118 add_action( "rsssl_after_save_field", 'rsssl_maybe_clear_transients', 100, 4 );
119 }
120
121 if ( !function_exists('rsssl_remove_htaccess_security_edits') ) {
122 /**
123 * Clean up on deactivation
124 *
125 * @param bool $clear_htaccess_redirect Whether to clear the htaccess redirect when deactivating
126 * @return void
127 */
128 function rsssl_remove_htaccess_security_edits( $clear_htaccess_redirect = false ) {
129
130 if ( ! rsssl_user_can_manage() ) {
131 return;
132 }
133
134 if ( ! rsssl_uses_htaccess() ) {
135 return;
136 }
137
138 $htaccess_file = RSSSL()->admin->htaccess_file();
139 if ( ! file_exists( $htaccess_file ) ) {
140 return;
141 }
142
143 $start = "\n" . '#Begin Really Simple Security';
144 $end = '#End Really Simple Security' . "\n";
145 $pattern = '/'.$start.'(.*?)'.$end.'/is';
146
147 /**
148 * htaccess in uploads dir
149 */
150 $upload_dir = wp_get_upload_dir();
151 $htaccess_file_uploads = trailingslashit( $upload_dir['basedir']).'.htaccess';
152 $content_htaccess_uploads = is_file($htaccess_file_uploads ) ? file_get_contents($htaccess_file_uploads) : '';
153 if (preg_match($pattern, $content_htaccess_uploads) && is_writable( $htaccess_file_uploads )) {
154 $content_htaccess_uploads = preg_replace($pattern, "", $content_htaccess_uploads);
155 file_put_contents( $htaccess_file_uploads, $content_htaccess_uploads, LOCK_EX );
156 }
157 // Uses the new conversion of the htaccess file manager
158 $root_htaccess_file = RSSSL()->admin->htaccess_file();
159
160 $root_manager = RSSSL_Htaccess_File_Manager::get_instance();
161
162 /*
163 * This is the root .htaccess file, which is used for security rules.
164 * We will clear the security rules from this file.
165 * This is done by clearing the rules that were added by the plugin.
166 * The rules are identified by their marker, which is a comment line in the .htaccess file.
167 * The marker is used to identify the rules that were added by the plugin.
168 *
169 * note: Only this is for the root .htaccess file, not the uploads .htaccess file.
170 */
171 if ( ! $root_manager->validate_htaccess_file_path() ) {
172 return;
173 }
174
175 // Only clear redirect rules if explicitly requested
176 if ( $clear_htaccess_redirect ) {
177 // Clear redirect rules block
178 $root_manager->clear_rule( 'Really Simple Security Redirect', 'clear redirect 1' );
179 //Legacy rules
180 $root_manager->clear_legacy_rule( 'Really Simple Security Redirect' );
181 // Clear any remaining security rules block
182 $root_manager->clear_legacy_rule( 'Really Simple Security' );
183 // Clear disable directory indexing block
184 $root_manager->clear_rule( RSSSL_DISABLE_DIRECTORY_INDEXING_MARKER, 'clear disable directory indexing' );
185 // Clear legacy Really Simple SSL block
186 $root_manager->clear_legacy_rule( 'rlrssslReallySimpleSSL' );
187 }
188 }
189 }
190
191
192 /**
193 * Wrap the security headers
194 */
195 if ( ! function_exists('rsssl_wrap_htaccess' ) ) {
196 function rsssl_wrap_htaccess() {
197 if ( ! rsssl_htaccess_should_wrap() ) {
198 return;
199 }
200 update_option( 'rsssl_htaccess_should_wrap', true, false );
201
202 rsssl_htaccess_clear_errors();
203 rsssl_handle_uploads_htaccess();
204 rsssl_handle_root_htaccess();
205 rsssl_htaccess_finalize();
206 }
207 add_action('admin_init', 'rsssl_wrap_htaccess' );
208 add_action('rsssl_after_saved_fields', 'rsssl_wrap_htaccess', 30);
209 }
210
211 /**
212 * Check whether we should wrap htaccess.
213 *
214 * @return bool
215 */
216 function rsssl_htaccess_should_wrap(): bool {
217 if ( ! rsssl_user_can_manage() || ! rsssl_uses_htaccess() ) {
218 return false;
219 }
220 if ( rsssl_get_option('do_not_edit_htaccess') ) {
221 delete_site_option('rsssl_htaccess_error');
222 delete_site_option('rsssl_htaccess_rules');
223 return false;
224 }
225
226 if ( get_option('rsssl_updating_htaccess') ) {
227 return false;
228 }
229 return true;
230 }
231
232 /**
233 * Finalize htaccess wrapping by removing the updating flag.
234 */
235 function rsssl_htaccess_finalize(): void {
236 delete_option('rsssl_updating_htaccess');
237 }
238
239 /**
240 * Handle root directory .htaccess wrapping.
241 */
242 function rsssl_handle_root_htaccess(): void {
243 $rules = apply_filters( 'rsssl_htaccess_security_rules', [] );
244 $htaccess_file = RSSSL()->admin->htaccess_file();
245 // If there are no rules at all, nothing to do (or record an error)
246 if ( empty( $rules ) ) {
247 delete_site_option( 'rsssl_htaccess_error' );
248 delete_site_option( 'rsssl_htaccess_rules' );
249 return;
250 }
251
252 // If file doesn’t exist yet, record that and cache the rules for later
253 if ( ! is_file( $htaccess_file ) ) {
254 update_site_option( 'rsssl_htaccess_error', 'not-exists' );
255 update_site_option( 'rsssl_htaccess_rules', implode( '', array_column( $rules, 'rules' ) ) );
256 return;
257 }
258
259 if ( is_file( $htaccess_file ) ) {
260 // Main path: file exists and we have rules
261 $manager = new RSSSL_Htaccess_File_Manager();
262 $manager->set_htaccess_file_path( $htaccess_file );
263
264 $definition = '';
265 $no_index_definition = '';
266
267 // 1) Drop any legacy blocks
268 rsssl_clear_legacy_rules( $manager );
269
270 // 2) Build the new redirect‐rules block
271 foreach ( $rules as $idx => $rule ) {
272 if ( isset( $rule['identifier'] ) && $rule['identifier'] === 'RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1' ) {
273 // removing the identifier from the rule, as it is not used in the new htaccess file manager
274 unset( $rule['identifier'] );
275 // 2.2) Add the redirect block
276 $definition = rsssl_build_redirect_block( $manager, $rule );
277 // remove this rule
278 unset( $rules[ $idx ] );
279 break; // stop after first match
280 }
281 }
282
283 foreach ( $rules as $idx => $rule ) {
284 if ( isset( $rule['identifier'] ) && $rule['identifier'] === 'Options -Indexes' ) {
285 // removing the identifier from the rule, as it is not used in the new htaccess file manager
286 unset( $rule['identifier'] );
287 // 2.1) Add the disable directory indexing block
288 $no_index_definition = rsssl_build_disable_indexing_block( $manager );
289 // remove this rule
290 unset( $rules[ $idx ] );
291 break; // stop after first match
292 }
293 }
294
295 // 3) If the file isn’t writable, record an error; otherwise write it
296 if ( ! is_writable( $htaccess_file ) ) {
297 update_site_option( 'rsssl_htaccess_error', 'not-writable' );
298
299 if (is_array($definition) && !empty($definition['lines'])) {
300 update_site_option( 'rsssl_htaccess_rules', implode( "\n", $definition['lines']));
301 }
302 return;
303 }
304
305 delete_site_option( 'rsssl_htaccess_error' );
306 delete_site_option( 'rsssl_htaccess_rules' );
307
308 if( !empty( $no_index_definition['lines'] ) ) {
309 // If we have a no-indexing block, write it first
310 $manager->write_rule( $no_index_definition, 'Writing no index block' );
311 } elseif( ! rsssl_get_option( 'disable_indexing', false ) ) {
312 // If we don't have a disable directory indexing block, clear it
313 $manager->clear_rule( RSSSL_DISABLE_DIRECTORY_INDEXING_MARKER, 'clear disable directory indexing' );
314 }
315 // // 4) Write the redirect block but only if it’s not empty
316 if ( ! empty( $definition['lines'] ) ) {
317 $manager->write_rule( $definition, 'Writing redirect block' );
318 }
319 if ( rsssl_get_option('redirect') !== 'htaccess' ) {
320 $manager->clear_rule( 'Really Simple Security Redirect', 'clear redirect 2 and value of config:' . rsssl_get_option('redirect') );
321 }
322 }
323 }
324
325 /**
326 * Build the redirect block for the .htaccess file.
327 *
328 * @param RSSSL_Htaccess_File_Manager $m
329 * @param array $lines the lines for the redirect block.
330 *
331 * @return array
332 */
333 function rsssl_build_redirect_block( RSSSL_Htaccess_File_Manager $m, array $lines = [] ): array
334 {
335 if ( empty($lines) ) {
336 return [
337 'marker' => 'Really Simple Security Redirect',
338 'lines' => [],
339 ];
340 }
341
342 // In case legacy markers are present, skip the rule. They should be
343 // cleared before this function is called.
344 $legacyMarkerPresent = $m->are_markers_present([
345 '#BEGIN Really Simple Security Redirect',
346 '#END Really Simple Security Redirect',
347 ]);
348
349 return [
350 'marker' => 'Really Simple Security Redirect',
351 'lines' => $lines,
352 ];
353 }
354
355 /**
356 * Build the disable directory indexing block for the .htaccess file.
357 *
358 * @param RSSSL_Htaccess_File_Manager $m
359 * @return array
360 */
361 function rsssl_build_disable_indexing_block( RSSSL_Htaccess_File_Manager $m ): array {
362 $content = $m->get_htaccess_content() ?: '';
363 $no_index = 'Options -Indexes';
364 if ( strpos( $content, $no_index ) !== false ) {
365 return [];
366 }
367
368 return [
369 'marker' => RSSSL_DISABLE_DIRECTORY_INDEXING_MARKER,
370 'lines' => [
371 '# Disable directory indexing to prevent listing of directory contents',
372 $no_index
373 ],
374 ];
375 }
376
377 /**
378 * Handle uploads directory .htaccess wrapping.
379 * TODO also needs to convert to the new file manager.
380 */
381 function rsssl_handle_uploads_htaccess(): void {
382 $start = '#Begin Really Simple Security';
383 $end = "\n" . '#End Really Simple Security' . "\n";
384 $pattern_content = '/' . preg_quote( $start, '/' ) . '(.*?)' . preg_quote( $end, '/' ) . '/is';
385 $pattern = '/' . preg_quote( $start, '/' ) . '.*?' . preg_quote( $end, '/' ) . '/is';
386 $rules_uploads = apply_filters( 'rsssl_htaccess_security_rules_uploads', [] );
387 $upload_dir = wp_get_upload_dir();
388 $htaccess_uploads = trailingslashit( $upload_dir['basedir'] ) . '.htaccess';
389
390 if ( ! is_file( $htaccess_uploads ) && count( $rules_uploads ) > 0 ) {
391 if ( is_writable( trailingslashit( $upload_dir['basedir'] ) ) ) {
392 file_put_contents( $htaccess_uploads, '', LOCK_EX );
393 } else {
394 update_site_option( 'rsssl_uploads_htaccess_error', 'not-writable' );
395 $rules_uploads_result = implode( '', array_column( $rules_uploads, 'rules' ) );
396 update_site_option( 'rsssl_uploads_htaccess_rules', $rules_uploads_result );
397 }
398 }
399
400 if ( is_file( $htaccess_uploads ) ) {
401 $content = file_get_contents( $htaccess_uploads );
402 preg_match( $pattern_content, $content, $matches );
403
404 if ( ( ! empty( $matches[1] ) && empty( $rules_uploads ) ) || ! empty( $rules_uploads ) ) {
405 $rules_uploads_result = '';
406 foreach ( $rules_uploads as $rule ) {
407 if ( strpos( $content, $rule['identifier'] ) !== false && ! preg_match( '/' . preg_quote( $start, '/' ) . '.*?(' . preg_quote( $rule['identifier'], '/' ) . ').*?' . preg_quote( $end, '/' ) . '/is', $content ) ) {
408 continue;
409 }
410 $rules_uploads_result .= $rule['rules'];
411 }
412
413 $has_block = preg_match( '/#Begin Really Simple Security.*?#End Really Simple Security/is', $content );
414 if ( ! empty( $rules_uploads_result ) || $has_block ) {
415 if ( ! is_file( $htaccess_uploads ) ) {
416 file_put_contents( $htaccess_uploads, '', LOCK_EX );
417 }
418 $new_block = empty( $rules_uploads_result ) ? '' : $start . $rules_uploads_result . $end;
419
420 if ( ! is_writable( $htaccess_uploads ) ) {
421 update_site_option( 'rsssl_uploads_htaccess_error', 'not-writable' );
422 update_site_option( 'rsssl_uploads_htaccess_rules', $rules_uploads_result );
423 } else {
424 delete_site_option( 'rsssl_uploads_htaccess_error' );
425 delete_site_option( 'rsssl_uploads_htaccess_rules' );
426 $cleaned = preg_replace( $pattern, '', $content );
427 $new = $cleaned . "\n" . $new_block;
428 $new = preg_replace( "/\n{3,}/", "\n\n", $new );
429 if ( file_get_contents( $htaccess_uploads ) !== $new ) {
430 file_put_contents( $htaccess_uploads, $new, LOCK_EX );
431 }
432 }
433 }
434 }
435 }
436 }
437
438 /**
439 * Clear any stored htaccess errors/options.
440 */
441 function rsssl_htaccess_clear_errors(): void {
442 delete_site_option('rsssl_htaccess_error');
443 delete_site_option('rsssl_htaccess_rules');
444 delete_site_option('rsssl_uploads_htaccess_error');
445 delete_site_option('rsssl_uploads_htaccess_rules');
446 }
447
448 function rsssl_clear_legacy_rules( RSSSL_Htaccess_File_Manager $m ) {
449 foreach ( [
450 'rlrssslReallySimpleSSL',
451 'Really Simple Security',
452 'Really Simple Security Redirect',
453 ] as $marker ) {
454 $m->clear_legacy_rule( $marker );
455 }
456 }
457
458 /**
459 * Store warning blocks for later use in the mailer
460 *
461 * @param array $changed_fields
462 *
463 * @return void
464 */
465 function rsssl_gather_warning_blocks_for_mail( array $changed_fields ){
466 if (!rsssl_user_can_manage() ) {
467 return;
468 }
469
470 if ( !rsssl_get_option('send_notifications_email') ) {
471 return;
472 }
473
474 $fields = array_filter($changed_fields, static function($field) {
475 // Check if email_condition exists and call the function, else assume true
476 if ( !isset($field['email']['condition']) ) {
477 $email_condition_result = true;
478 } else if (is_array($field['email']['condition'])) {
479 //rsssl option check
480 $fieldname = array_key_first($field['email']['condition']);
481 $value = $field['email']['condition'][$fieldname];
482 $email_condition_result = rsssl_get_option($fieldname) === $value;
483 } else {
484 //function check
485 $function = $field['email']['condition'];
486 $email_condition_result = function_exists($function) && $function();
487 }
488 return isset($field['email']['message']) && $field['value'] && $email_condition_result;
489 });
490
491 if ( count($fields)===0 ) {
492 return;
493 }
494 $current_fields = get_option('rsssl_email_warning_fields', []);
495 //if it's empty, we start counting time. 30 mins later we send a mail.
496 update_option('rsssl_email_warning_fields_saved', time(), false );
497
498 $current_ids = array_column($current_fields, 'id');
499 foreach ($fields as $field){
500 if ( !in_array( $field['id'], $current_ids, true ) ) {
501 $current_fields[] = $field;
502 }
503 }
504 update_option('rsssl_email_warning_fields', $current_fields, false);
505 }
506 add_action('rsssl_after_saved_fields', 'rsssl_gather_warning_blocks_for_mail', 40);
507
508 /**
509 * Check if server uses .htaccess
510 * @return bool
511 */
512 function rsssl_uses_htaccess() {
513 //when using WP CLI, the get_server check does not work, so we assume .htaccess is being used
514 //and rely on the file exists check to catch if not.
515 if ( defined( 'WP_CLI' ) && WP_CLI ) {
516 return true;
517 }
518 return rsssl_get_server() === 'apache' || rsssl_get_server() === 'litespeed';
519 }
520
521 /**
522 * Get htaccess status
523 * @return string | bool
524 */
525 function rsssl_htaccess_status(){
526 if ( empty(get_site_option('rsssl_htaccess_rules','')) ) {
527 return false;
528 }
529 return get_site_option('rsssl_htaccess_error');
530 }
531
532 /**
533 * Get htaccess status
534 * @return string | bool
535 */
536
537 function rsssl_uploads_htaccess_status(){
538 if ( empty(get_site_option('rsssl_uploads_htaccess_rules','')) ) {
539 return false;
540 }
541 return get_site_option('rsssl_uploads_htaccess_error');
542 }
543
544 /**
545 * @return string|null
546 * Get the wp-config.php path
547 */
548 function rsssl_find_wp_config_path() {
549 if ( ! rsssl_user_can_manage() ) {
550 return null;
551 }
552
553 // Allow the wp-config.php path to be overridden via a filter.
554 $filtered_path = apply_filters( 'rsssl_wpconfig_path', '' );
555
556 // If a filtered path is provided, validate it.
557 if ( ! empty( $filtered_path ) ) {
558 $directory = dirname( $filtered_path );
559
560 // Ensure the directory exists before checking for the file.
561 if ( is_dir( $directory ) && file_exists( $filtered_path ) ) {
562 return $filtered_path;
563 }
564 }
565
566 // Limit number of iterations to 10
567 $i = 0;
568 $dir = __DIR__;
569 do {
570 $i ++;
571 if ( file_exists( $dir . "/wp-config.php" ) ) {
572 return $dir . "/wp-config.php";
573 }
574 } while ( ( $dir = realpath( "$dir/.." ) ) && ( $i < 10 ) );
575
576 return null;
577 }
578
579 /**
580 * Returns the server type of the plugin user.
581 *
582 * @return string|bool server type the user is using of false if undetectable.
583 */
584
585 function rsssl_get_server() {
586 //Allows to override server authentication for testing or other reasons.
587 if ( defined( 'RSSSL_SERVER_OVERRIDE' ) ) {
588 return RSSSL_SERVER_OVERRIDE;
589 }
590
591 $server_raw = strtolower( htmlspecialchars( $_SERVER['SERVER_SOFTWARE'], ENT_QUOTES | ENT_HTML5 ) );
592
593 //figure out what server they're using
594 if ( strpos( $server_raw, 'apache' ) !== false ) {
595 return 'apache';
596 } elseif ( strpos( $server_raw, 'nginx' ) !== false ) {
597 return 'nginx';
598 } elseif ( strpos( $server_raw, 'litespeed' ) !== false ) {
599 return 'litespeed';
600 } else { //unsupported server
601 return false;
602 }
603 }
604
605 /**
606 * @return string
607 * Generate a random prefix
608 */
609
610 function rsssl_generate_random_string($length) {
611 $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
612 $randomString = '';
613
614 for ( $i = 0; $i < $length; $i++ ) {
615 $index = rand(0, strlen($characters) - 1);
616 $randomString .= $characters[$index];
617 }
618
619 return $randomString;
620 }
621
622 /**
623 * @return string
624 *
625 * Get users as string to display
626 */
627 function rsssl_list_users_where_display_name_is_login_name() {
628
629 if ( !rsssl_user_can_manage() ) {
630 return '';
631 }
632 $users = rsssl_get_users_where_display_name_is_login( true );
633 if ( is_array( $users ) ) {
634 $ext = count($users)>=10 ? '...' : '';
635 $users = array_slice($users, 0, 10);
636 return implode( ', ', $users ).$ext;
637 }
638
639 return '';
640 }
641
642 /**
643 * Check if user e-mail is verified
644 * @return bool
645 */
646 function rsssl_is_email_verified() {
647 $verificationStatus = get_option('rsssl_email_verification_status');
648 if (rsssl_user_can_manage() && $verificationStatus == 'completed' ) {
649 return true;
650 }
651
652 // User cannot manage or status is ['started', 'email_changed']
653 return false;
654 }
655
656 function rsssl_remove_prefix_from_version($version) {
657 return preg_replace('/^[^\d]*(?=\d)/', '', $version);
658 }
659 function rsssl_version_compare($version, $compare_to, $operator = null) {
660 $version = rsssl_remove_prefix_from_version($version);
661 $compare_to = rsssl_remove_prefix_from_version($compare_to);
662 return version_compare($version, $compare_to, $operator);
663 }
664
665 function rsssl_maybe_disable_404_blocking() {
666 $option_value = get_option( 'rsssl_homepage_contains_404_resources', false );
667 // Explicitly check for boolean true or string "true"
668 return $option_value === true || $option_value === "true";
669 }
670
671 function rsssl_lock_file_exists() {
672 if ( file_exists( trailingslashit( WP_CONTENT_DIR ) . 'rsssl-safe-mode.lock' ) ) {
673 return true;
674 }
675
676 return false;
677 }
678