File "cerber-admin-settings.php"

Full Path: /home/concvitk/public_html/wp-content/plugins/wp-cerber/admin/cerber-admin-settings.php
File size: 39.53 KB
MIME-type: text/x-php
Charset: utf-8

<?php
/*
	Copyright (C) 2015-22 CERBER TECH INC., https://cerber.tech
	Copyright (C) 2015-22 Markov Gregory, https://wpcerber.com

    Licenced under the GNU GPL.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

/*

*========================================================================*
|                                                                        |
|	       ATTENTION!  Do not change or edit this file!                  |
|                                                                        |
*========================================================================*

*/

if ( ! defined( 'WPINC' ) || ! defined( 'CERBER_VER' ) ) {
	exit;
}

// Cerber's settings form in the WP dashboard
// @since 8.5.9.1
const CRB_SETTINGS_GROUP = 'cerber_settings_group';
const CRB_FIELD_PREFIX = 'crb-input-';

function cerber_admin_init() {

	if ( ! cerber_is_admin_page()
	     && ! strpos( $_SERVER['REQUEST_URI'], '/options.php' )
	     && ! nexus_is_valid_request() ) {
		return;
	}

	if ( crb_get_settings( 'top_admin_menu' ) ) {
		add_filter( 'admin_body_class', function ( $classes ) {
			return $classes . ' crb-top_admin_menu_enabled ';
		} );
	}

	cerber_wp_settings_setup( cerber_get_setting_id() );

	if ( isset( $_POST[ CRB_SETTINGS_GROUP ] ) ) {
		add_action( 'updated_option', function () {
			crb_purge_settings_cache();
		} );
	}

	if ( is_multisite() ) {
		cerber_ms_update();
	}

	if ( cerber_is_http_post()
	     && ! nexus_get_context() ) { // it's crucial

		cerber_settings_update();
	}

}

/**
 * Configure WP Settings API stuff for a given admin page
 *
 * @since 7.9.7
 *
 * @param $screen_id string
 * @param $sections array
 */
function cerber_wp_settings_setup( $screen_id, $sections = array() ) {
	if ( ! $sections && ! $sections = cerber_settings_config( array( 'screen_id' => $screen_id ) ) ) {
		return;
	}

	$option = 'cerber-' . $screen_id;
	register_setting( 'cerberus-' . $screen_id, $option );

	global $tmp;
	foreach ( $sections as $section_id => $section_config ) {

		$desc = crb_array_get( $section_config, 'desc' );

		if ( $links = crb_array_get( $section_config, 'seclinks' ) ) {
			foreach ( $links as $link ) {
				$desc .= '<span class="crb-insetting-link">[ <a target="_blank" href="' . $link[1] . '">' . $link[0] . '</a> ]</span>';
			}
		}

		if ( $doclink = crb_array_get( $section_config, 'doclink' ) ) {
			$desc .= '<span class="crb-insetting-link">[ <a class="" target="_blank" href="' . $doclink . '">' . __( 'Know more', 'wp-cerber' ) . '</a> ]</span>';
		}

		$tmp[ $section_id ] = '<span class="crb-section-desc">' . $desc . '</span>';

		add_settings_section( $section_id, crb_array_get( $section_config, 'name', '' ), function ( $sec ) {
			global $tmp;
			if ( $tmp[ $sec['id'] ] ) {
				echo $tmp[ $sec['id'] ];
			}
		}, $option );

		foreach ( $section_config['fields'] as $field => $config ) {

			if ( ( $req_wp = $config['requires_wp'] ?? false )
			     && ! crb_wp_version_compare( (string) $req_wp ) ) {
				continue;
			}

			if ( ( $cb = $config['requires_true'] ?? false )
			     && is_callable( $cb )
			     && ! $cb() ) {
				continue;
			}

			if ( in_array( $field, CRB_PRO_SETTINGS ) && ! lab_lab() ) {
				continue;
			}

			$config['setting'] = $field;
			$config['group'] = $screen_id;

			$config['type'] = $config['type'] ?? 'text';

			// Setting row (tr) class, to specify the input class use 'input_class'
			$config['class'] = '';

			if ( $config['type'] == 'hidden' ) {
				$config['class'] .= ' crb-display-none';
			}

			if ( isset( $config['enabler'] ) ) {
				$config['class'] .= crb_check_enabler( $config, crb_get_settings( $config['enabler'][0] ) );
			}

			add_settings_field( $field, crb_array_get( $config, 'title', '' ), 'cerber_field_show', $option, $section_id, $config );
		}
	}
}

function cerber_get_setting_id( $tab = null ) {
	$id = ( ! $tab ) ? cerber_get_get( 'tab', CRB_SANITIZE_ID ) : $tab;
	if ( ! $id ) {
		$id = cerber_get_wp_option_id();
	}
	if ( ! $id ) {
		$id = crb_admin_get_page();
	}
	// Mapping: some tab names (or page id) doesn't match WP setting names
	// tab => settings id
	$map = array(
		'scan_settings'    => 'scanner', // define('CERBER_OPT_S','cerber-scanner');
		'scan_schedule'    => 'schedule', // define('CERBER_OPT_E','cerber-schedule');
		'scan_policy'      => 'policies',
		'ti_settings'      => 'traffic',
		'captcha'          => 'recaptcha',
		'cerber-recaptcha' => 'antispam',
		'global_policies'  => 'users',
		'cerber-shield'    => 'user_shield',

		'cerber-nexus' => 'nexus-slave',
		'nexus_slave'  => 'nexus-slave',
	);

	crb_addon_settings_mapper( $map );

	if ( isset( $map[ $id ] ) ) {
		return $map[ $id ];
	}

	return $id;
}

/**
 * Works when updating WP options
 *
 * @return bool|string
 */
