<?php
/**
 *
 * @author        Webcraftic <wordpress.webraftic@gmail.com>
 * @version       1.0
 * @copyright (c) 10.02.2020, Webcraftic
 */

use WBCR\Titan\Cert\Cert;
use WBCR\Titan\Client\Client;
use WBCR\Titan\Client\Entity\CmsCheckItem;
use WBCR\Titan\Logger\Writter;
use WBCR\Titan\MalwareScanner\HashListPool;
use WBCR\Titan\MalwareScanner\Result;
use WBCR\Titan\MalwareScanner\Scanner;
use WBCR\Titan\MalwareScanner\Signature;
use WBCR\Titan\Plugin;

add_action('plugins_loaded', 'titan_digest_schedule');
function titan_digest_schedule()
{
	$digest = Plugin::app()->getOption('digest', 'disable');

	if( $digest == 'enable' ) {
		if( !wp_next_scheduled('titan_malware_weekly_digest') ) {
			wp_schedule_event(time(), 'weekly', 'titan_malware_weekly_digest');
		}
	} else {
		wp_unschedule_hook('titan_malware_weekly_digest');
	}
}

add_filter('cron_schedules', 'titan_add_minute_schedule');
/**
 * @param array $schedules
 */
function titan_add_minute_schedule($schedules)
{
	$schedules['minute'] = [
		'interval' => 30,
		'display' => __('Once 30 sec', 'titan-security'),
	];

	$schedules['weekly'] = [
		'interval' => 86400 * 7, // 7 days
		'display' => __('Weekly', 'titan-security'),
	];

	return $schedules;
}

add_action('titan_malware_weekly_digest', 'titan_malware_weekly_digest');
function titan_malware_weekly_digest()
{
	/**
	 * @var Result[] $matched
	 */
	$matched = get_option(Plugin::app()->getPrefix() . 'matched_weekly', []);

	if( empty($matched) ) {
		$matched = get_option(Plugin::app()->getPrefix() . 'scanner_malware_matched', []);
		if( !empty($matched) ) {
			Plugin::app()->updatePopulateOption('matched_weekly', $matched);
		}
	}

	Writter::info("Sending weekly digest");

	$license_key = Plugin::app()->is_premium() ? Plugin::app()->premium->get_license()->get_key() : '';
	$site = get_option('home');
	$client = new Client($license_key);
	$client->send_notification('email', 'digestWeekly', get_option('admin_email'), [
		'infectedFiles' => $matched,
		'vulnerabilities' => [
			'wordpress' => get_option(Plugin::app()->getPrefix() . 'vulnerabilities_wordpress', []),
			'plugins' => get_option(Plugin::app()->getPrefix() . 'vulnerabilities_plugins', []),
			'themes' => get_option(Plugin::app()->getPrefix() . 'vulnerabilities_themes', []),
		],
		'audit' => get_option(Plugin::app()->getPrefix() . 'audit_results', []),
		'subject' => "[{$site}] Weekly security digest",
	]);
}

add_action('titan_scheduled_scanner', 'titan_scheduled_scanner');
/**
 * @throws Exception
 */
function titan_scheduled_scanner()
{
	require_once WTITAN_PLUGIN_DIR . '/libs/api-client/boot.php';
	require_once WTITAN_PLUGIN_DIR . '/includes/scanner/classes/scanner/boot.php';

	/** @var Scanner $scanner */
	$scanner = get_option(Plugin::app()->getPrefix() . 'scanner', null);
	if( is_null($scanner) || $scanner === false ) {
		Writter::error('Scanner does not exists');
		error_log('Scanner does not exists');
		titan_remove_scheduler_scanner();

		return;
	}

	set_time_limit(0);

	$speed = Plugin::app()->getPopulateOption('scanner_speed', 'slow');
	$files_count = @Scanner::SPEED_FILES[$speed];
	if( is_null($files_count) ) {
		$files_count = Scanner::SPEED_FILES[Scanner::SPEED_MEDIUM];
	}

	$matched = Plugin::app()->getOption('scanner_malware_matched', []);

	foreach($scanner->scan($files_count) as $match) {
		/** @var Result $match */
		if( $match->getSignature()->getSever() === Signature::SEVER_CRITICAL ) {
			array_unshift($matched, $match);
		} else {
			array_push($matched, $match);
		}
	}

	Plugin::app()->updateOption('scanner_malware_matched', $matched);
	Plugin::app()->updateOption('scanner', $scanner);

	if( $scanner->get_files_count() < 1 ) {
		titan_remove_scheduler_scanner();
	}
}