function cerber_get_wp_option_id( $option_page = null ) {

	if ( ! $option_page ) {
		$option_page = crb_array_get( $_POST, 'option_page' );
	}
	if ( $option_page && ( 0 === strpos( $option_page, 'cerberus-' ) ) ) {
		return substr( $option_page, 9 ); // 8 = length of 'cerberus-'
	}

	return false;
}

/*
 * Display a settings form on an admin page
 *
 */
function cerber_show_settings_form( $group = null ) {

	$action = '';
	if ( is_multisite() ) {
		$action = '';  // Settings API doesn't work in multisite. Post data will be handled in the cerber_ms_update()
	}
	else {
		if ( nexus_is_valid_request() ) {
			//$action = cerber_admin_link();
		}
		else {
			$action = 'options.php'; // Standard way
		}
	}

	?>
	<div class="crb-admin-form">
		<form id="crb-form-<?php echo $group; ?>" class="crb-settings" method="post" action="<?php echo $action; ?>">

			<?php

			cerber_nonce_field( 'control', true );

			settings_fields( 'cerberus-' . $group ); // option group name, the same as used in register_setting().
			do_settings_sections( 'cerber-' . $group ); // the same as used in add_settings_section()	$page

			echo '<div style="padding-left: 220px">';
			//submit_button();
			echo crb_admin_submit_button();
			echo '</div>';

			?>

			<?php echo '<input type="hidden" name="' . CRB_SETTINGS_GROUP . '" value="' . $group . '">'; ?>
		</form>
	</div>
	<?php
}

/**
 * Generates HTML for a single input field on the admin settings page.
 * Prepares setting value to display.
 *
 * @param array $config Setting field config
 */