/**
 * @return CmsCheckItem[]
 */
function titan_check_cms()
{
	global $wp_version;

	if( Plugin::app()->is_premium() ) {
		$license_key = Plugin::app()->premium->get_license()->get_key();
	} else {
		$license_key = null;
	}

	$client = new Client($license_key);

	if( Plugin::app()->is_premium() ) {
		$result = $client->check_cms_premium($wp_version, collect_wp_hash_sum());
	} else {
		$result = $client->check_cms_free($wp_version, collect_wp_hash_sum());
	}

	if( is_null($result) ) {
		return [];
	}

	WBCR\Titan\Logger\Writter::info(sprintf("Founded %d corrupted files", count($result->items)));

	foreach($result->items as $check_item) {
		WBCR\Titan\Logger\Writter::debug(sprintf("File `%s` (action %s)", $check_item->path, $check_item->action));
		$path = dirname(WP_CONTENT_DIR) . '/' . $check_item->path;
		switch( $check_item->action ) {
			case CmsCheckItem::ACTION_REMOVE:
				if( file_exists($path) && is_writable($path) ) {
					unlink($path);
				}
				break;

			case CmsCheckItem::ACTION_REPAIR:
				if( file_exists($path) && is_writeable($path) ) {
					$data = file_get_contents($check_item->url);
					file_put_contents($path, $data);
				}
				break;
		}
	}

	return $result->items;
}

#region scanner

/**
 * Creating cron task
 *
 * @param string|string[] $path
 * @param string[]|null   $plugin_in_action
 * @param bool            $activate
 */
function titan_create_scheduler_scanner($path = ABSPATH, $plugin_in_action = null, $activate = false)
{
	// todo: реализовать уровень проверки сайта

	if( Plugin::app()->is_premium() ) {
		$license_key = Plugin::app()->premium->get_license()->get_key();
	} else {
		$license_key = null;
	}

	$client = new Client($license_key);

	if( Plugin::app()->is_premium() ) {
		$signatures = $client->get_signatures();
	} else {
		$signatures = $client->get_free_signatures();
	}

	/** @var array[]|WBCR\Titan\Client\Entity\Signature[] $signatures */

	foreach($signatures as $key => $signature) {
		$signatures[$key] = $signature->to_array();
	}
	$signature_pool = WBCR\Titan\MalwareScanner\SignaturePool::fromArray($signatures);

	$file_hash = get_option(Plugin::app()->getPrefix() . 'files_hash');
	if( !$file_hash ) {
		$file_hash_pool = HashListPool::fromArray($file_hash);
	} else {
		$file_hash_pool = null;
	}

	$scanner = new WBCR\Titan\MalwareScanner\Scanner($path, $signature_pool, $file_hash_pool, [
		'wp-admin',
		'wp-includes',
		'wp-activate.php',
		'wp-blog-header.php',
		'wp-comments-post.php',
		'wp-config-sample.php',
		'wp-cron.php',
		'wp-links-opml.php',
		'wp-load.php',
		'wp-login.php',
		'wp-mail.php',
		'wp-settings.php',
		'wp-signup.php',
		'wp-trackback.php',
		'xmlrpc.php',
		'debug.log',
		'node_modules',
		'vendor',
		'wp-plugin-titan-security',
		'anti-spam',
	]);

	Plugin::app()->updateOption('scanner', $scanner);
	Plugin::app()->updateOption('scanner_plugins_in_action', $plugin_in_action);
	Plugin::app()->updateOption('scanner_activate_plugin', $activate ? 'yes' : 'no');
	Plugin::app()->updateOption('scanner_malware_matched', []);
	Plugin::app()->updateOption('scanner_files_count', $scanner->get_files_count());
	Plugin::app()->updateOption('scanner_status', 'started');
	wp_schedule_event(time(), 'minute', 'titan_scheduled_scanner');
}