function cerber_field_show( $config ) {

	$settings = crb_get_settings();

	$pre = '';
	$atts = '';

	$label = $config['label'] ?? '';

	if ( ! empty( $config['doclink'] ) ) {
		$label .= '<span class="crb-insetting-link">[ <a class="crb-no-wrap" target="_blank" href="' . $config['doclink'] . '">' . __( 'Know more', 'wp-cerber' ) . '</a> ]</span>';
	}

	// Unconditionally required
	$req = $config['required'] ?? false;

    if ( $req ) {
		$atts .= ' required="required" ';
	}

    // Conditionally (when enabled) required
	$required = $config['validate']['required'] ?? false;

	if ( $required ) {
		$atts .= ' data-input_required="1" ';
	}

	$placeholder = $config['placeholder'] ?? '';

	if ( ! $placeholder && ( $required || $req ) ) {
		$placeholder = __( 'Required', 'wp-cerber' );
	}

	if ( $placeholder ) {
		$atts .= ' placeholder="' . crb_attr_escape( $placeholder ) . '" ';
	}

	if ( isset( $config['disabled'] ) ) {
		$atts .= ' disabled="disabled" ';
	}

	$value = $config['value'] ?? '';

	$setting = $config['setting'] ?? '';

	if ( $setting ) {
		if ( ! $value && isset( $settings[ $setting ] ) ) {
			$value = $settings[ $setting ];
		}
		if ( ( $setting == 'loginnowp' || $setting == 'loginpath' ) && ! cerber_is_permalink_enabled() ) {
			$atts .= ' disabled="disabled" ';
		}
		if ( $setting == 'loginpath' ) {
			$pre = cerber_get_home_url() . '/';
			$value = urldecode( $value );
		}
	}

	$value = crb_attr_escape( $value );

	if ( isset( $config['list'] ) ) {
		$dlt = $config['delimiter_show'] ?? $config['delimiter'];
		$value = cerber_array2text( $value, $dlt );
	}

	$name_prefix = 'cerber-' . $config['group'];
	$name = $name_prefix . '[' . $setting . ']';

	$id = $config['id'] ?? CRB_FIELD_PREFIX . $setting;

	$class = $config['input_class'] ?? '';

	$data_atts = '';
	$ena_atts = array();
	if ( isset( $config['enabler'] ) ) {
		$ena_atts['input_enabler'] = CRB_FIELD_PREFIX . $config['enabler'][0];
		if ( isset( $config['enabler'][1] ) ) {
			$ena_atts['input_enabler_value'] = $config['enabler'][1];
		}
		foreach ( $ena_atts as $att => $val ) {
			$data_atts .= ' data-' . $att . '="' . $val . '"';
		}
	}

	switch ( $config['type'] ) {

		case 'limitz':
			$s1 = $config['group'] . '-period';
			$s2 = $config['group'] . '-number';
			$s3 = $config['group'] . '-within';

			$html = sprintf( $label,
				cerber_digi_field( $name_prefix . '[' . $s1 . ']', $settings[ $s1 ] ),
				cerber_digi_field( $name_prefix . '[' . $s2 . ']', $settings[ $s2 ] ),
				cerber_digi_field( $name_prefix . '[' . $s3 . ']', $settings[ $s3 ] ) );
			break;

		case 'attempts':
			$html = sprintf( __( '%s retries are allowed within %s minutes', 'wp-cerber' ),
				cerber_digi_field( $name_prefix . '[attempts]', $settings['attempts'] ),
				cerber_digi_field( $name_prefix . '[period]', $settings['period'] ) );
			break;

		case 'reglimit':
			$html = sprintf( __( '%s registrations are allowed within %s minutes from one IP address', 'wp-cerber' ),
				cerber_digi_field( $name_prefix . '[reglimit_num]', $settings['reglimit_num'] ),
				cerber_digi_field( $name_prefix . '[reglimit_min]', $settings['reglimit_min'], 4, 4 ) );
			break;

		case 'aggressive':
			$html = sprintf( __( 'Increase lockout duration to %s hours after %s lockouts in the last %s hours', 'wp-cerber' ),
				cerber_digi_field( $name_prefix . '[agperiod]', $settings['agperiod'] ),
				cerber_digi_field( $name_prefix . '[aglocks]', $settings['aglocks'] ),
				cerber_digi_field( $name_prefix . '[aglast]', $settings['aglast'] ) );
			break;

		case 'notify':
			$html = '<label class="crb-switch"><input class="screen-reader-text" type="checkbox" id="' . $setting . '" name="cerber-' . $config['group'] . '[' . $setting . ']" value="1" ' . checked( 1, $value, false ) . $atts . ' /><span class="crb-slider round"></span></label>'
			        . __( 'Send notification if the number of active lockouts above', 'wp-cerber' ) . ' ' .
			        cerber_digi_field( $name_prefix . '[above]', $settings['above'] ) .
			        crb_test_notify_link( array( 'channel' => 'email' ) );
			break;

		case 'citadel':
			$html = sprintf( __( 'Enable after %s failed login attempts in the last %s minutes', 'wp-cerber' ),
				cerber_digi_field( $name_prefix . '[cilimit]', $settings['cilimit'] ),
				cerber_digi_field( $name_prefix . '[ciperiod]', $settings['ciperiod'] ) . '<i ' . $data_atts . '></i>' );
			break;

		case 'checkbox':
			$html = '<div style="display: table-cell;"><label class="crb-switch"><input class="screen-reader-text" type="checkbox" id="' . $id . '" name="' . $name . '" value="1" ' . checked( 1, $value, false ) . $atts . ' /><span class="crb-slider round"></span></label></div>';
			//$html .= '<div style="display: table-cell;"><label for="' . $args['setting'] . '">' . $label . '</label></div><i ' . $data . '></i>';
			if ( $label ) {
				$html .= '<div style="display: table-cell;"><label for="' . $setting . '">' . $label . '</label></div>';
			}
			if ( $data_atts ) {
				$html .= '<i ' . $data_atts . '></i>';
			}
			break;

		case 'textarea':
			$html = '<textarea class="large-text crb-monospace" id="' . $id . '" name="' . $name . '" ' . $atts . $data_atts . '>' . $value . '</textarea>';
			if ( $label ) {
				$html .= '<br/><label class="crb-below" for="' . $setting . '">' . $label . '</label>';
			}
			break;

		case 'select':
			$html = cerber_select( $name, $config['set'], $value, $class, $id, '', $placeholder, $ena_atts );
			if ( $label ) {
				$html .= '<br/><label class="crb-below">' . $label . '</label>';
			}
			break;

		case 'role_select':
			if ( $label ) {
				$label = '<p class="crb-label-above"><label for="' . $name . '">' . $label . '</label></p>';
			}
			$html = $label . '<div class="crb-select2-multi">' . cerber_role_select( $name . '[]', $value, '', true, '' ) . '<i ' . $data_atts . '></i></div>';
			break;
		case 'checkbox_set':
			if ( $label ) {
				$label = '<p class="crb-label-above"><label for="' . $name . '">' . $label . '</label></p>';
			}
			$html = '<div class="crb-checkbox_set" style="line-height: 2em;" ' . $data_atts . '>' . $label;
			foreach ( $config['set'] as $key => $item ) {
				$v = ( ! empty( $value[ $key ] ) ) ? $value[ $key ] : 0;
				$html .= '<input type="checkbox" value="1" name="' . $name . '[' . $key . ']" ' . checked( 1, $v, false ) . $atts . '/>' . $item . '<br />';
			}
			$html .= '</div>';
			break;
		case 'reptime':
			$html = cerber_time_select( $config, $settings ) . '<i ' . $data_atts . '></i>';
			break;
		case 'timepicker':
			$html = '<input class="crb-tpicker" type="text" size="7" id="' . $setting . '" name="' . $name . '" value="' . $value . '"' . $atts . '/>';
			$html .= ' <label for="' . $setting . '">' . $label . '</label>';
			break;
		case 'hidden':
			$html = '<input type="hidden" id="' . $setting . '" class="crb-hidden-field" name="' . $name . '" value="' . $value . '" />';
			break;
		case 'text':
		default:

		    $type = $config['type'] ?? 'text';

			if ( in_array( $type, array( 'url', 'number', 'email' ) ) ) {
				$input_type = $type;
			}
			else {
				$input_type = 'text';
			}

			$size = '';
			$class = '';

			if ( $type == 'digits' ) {
				$size = '3';
				$class = 'crb-digits';
			}

		    $size = $config['size'] ?? $size;
			$maxlength = $config['maxlength'] ?? $size;

			if ( $maxlength ) {
				$maxlength = ' maxlength="' . $maxlength . '" ';
			}

			if ( $size ) {
				$size = ' size="' . $size . '"';
			}
			else {
				$class = 'crb-wide';
			}


			if ( isset( $config['pattern'] ) ) {
				$atts .= ' pattern="' . $config['pattern'] . '"';
			}

			if ( isset( $config['attr'] ) ) {
				foreach ( $config['attr'] as $at_name => $at_value ) {
					$atts .= ' ' . $at_name . ' ="' . $at_value . '" ';
				}
			}
			else {
				if ( isset( $config['title'] ) ) {
					$atts .= ' title="' . $config['title'] . '"';
				}
			}

			$html = $pre . '<input type="' . $input_type . '" id="' . $setting . '" name="' . $name . '" value="' . $value . '"' . $atts . ' class="' . $class . '" ' . $size . $maxlength . $atts . $data_atts . ' />';

			if ( $label ) {
				if ( ! $size || crb_array_get( $config, 'label_pos' ) == 'below' ) {
					$label = '<br/><label class="crb-below" for="' . $setting . '">' . $label . '</label>';
				}
				else {
					$label = ' <label for="' . $setting . '">' . $label . '</label>';
				}
			}

			$html .= $label;

		break;
	}

	if ( $loh = $config['act_relation'] ?? false ) {
		foreach ( $loh as $item ) {
			if ( in_array( $value, $item[0] ) ) {
				$html .= '<span class="crb-insetting-link">[ <a href="' . cerber_admin_link( 'activity', $item[1] ) . '" target="_blank">' . $item[2] . '</a> ]</span>';
			}
		}
	}

	if ( ! empty( $config['field_switcher'] ) ) {
		$name = 'cerber-' . $config['group'] . '[' . $setting . '-enabled]';
		$value = $settings[ $setting . '-enabled' ] ?? 0;
		$checkbox = '<label class="crb-switch"><input class="screen-reader-text" type="checkbox" id="' . $setting . '-enabled" name="' . $name . '" value="1" ' . checked( 1, $value, false ) . ' /><span class="crb-slider round"></span></label><span>' . $config['field_switcher'] . '</span>';
		$html = $checkbox . ' ' . $html;
	}

	echo '<div class="crb-settings-field">';
	echo $html;

	if ( ! empty( $config['callback_under'] )
	     && $content = call_user_func( $config['callback_under'] ) ) {
		echo '<div class="crb-settings-under">';
		echo $content;
		echo '</div>';
	}

	echo "</div>\n";
}

function cerber_role_select( $name = 'cerber-roles', $selected = array(), $class = '', $multiple = '', $placeholder = '', $width = '75%' ) {

	if ( ! is_array( $selected ) ) {
		$selected = array( $selected );
	}
	if ( ! $placeholder ) {
		$placeholder = __( 'Select one or more roles', 'wp-cerber' );
	}
	$roles = wp_roles();
	$options = array();
	foreach ( $roles->get_names() as $key => $title ) {
		$s         = ( in_array( $key, $selected ) ) ? 'selected' : '';
		$options[] = '<option value="' . $key . '" ' . $s . '>' . $title . '</option>';
	}

	$m = ( $multiple ) ? 'multiple="multiple"' : '';

	// Setting width via class is not working
	$style = '';
	if ( $width ) {
		$style = 'width: ' . $width.';';
	}

	return ' <select style="' . $style . '" name="' . $name . '" class="crb-select2 ' . $class . '" ' . $m . ' data-placeholder="' . $placeholder . '" data-allow-clear="true">' . implode( "\n", $options ) . '</select>';
}

function cerber_time_select($args, $settings){

	// Week
	$php_week = array(
		__( 'Sunday' ),
		__( 'Monday' ),
		__( 'Tuesday' ),
		__( 'Wednesday' ),
		__( 'Thursday' ),
		__( 'Friday' ),
		__( 'Saturday' ),
	);
	$field = $args['setting'].'-day';
	$selected = $settings[ $field ] ?? '';
	$ret = cerber_select( 'cerber-' . $args['group'] . '[' . $field . ']', $php_week, $selected );
	$ret .= ' &nbsp; ' . _x( 'at', 'preposition of time like: at 11:00', 'wp-cerber' ) . ' &nbsp; ';

	// Hours
	$hours = array();
	for ( $i = 0; $i <= 23; $i ++ ) {
		$hours[] = str_pad( $i, 2, '0', STR_PAD_LEFT ) . ':00';
	}
	$field = $args['setting'] . '-time';
	$selected = $settings[ $field ] ?? '';
	$ret .= cerber_select( 'cerber-' . $args['group'] . '[' . $field . ']', $hours, $selected );

	return $ret . crb_test_notify_link( array( 'type' => 'report' ), __( 'Click to send now', 'wp-cerber' ) );
}

function cerber_checkbox( $name, $value, $label = '', $id = '', $atts = '' ) {
	if ( ! $id ) {
		$id = CRB_FIELD_PREFIX . $name;
	}

	return '<div style="display: table-cell;"><label class="crb-switch"><input class="screen-reader-text" type="checkbox" id="' . $id . '" name="' . $name . '" value="1" ' . checked( 1, $value, false ) . $atts . ' /><span class="crb-slider round"></span></label></div>
	<div style="display: table-cell;"><label for="' . $id . '">' . $label . '</label></div>';
}

function cerber_digi_field( $name, $value = '', $size = '3', $maxlength = '3', $id = '' ) {
	return cerber_txt_field( $name, $value, $id, $size, $maxlength, '\d+', 'crb-digits' );
}

function cerber_txt_field( $name, $value = '', $id = '', $size = '', $maxlength = '', $pattern = '', $class = '' ) {
	$atts = '';
	if ( $id ) {
		$atts .= ' id="' . $id . '" ';
	}
	if ( $class ) {
		$atts .= ' class="' . $class . '" ';
	}
	if ( $size ) {
		$atts .= ' size="' . $size . '" ';
	}
	if ( $maxlength ) {
		$atts .= ' maxlength="' . $maxlength . '" ';
	}
	if ( $pattern ) {
		$atts .= ' pattern="' . $pattern . '" ';
	}

	return '<input type="text" name="' . $name . '" value="' . $value . '" ' . $atts . ' />';
}

function cerber_nonce_field( $action = 'control', $echo = false ) {
	$sf = '';
	if ( nexus_is_valid_request() ) {
		$sf = '<input type="hidden" name="cerber_nexus_seal" value="' . nexus_request_data()->seal . '">';
	}
	$nf = wp_nonce_field( $action, 'cerber_nonce', false, false );
	if ( ! $echo ) {
		return $nf . $sf;
	}

	echo $nf . $sf;
}

function crb_admin_submit_button( $text = '', $echo = false ) {
	if ( ! $text ) {
		$text = __( 'Save Changes' );
	}

	$d    = '';
	$hint = '';
	if ( nexus_is_valid_request() && ! nexus_is_granted( 'submit' ) ) {
		$d    = 'disabled="disabled"';
		$hint = ' not available in the read-only mode';
	}

	$html = '<p class="submit"><input ' . $d . ' type="submit" name="submit" id="submit" class="button button-primary" value="' . $text . '"  /> ' . $hint . '</p>';
	if ( $echo ) {
		echo $echo;
	}

	return $html;
}