/**
 * Deleting a cron task
 */
function titan_remove_scheduler_scanner()
{
	$scanner = get_option(Plugin::app()->getPrefix() . 'scanner');
	if( $scanner ) {
		$file_hash = [];
		foreach($scanner->getFileList() as $file) {
			$file_hash[$file->getPath()] = $file->getFileHash();
		}
		Plugin::app()->updateOption('files_hash', $file_hash);
	}

	wp_unschedule_hook('titan_scheduled_scanner');
	Plugin::app()->updateOption('scanner_status', 'stopped');

	try {
		/** @var Result[] $matched */
		$matched = get_option(Plugin::app()->getPrefix() . 'scanner_malware_matched', []);
		$weeklyMatched = get_option(Plugin::app()->getPrefix() . 'matched_weekly', []);

		$plugins = Plugin::app()->getPopulateOption('scanner_plugins_in_action', []);
		$plugin_matched = Plugin::app()->getPopulateOption('scanner_malware_plugin_matched', []);
		$activate = Plugin::app()->getPopulateOption('scanner_activate_plugin', 'no') === 'yes';

		foreach((array)$plugins as $plugin) {
			$plugin_dir = substr($plugin, 0, strpos($plugin, "/"));
			if( isset($plugin_matched[$plugin_dir]) && count($plugin_matched[$plugin_dir]) > 0 ) {
				continue;
			}

			if( $activate ) {
				activate_plugin($plugin);
			}
		}

		Plugin::app()->deletePopulateOption('scanner_plugins_in_action');
		Plugin::app()->deletePopulateOption('scanner_activate_plugin');

		$weeklyMatched = array_merge($weeklyMatched, $matched);
		//todo: глупая конструкция, так как входные данные идут массивом, а не строкой. Закоментировал, чтобы не вызывала ошибку.
		//$weeklyMatched = array_unique($weeklyMatched, SORT_STRING);
		Plugin::app()->updateOption('matched_weekly', $weeklyMatched);

		$client = new Client(null);
		$client->send_notification('email', 'digestDaily', get_option('admin_email'), ['infectedFiles' => $weeklyMatched]);

		//		if ( count( $matched ) > 0 ) {
		//			if ( Plugin::app()->is_premium() ) {
		//				$license_key = Plugin::app()->premium->get_license()->get_key();
		//			} else {
		//				$license_key = null;
		//			}
		//			$client = new Client( $license_key );
		//
		//			$client->send_notification( 'email', 'virusFound', [
		//				'subject' => 'VIRUS',
		//				'url'     => get_site_url(),
		//				'files'   => $matched
		//			] );
		//		}
	} catch( Exception $e ) {

	}
}

#endregion scanner

/**
 * Collecting hash sums of WP files
 *
 * @param string $path
 *
 * @return array
 */
function collect_wp_hash_sum($path = ABSPATH)
{
	$hash = [];

	foreach(scandir($path) as $item) {
		if( $item == '.' || $item == '..' || $item == 'plugins' || $item == 'themes' ) {
			continue;
		}

		$newPath = $path . $item;
		$relativePath = str_replace(ABSPATH, '', $newPath);
		if( is_dir($newPath) ) {
			$hash = array_merge($hash, collect_wp_hash_sum($newPath . '/'));
		} else {
			$hash[$relativePath] = md5_file($newPath);
		}
	}

	return $hash;
}

/**
 * Displays a notification inside the Antispam interface, on all pages of the plugin.
 * This is necessary to remind the user to update the configuration of the plugin components,
 * Otherwise, the newly activated components will not be involved in the work of the plugin.
 *
 * @param Wbcr_Factory475_Plugin                   $plugin
 * @param Wbcr_FactoryPages475_ImpressiveThemplate $obj
 *
 * @return bool
 */
add_action('wbcr/factory/pages/impressive/print_all_notices', function ($plugin, $obj) {
	if( $plugin->getPluginName() != Plugin::app()->getPluginName() ) {
		return;
	}

	if( !empty($_GET['page']) && "sitechecker-" . Plugin::app()->getPluginName() === $_GET['page'] ) {
		require_once WTITAN_PLUGIN_DIR . '/includes/audit/classes/class.cert.php';
		$cert = Cert::get_instance();
		$output = false;
		$message = '';
		$type = 'warning';
		//$plugin_name = WBCR\Titan\Plugin::app()->getPluginTitle();

		if( $cert->is_available() ) {
			if( !$cert->is_lets_encrypt() ) {
				$remaining = $cert->get_expiration_timestamp() - time();
				if( $remaining <= 86400 * 90 ) { // 3 month (90 days)
					$message = 'The SSL certificate expires in less than three months';
					$output = true;
				} else if( $remaining <= 86400 * 3 ) { // 3 days
					$type = 'notice-error';
					$message = 'The SSL certificate expires in less than three days';
					$output = true;
				}
			}
		} else {
			$output = true;
			$type = 'error';
			$message = $cert->get_error_message();
		}

		if( $output ) {
			switch( $type ) {
				case 'error':
					$obj->printErrorNotice($message);
					break;
				case 'warning':
					$obj->printWarningNotice($message);
					break;
			}
		}
	}
}, 10, 2);

add_action('init', 'titan_init_https_redirect');
function titan_init_https_redirect()
{
	$strict_https = Plugin::app()->getPopulateOption('strict_https', false);
	if( !is_ssl() && $strict_https ) {
		wp_redirect(home_url(add_query_arg($_GET, $_SERVER['REQUEST_URI']), 'https'));
		die;
	}
}

add_action(Plugin::app()->getPluginName() . "/factory/premium/license_activate", 'titan_set_scanner_speed_active');
function titan_set_scanner_speed_active()
{
	$scanner_speed = Plugin::app()->getPopulateOption('scanner_speed', 'free');
	if( $scanner_speed == 'free' ) {
		Plugin::app()->updatePopulateOption('scanner_speed', 'slow');
	}

	$scanner_schedule = Plugin::app()->getPopulateOption('scanner_schedule', 'disabled');
	if( $scanner_schedule == 'disabled' ) {
		Plugin::app()->updatePopulateOption('scanner_schedule', 'disabled');
	}

	$scanner_type = Plugin::app()->getPopulateOption('scanner_type', 'basic');
	if( $scanner_type == 'basic' ) {
		Plugin::app()->updatePopulateOption('scanner_type', 'advanced');
	}
}

add_action(Plugin::app()->getPluginName() . "/factory/premium/license_deactivate", 'titan_set_scanner_speed_deactive');
function titan_set_scanner_speed_deactive()
{
	$scanner_speed = Plugin::app()->getPopulateOption('scanner_speed', 'free');
	if( $scanner_speed !== 'free' ) {
		Plugin::app()->updatePopulateOption('scanner_speed', 'free');
	}

	$scanner_schedule = Plugin::app()->getPopulateOption('scanner_schedule', 'disabled');
	if( $scanner_schedule !== 'disabled' ) {
		Plugin::app()->updatePopulateOption('scanner_schedule', 'disabled');
	}

	$scanner_type = Plugin::app()->getPopulateOption('scanner_type', 'basic');
	if( $scanner_type !== 'basic' ) {
		Plugin::app()->updatePopulateOption('scanner_type', 'basic');
	}
}