/*
	Sanitizing users input for Main Settings
*/
add_filter( 'pre_update_option_'.CERBER_OPT, function ($new, $old, $option) {

	$ret = cerber_set_boot_mode( $new['boot-mode'], $old['boot-mode'] );
	if ( crb_is_wp_error( $ret ) ) {
		cerber_admin_notice( __( 'ERROR:', 'wp-cerber' ) . ' ' . $ret->get_error_message() );
		cerber_admin_notice( __( 'Plugin initialization mode has not been changed', 'wp-cerber' ) );
		$new['boot-mode'] = $old['boot-mode'];
	}

	$new['attempts'] = absint( $new['attempts'] );
	$new['period']   = absint( $new['period'] );
	$new['lockout']  = absint( $new['lockout'] );

	$new['agperiod'] = absint( $new['agperiod'] );
	$new['aglocks']  = absint( $new['aglocks'] );
	$new['aglast']   = absint( $new['aglast'] );

	if ( cerber_is_permalink_enabled() ) {
		$new['loginpath'] = urlencode( str_replace( '/', '', $new['loginpath'] ) );
		$new['loginpath'] = sanitize_text_field( $new['loginpath'] );

		if ( $new['loginpath'] ) {
			if ( $new['loginpath'] == 'wp-admin'
			     || preg_match( '/[#|\.\!\?\:\s]/', $new['loginpath'] ) ) {
				cerber_admin_notice( __( 'ERROR:', 'wp-cerber' ) . ' You may not set this value for Custom login URL. Please specify another one.' );
				$new['loginpath'] = $old['loginpath'];
			}
			elseif ( $new['loginpath'] != $old['loginpath'] ) {
				$href    = cerber_get_home_url() . '/' . $new['loginpath'] . '/';
				$url     = urldecode( $href );
				$msg     = array();
				$msg_e   = array();
				$msg[]   = __( 'Attention! You have changed the login URL! The new login URL is', 'wp-cerber' ) . ': <a href="' . $href . '">' . $url . '</a>';
				$msg_e[] = __( 'Attention! You have changed the login URL! The new login URL is', 'wp-cerber' ) . ': ' . $url;
				$msg[]   = __( 'If you use a caching plugin, you have to add your new login URL to the list of pages not to cache.', 'wp-cerber' );
				$msg_e[] = __( 'If you use a caching plugin, you have to add your new login URL to the list of pages not to cache.', 'wp-cerber' );
				cerber_admin_notice( $msg );
				cerber_send_notification( 'newlurl', array( 'text' => $msg_e ) );
			}
		}
	}
	else {
		$new['loginpath'] = '';
		$new['loginnowp'] = 0;
	}

	if ( $new['loginnowp'] && empty( $new['loginpath'] ) && ! class_exists( 'WooCommerce' ) ) {
		cerber_admin_notice( array(
			'<b>' . __( 'Heads up!' ) . '</b>',
			__( 'You have disabled the default login page. Ensure that you have configured an alternative login page. Otherwise, you will not be able to log in.', 'wp-cerber' )
		) );
	}

	$new['ciduration'] = absint( $new['ciduration'] );
	$new['cilimit']    = absint( $new['cilimit'] );
	$new['cilimit']    = $new['cilimit'] == 0 ? '' : $new['cilimit'];
	$new['ciperiod']   = absint( $new['ciperiod'] );
	$new['ciperiod']   = $new['ciperiod'] == 0 ? '' : $new['ciperiod'];
	if ( ! $new['cilimit'] ) {
		$new['ciperiod'] = '';
	}
	if ( ! $new['ciperiod'] ) {
		$new['cilimit'] = '';
	}

	$new['keeplog'] = absint( $new['keeplog'] );

	if ( $new['keeplog'] == 0 ) {
		$new['keeplog'] = 1;
	}

	if ( $new['cookiepref'] != $old['cookiepref'] ) {
		crb_update_cookie_dependent();
	}

	return $new;
}, 10, 3 );