/**
 * @return int|float [Memory limit in MB]
 */
function titan_get_memory_limit()
{
	$mem = ini_get('memory_limit');
	$last = $mem[strlen($mem) - 1];
	$mem = (int)$mem;
	do {
		switch( $last ) {
			case 'g':
			case 'G':
				$mem = $mem * 1024;
				$last = 'm';
				break;

			case 'm':
			case 'M':
				break 2;

			default:
				$mem = ((int)$mem) / 1024 / 1024; // bytes to mbytes
				break 2;
		}
	} while( true );

	return $mem;
}

function get_recommended_scanner_speed()
{
	$mem = titan_get_memory_limit();
	if( $mem > 100 ) {
		$recommendation = Scanner::SPEED_FAST;
	} else if( $mem > 60 ) {
		$recommendation = Scanner::SPEED_MEDIUM;
	} else {
		$recommendation = Scanner::SPEED_SLOW;
	}

	return $recommendation;
}

/**
 * @param $time
 *
 * @return int
 */
function correct_timezone($time)
{
	$localOffset = (new DateTime)->getOffset();

	return $time + $localOffset;
}

add_action('plugins_loaded', 'titan_init_check_schedule');
function titan_init_check_schedule()
{
	$format_date = 'Y/m/d H:i';
	$format_time = 'H:i';

	$is_schedule = false;

	$lasttime = Plugin::app()->getPopulateOption('scanner_schedule_last_time', date_i18n($format_date));
	$schedule = Plugin::app()->getPopulateOption('scanner_schedule', 'disabled');
	$last = date_parse_from_format($format_time, $lasttime);

	switch( $schedule ) {
		case 'daily':
			$daily = Plugin::app()->getPopulateOption('scanner_schedule_daily', '2000/01/01 23:00');
			$daily = date_parse_from_format($format_time, $daily);
			$daily = $daily['hour'] * 60 + $daily['minute'];
			$last = $last['hour'] * 60 + $last['minute'];
			if( $last <= $daily ) {
				titan_create_scheduler_scanner();
				$is_schedule = true;
			}
			break;
		case 'weekly':
			$week = Plugin::app()->getPopulateOption('scanner_schedule_weekly_day', '7');
			$time = Plugin::app()->getPopulateOption('scanner_schedule_weekly_time', '2000/01/01 23:00');
			$time = date_parse_from_format($format_time, $time);
			$time = $time['hour'] * 60 + $time['minute'];
			$last = $last['hour'] * 60 + $last['minute'];
			$this_week = date('N');
			if( $this_week == $week && $last <= $time ) {
				titan_create_scheduler_scanner();
				$is_schedule = true;
			}
			break;
		case 'custom':
			$time = Plugin::app()->getPopulateOption('scanner_schedule_custom', '2000/01/01 23:00');
			$time = strtotime($time);
			$last = strtotime($lasttime);
			if( $last <= $time ) {
				titan_create_scheduler_scanner();
				$is_schedule = true;
			}
			break;
		case 'disabled':
			break;
	}
	if( $is_schedule ) {
		Plugin::app()->updatePopulateOption('scanner_schedule_last_time', date_i18n($format_date));
	}
}

/**
 * Return premium widget markup
 *
 * @return string
 */
function wtitan_get_sidebar_adverts_widget()
{
	ob_start();
	?>
	<div id="wtitan-adverts-widget" class="wbcr-factory-sidebar-widget">
		<a href="https://cm-wp.com/wp-support/" target="_blank" rel="noopener noreferrer">
			<img src="<?php echo WTITAN_PLUGIN_URL; ?>/assets/img/vertical_maintance.jpg" width="100%" alt="">
		</a>
	</div>
	<?php

	$output = ob_get_contents();

	ob_end_clean();

	return $output;
}