/*
	Sanitizing/checking user input for anti-spam tab settings
*/
add_filter( 'pre_update_option_' . CERBER_OPT_A, function ( $new, $old, $option ) {

	if ( empty( $new['botsany'] ) && empty( $new['botscomm'] ) && empty( $new['botsreg'] ) ) {
		update_site_option( 'cerber-antibot', '' );
	}

	$warn = false;

	if ( ! empty( $new['botsany'] )
         && crb_array_get( $new, 'botsany' ) != crb_array_get( $old, 'botsany' ) ) {
		$warn = true;
	}

	if ( ! empty( $new['botscomm'] )
         && crb_array_get( $new, 'botscomm' ) != crb_array_get( $old, 'botscomm' ) ) {
		$warn = true;
	}

	if ( ! empty( $new['customcomm'] ) ) {
		if ( ! crb_get_compiled( 'custom_comm_slug' ) ) {
			crb_update_compiled( 'custom_comm_slug', crb_random_string( 20, 30 ) );
			crb_update_compiled( 'custom_comm_mark', crb_random_string( 20, 30 ) );
			$warn = true;
		}
	}
	else {
		if ( crb_get_compiled( 'custom_comm_slug' ) ) {
			crb_update_compiled( 'custom_comm_slug', '' );
			$warn = true;
		}
	}

	if ( $warn ) {
		cerber_admin_notice( array(
			'<b>' . __( 'Important note if you have a caching plugin in place', 'wp-cerber' ) . '</b>',
			__( 'To avoid false positives and get better anti-spam performance, please clear the plugin cache.', 'wp-cerber' )
		) );
	}

	return $new;
}, 10, 3 );
/*
	Sanitizing/checking user input for reCAPTCHA tab settings
*/
add_filter( 'pre_update_option_'.CERBER_OPT_C, function ($new, $old, $option) {

	// Check ability to make external HTTP requests
	if ( ! empty( $new['sitekey'] ) && ! empty( $new['secretkey'] ) ) {
		if ( ( ! $goo = get_wp_cerber()->reCaptchaRequest( '1' ) )
		     || ! isset( $goo['success'] ) ) {
			cerber_admin_notice( __( 'ERROR:', 'wp-cerber' ) . ' ' . cerber_get_labels( 'status', 534 ) );
		}
	}

	$new['recaptcha-period'] = absint( $new['recaptcha-period'] );
	$new['recaptcha-number'] = absint( $new['recaptcha-number'] );
	$new['recaptcha-within'] = absint( $new['recaptcha-within'] );

	return $new;
}, 10, 3 );
/*
	Sanitizing/checking user input for Notifications tab settings
*/
add_filter( 'pre_update_option_'.CERBER_OPT_N, function ($new, $old, $option) {

	$emails = cerber_text2array( $new['email'], ',' );

	$new['email'] = array();
	foreach ( $emails as $item ) {
		if ( is_email( $item ) ) {
			$new['email'][] = $item;
		}
		else {
			cerber_admin_notice( __( '<strong>ERROR</strong>: please enter a valid email address.' ) );
		}
	}

	$emails = cerber_text2array( $new['email-report'], ',' );

	$new['email-report'] = array();
	foreach ( $emails as $item ) {
		if ( is_email( $item ) ) {
			$new['email-report'][] = $item;
		}
		else {
			cerber_admin_notice( __( '<strong>ERROR</strong>: please enter a valid email address.' ) );
		}
	}

	$new['emailrate'] = absint( $new['emailrate'] );

	// When we install a new token, we set proper default value for the device setting

	if ( $new['pbtoken'] != $old['pbtoken'] ) {

		if ( ! $new['pbtoken'] ) {
			$new['pbdevice'] = '';
		}
		else {

			$list = cerber_pb_get_devices( $new['pbtoken'] );

			if ( is_array( $list ) && ! empty( $list ) ) {
				$new['pbdevice'] = 'all';
			}
			else {
				$new['pbdevice'] = '';
			}
		}
	}

	return $new;
}, 10, 3 );

/*
    Sanitizing/checking user input for Hardening tab settings
*/
add_filter( 'pre_update_option_'.CERBER_OPT_H, function ($new, $old, $option) {

	$new['restwhite'] = cerber_text2array( $new['restwhite'], "\n", function ( $v ) {
		$v = preg_replace( '/[^a-z_\-\d\/]/i', '', $v );

		return trim( $v, '/' );
	} );

	$result = cerber_htaccess_sync( 'main', $new );
	if ( crb_is_wp_error( $result ) ) {
		$new['adminphp'] = $old['adminphp'];
		cerber_admin_notice( $result->get_error_message() );
	}

	$result = cerber_htaccess_sync( 'media', $new );
	if ( crb_is_wp_error( $result ) ) {
		$new['phpnoupl'] = $old['phpnoupl'];
		cerber_admin_notice( $result->get_error_message() );
	}

	return $new;
}, 10, 3 );
/*
    Sanitizing/checking user input for Traffic Inspector tab settings
*/
add_filter( 'pre_update_option_'.CERBER_OPT_T, function ($new, $old, $option) {

	$new['tiwhite'] = cerber_text2array( $new['tiwhite'], "\n" );
	foreach ( $new['tiwhite'] as $item ) {
		if ( strrpos( $item, '?' ) ) {
			cerber_admin_notice( 'You may not specify the query string with a question mark: ' . htmlspecialchars( $item, ENT_SUBSTITUTE ) );
		}
		if ( strrpos( $item, '://' ) ) {
			cerber_admin_notice( 'You may not specify the full URL: ' . htmlspecialchars( $item, ENT_SUBSTITUTE ) );
		}
	}

	if ( $new['tithreshold'] ) {
		$new['tithreshold'] = absint( $new['tithreshold'] );
	}

	$new['tikeeprec'] = absint( $new['tikeeprec'] );
	if ( $new['tikeeprec'] == 0 ) {
		$new['tikeeprec'] = 1;
		cerber_admin_notice( 'You may not set <b>Keep records for</b> to 0 days. To completely disable logging set <b>Logging mode</b> to Logging disabled.' );
	}

	return $new;
}, 10, 3 );

add_filter( 'pre_update_option_' . CERBER_OPT_US, function ( $new, $old, $option ) {

	if ( ! empty( $new['ds_4acc'] ) ) {
		CRB_DS::enable_shadowing( 1 );
	}
	else {
		CRB_DS::disable_shadowing( 1 );
	}

	if ( ! empty( $new['ds_4roles'] ) ) {
		CRB_DS::enable_shadowing( 2 );
	}
	else {
		CRB_DS::disable_shadowing( 2 );
	}

	return $new;
}, 10, 3 );

add_filter( 'pre_update_option_' . CERBER_OPT_OS, function ( $new, $old, $option ) {

	if ( ! empty( $new['ds_4opts'] ) ) {
		CRB_DS::enable_shadowing( 3 );
	}
	else {
		CRB_DS::disable_shadowing( 3 );
	}

	return $new;
}, 10, 3 );

/*
    Sanitizing/checking user input for Security Scanner settings
*/
add_filter( 'pre_update_option_' . CERBER_OPT_S, function ( $new, $old, $option ) {

	$new['scan_exclude'] = cerber_normal_dirs( $new['scan_exclude'] );

	return $new;
}, 10, 3 );

/*
    Sanitizing/checking user input for Scanner Schedule settings
*/
add_filter( 'pre_update_option_' . CERBER_OPT_E, function ( $new, $old, $option ) {
	$new['scan_aquick']        = absint( $new['scan_aquick'] );
	$new['scan_afull-enabled'] = ( empty( $new['scan_afull-enabled'] ) ) ? 0 : 1;

	$sec = cerber_sec_from_time( $new['scan_afull'] );
	if ( ! $sec || ! ( $sec >= 0 && $sec <= 86400 ) ) {
		$new['scan_afull'] = '01:00';
	}

	$emails = cerber_text2array( $new['email-scan'], ',' );
	$new['email-scan'] = array();
	foreach ( $emails as $item ) {
		if ( is_email( $item ) ) {
			$new['email-scan'][] = $item;
		}
		else {
			cerber_admin_notice( __( '<strong>ERROR</strong>: please enter a valid email address.' ) );
		}
	}

	if ( lab_lab() ) {
		if ( cerber_cloud_sync( $new ) ) {
			cerber_admin_message( __( 'The schedule has been updated', 'wp-cerber' ) );
		}
		else {
			cerber_admin_message( __( 'Unable to update the schedule', 'wp-cerber' ) );
		}
	}

	return $new;
}, 10, 3 );

add_filter( 'pre_update_option_' . CERBER_OPT_P, function ( $new, $old, $option ) {

	$new['scan_delexdir'] = cerber_normal_dirs($new['scan_delexdir']);

	return $new;
}, 10, 3 );

/**
 * Let's sanitize and normalize them all
 * @since 4.1
 *
 */
add_filter( 'pre_update_option', 'cerber_o_o_sanitizer', 10, 3 );
function cerber_o_o_sanitizer( $value, $option, $old_value ) {

	if ( ! in_array( $option, cerber_get_setting_list() ) ) {
		return $value;
	}

	if ( is_array( $value ) ) {

		// Parsing settings, applying formatting, etc.

		foreach ( $value as $setting => &$setting_val ) {
			if ( ! $conf = cerber_settings_config( array( 'setting' => $setting ) ) ) {
				continue;
			}

			if ( $enabler = $conf['enabler'] ?? '' ) {
				if ( crb_check_enabler( $conf, $value[ $enabler[0] ] ) ) {
					continue;
				}
			}

			$callback = crb_array_get( $conf, 'apply' );
			$regex = crb_array_get( $conf, 'regex_filter' ); // Filtering out not allowed chars
			$validate = crb_array_get( $conf, 'validate' );

			if ( isset( $conf['list'] ) ) {
				// Process the values
				$setting_val = cerber_text2array( $setting_val, $conf['delimiter'], $callback, $regex );

				// Remove not allowed values
				global $_deny;
				if ( $_deny = crb_array_get( $conf, 'deny_filter' ) ) {
					$setting_val = array_filter( $setting_val, function ( $e ) {
						global $_deny;

						return ! in_array( $e, $_deny );
					} );
				}
			}
			else {
				// Process the value
				if ( $callback && is_callable( $callback ) ) {
					$setting_val = call_user_func( $callback, $setting_val );
				}
				if ( $regex ) {
					$setting_val = mb_ereg_replace( $regex, '', $setting_val );
				}
				// Validating the value
				if ( $validate ) {

					$field_name = $conf['title'] ?? $conf['label'] ?? 'Unknown field';
					$error_msg = '';

					if ( ! empty( $validate['required'] )
					     && ! $setting_val ) {
						$error_msg = sprintf( __( 'Field %s may not be empty', 'wp-cerber' ), '<b>' . $field_name . '</b>' );
					}
                    elseif ( $setting_val
					         && ( $sat = $validate['satisfy'] ?? false )
					         && is_callable( $sat )
					         && ! call_user_func( $sat, $setting_val ) ) {
						$error_msg = sprintf( __( 'Field %s contains an invalid value', 'wp-cerber' ), '<b>' . $field_name . '</b>' );
					}

					if ( $error_msg ) {
						cerber_admin_notice( '<b>' . __( 'ERROR:' ) . '</b> ' . $error_msg );
					}
				}
			}
		}
	}

	if ( is_array( $value ) ) {
		array_walk_recursive( $value, function ( &$element, $key ) {
			if ( ! is_array( $element ) ) {
				$element = sanitize_text_field( (string) $element );
			}
		} );
	}
	else {
		$value = sanitize_text_field( (string) $value );
	}

    $value = cerber_normalize( $value, $option );

	return $value;
}

function cerber_normal_dirs( $list = array() ) {
	if ( ! is_array( $list ) ) {
		$list = cerber_text2array( $list, "\n" );
	}
	$ready = array();

	foreach ( $list as $item ) {
		$item = rtrim( cerber_normal_path( $item ), '/\\' ) . DIRECTORY_SEPARATOR;
		if ( ! @is_dir( $item ) ) {
			$dir = cerber_get_abspath() . ltrim( $item, DIRECTORY_SEPARATOR );
			if ( ! @is_dir( $dir ) ) {
				cerber_admin_notice( 'Directory does not exist: ' . htmlspecialchars( $item, ENT_SUBSTITUTE ) );
				continue;
			}
			$item = $dir;
		}
		$ready[] = $item;
	}

	return $ready;
}

/*
 * Save settings on the multisite WP.
 * Process POST Form for settings screens.
 * Because Settings API doesn't work in multisite mode!
 *
 */
function cerber_ms_update() {
	if ( ! cerber_is_http_post() || ! isset( $_POST['action'] ) || $_POST['action'] != 'update' ) {
		return;
	}

	if ( ! $wp_id = cerber_get_wp_option_id() ) {  // 7.9.7
		return;
	}

	if ( ! cerber_user_can_manage() ) {
		return;
	}

	// See wp_nonce_field() in the settings_fields() function
	check_admin_referer($_POST['option_page'].'-options');

	$opt_name = 'cerber-' . $wp_id;

	$old = (array) get_site_option( $opt_name );
	$new = $_POST[ $opt_name ];
	$new = apply_filters( 'pre_update_option_' . $opt_name, $new, $old, $opt_name );

	$new = cerber_normalize( $new, $opt_name ); // @since 8.5.1

	cerber_update_site_option( $opt_name, $new );
}

/**
 * An intermediate level for update_site_option() for Cerber's settings.
 * Goal: have a more granular control over processing settings.
 *
 * @since 8.5.9.1
 *
 * @param string $option_name
 * @param $value
 *
 * @return bool
 */
function cerber_update_site_option( $option_name, $value ) {

	$result = update_site_option( $option_name, $value );

	cerber_settings_update();

	crb_purge_settings_cache();

	return $result;
}

/**
 * Updates Cerber's settings in a new way
 *
 * @since 8.6
 *
 */
function cerber_settings_update() {

	if ( ! cerber_is_http_post()
	     || ! $group = crb_get_post_fields( CRB_SETTINGS_GROUP ) ) {
		return;
	}

	// We do not process some specific cases - not a real settings form
	if ( defined( 'CRB_NX_SLAVE' ) && $group == CRB_NX_SLAVE ) {
		return;
	}

	if ( ! $remote = nexus_is_valid_request() ) {
		if ( ! cerber_user_can_manage() ) {
			return;
		}

		// See wp_nonce_field() in the settings_fields() function
		check_admin_referer( $_POST['option_page'] . '-options' );
	}

	$sections = cerber_settings_config( array( 'screen_id' => $group ) );
	$field_keys = array();
	$diag_log = array();

	foreach ( $sections as $sec ) {
		if ( $fls = crb_array_get( $sec, 'fields' ) ) {

			$field_keys = array_merge( $field_keys, array_keys( $fls ) );

            // Collect settings to be logged if they have been changed

			foreach ( $fls as $id => $config ) {
				if ( $msg = crb_array_get( $config, 'diag_log' ) ) {
					$diag_log[ $id ] = $msg;
				}
			}
		}
	}

	$fields = array_fill_keys( $field_keys, '' );
	$post_fields = crb_get_post_fields( 'cerber-' . $group, array() );
	crb_trim_deep( $post_fields );
	$post_fields = stripslashes_deep( $post_fields );
	crb_sanitize_deep( $post_fields ); // removes all tags

	$new_settings = array_merge( $fields, $post_fields );

	if ( ( ! $old_settings = get_site_option( CERBER_CONFIG ) )
	     || ! is_array( $old_settings ) ) {
		$old_settings = array();
	}

	$settings = array_merge( $old_settings, $new_settings );

	$changed = array();

	foreach ( $new_settings as $key => $val ) {
		if ( ! isset( $old_settings[ $key ] ) || $old_settings[ $key ] !== $val ) {
			$changed[] = $key;
		}
	}

	$equal = empty( $changed );
	$result = null;

	if ( ! $equal ) {
		$result = update_site_option( CERBER_CONFIG, $settings );
	}

	$data = array(
		'group'      => $group,
		'equal'      => $equal,
		'result'     => $result,
		'remote'     => $remote,
		'changed'    => $changed,
		'new_values' => $new_settings,
		'old_values' => $old_settings,
	);

	if ( $result ) {
		crb_journaling( $data, $diag_log );
	}

	crb_event_handler( 'update_settings', $data );

}

/**
 * Logs changes of the plugin settings to the diagnostic log.
 * To enable logging, a setting has to have a 'diag_log' field in the setting config.
 *
 * @param array $data Information about updated settings.
 * @param array $list The list of settings that have to be logged to the diagnostic log.
 *
 * @return void
 *
 * @since 9.0.1
 */
function crb_journaling( $data, $list ) {
	if ( $data['equal'] ) {
		return;
	}

	if ( ! $changed = array_intersect( array_keys( $list ), $data['changed'] ) ) {
		return;
	}

	foreach ( $changed as $key ) {

		$what = $list[ $key ];

		if ( empty( $data['new_values'][ $key ] ) ) {
			$msg = 'Disabled: ' . $what;
		}
		else {
			$msg = 'Enabled: ' . $what;
		}

		cerber_diag_log( $msg, '*' );
	}

}

/**
 * Escaping attributes (values) for forms
 *
 * @param array|string $value
 *
 * @return array|string
 */
function crb_attr_escape( $value ) {
	if ( is_array( $value ) ) {
		array_walk_recursive( $value, function ( &$element ) {
			$element = crb_escape( $element );
		} );
	}
	else {
		$value = crb_escape( $value );
	}

	return $value;
}

/**
 * Helper
 *
 * @param string $val
 *
 * @return string Escaped string
 */
function crb_escape( $val ) {
	if ( ! $val
	     || is_numeric( $val ) ) {
		return $val;
	}

	// the same way as in esc_attr();
	return _wp_specialchars( $val, ENT_QUOTES );
}

/**
 * Check setting field enabler and returns conditional inputs CSS class
 *
 * @param array $config The config of a setting field
 * @param mixed $enab_val The value of the enabler field
 *
 * @return string CSS class to be used
 */
function crb_check_enabler( $config, $enab_val ) {
	if ( ! isset( $config['enabler'] ) ) {
		return '';
	}

	$enabled = true;

	if ( isset( $config['enabler'][1] ) ) {
		$target_val = $config['enabler'][1];
		if ( 0 === strpos( $target_val, '[' ) ) {
			$list = json_decode( $target_val );
			if ( ! in_array( $enab_val, $list ) ) {
				$enabled = false;
			}
		}
		else {
			if ( $enab_val != $target_val ) {
				$enabled = false;
			}
		}
	}
	else {
		if ( empty( $enab_val ) ) {
			$enabled = false;
		}
	}

	return ( ! $enabled ) ? ' crb-disable-this' : '';
}