#!/usr/bin/php
<?php
//
// DB Maintenance Subsystem
// Copyright (c) 2017-2019 Nagios Enterprises, LLC. All rights reserved.
//
define('SUBSYSTEM', 1);
require_once(dirname(__FILE__) . '/../html/includes/base.inc.php');

subsys_get_options();
dbmaint_subsys();

// If the trial has expired, we don't delete anyhing anymore. So their old data still is visible
if (is_trial_expired()) {
	log_debug(LOG_TYPE_SYSTEM, "dbmaint_subsys: Trial is expired. No longer removing stale data.");
	exit(0);
}

function dbmaint_subsys() {

	global $db;

	$fusiondb = 'fusion';

	dbmaint_get_retention_policies($log_retention, $polled_retention);
    $debug_in_db = get_option('debug_in_db');

    // when was the dbmaint last ran?
    $last_dbmaint_run_in_seconds = time() - intval(get_option('last_dbmaint_run', 0));
    $last_dbmaint_run_in_minutes = intval($last_dbmaint_run_in_seconds / 60);
    $last_dbmaint_run_in_hours = intval($last_dbmaint_run_in_minutes / 60);
    $last_dbmaint_run_in_days = intval($last_dbmaint_run_in_hours / 24);
    $last_dbmaint_run_in_weeks = intval($last_dbmaint_run_in_days / 7);

    $server_list = get_server_list();
    $user_list = get_user_list();

	try {

		// process this regardless of when it was last ran
		{			
			// remove any invalid polling_locks
			dbmaint_query_and_log('DELETE 
				FROM polling_lock 
				WHERE server_id NOT IN (SELECT server_id FROM servers)', 'polling_lock');
		}

		// process this once every 15 minutes or so
		if ($last_dbmaint_run_in_minutes > 15) {

			// Nagios v2 license systems only (update database with latest data from v2 API)
			get_v2_license_stats();

			// clean up stale commands
			$command_stale_time = time() - (24 * 60 * 60); // 1 day
			dbmaint_query_and_log("DELETE
				FROM commands
				WHERE UNIX_TIMESTAMP(submission_time) < {$command_stale_time}
				OR status_code = " . COMMAND_STATUS_COMPLETED, 'commands');

			// clean up auth tokens
			$auth_token_stale_time = time() - (24 * 60 * 60); // 1 day
			dbmaint_query_and_log("DELETE
				FROM auth_tokens
				WHERE UNIX_TIMESTAMP(auth_valid_until) < {$auth_token_stale_time}", 'auth_tokens');

			// clean up sessions
			$session_stale_time = time() - (30 * 60); // 30 minutes
			dbmaint_query_and_log("DELETE
				FROM users_sessions
				WHERE UNIX_TIMESTAMP(session_last_active) < {$session_stale_time}", 'users_sessions');

		}

		// process this once an hour or so
		if ($last_dbmaint_run_in_hours > 1) {

			// clean up polled data
			dbmaint_query_and_log("DELETE 
				FROM polled_data 
				WHERE server_id NOT IN ({$server_list})", 'polled_data');

			dbmaint_query_and_log('DELETE
				FROM polled_data 
				WHERE UNIX_TIMESTAMP(polled_time) < ' . (time() - $polled_retention), 'polled_data');

			dbmaint_query_and_log("DELETE
				FROM polled_averages
				WHERE server_id NOT IN 
					({$server_list})", 'polled_averages');

			dbmaint_query_and_log("DELETE 
				FROM polled_averages 
				WHERE poll_key NOT IN 
					(SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '{$fusiondb}' AND TABLE_NAME = 'polled_data') 
				AND poll_key NOT IN 
					(SELECT poll_key FROM polled_extras)", 'polled_averages');

			dbmaint_query_and_log("DELETE
				FROM polled_deltas
				WHERE server_id NOT IN 
					({$server_list})", 'polled_deltas');

			dbmaint_query_and_log("DELETE 
				FROM polled_deltas 
				WHERE poll_key NOT IN 
					(SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '{$fusiondb}' AND TABLE_NAME = 'polled_data') 
				AND poll_key NOT IN 
					(SELECT poll_key FROM polled_extras)", 'polled_deltas');

			dbmaint_query_and_log('DELETE 
				FROM polled_extras 
				WHERE polled_data_id NOT IN 
					(SELECT polled_data_id FROM polled_data)', 'polled_extras');
		}


		// process this once a day or so
		if ($last_dbmaint_run_in_days > 1) {

			// clean up users_servers mappings
			dbmaint_query_and_log("DELETE 
				FROM users_servers 
				WHERE user_id NOT IN ({$user_list}) 
				OR server_id NOT IN ({$server_list})", 'users_servers');

			// clean up dashboards, dashlets, dashlet_params
			dbmaint_query_and_log("DELETE
				FROM dashboards
				WHERE user_id NOT IN ({$user_list})", 'dashboards');
			dbmaint_query_and_log('DELETE
				FROM dashlets
				WHERE dashboard_id NOT IN (SELECT dashboard_id FROM dashboards)', 'dashlets');
			dbmaint_query_and_log('DELETE
				FROM dashlets_params
				WHERE dashlet_id NOT IN (SELECT dashlet_id FROM dashlets)', 'dashlets_params');

			// clean up views
			dbmaint_query_and_log("DELETE
				FROM views
				WHERE user_id NOT IN ({$user_list})", 'views');

			// clean log
			$logs = dbmaint_query_and_log('DELETE
				FROM log
				WHERE UNIX_TIMESTAMP(log_time) < ' . (time() - $log_retention), 'log');

			// remove debugging from log if keep debugging in database is unchecked
			if (!$debug_in_db) {
				$debug_logs_left = dbmaint_query_and_log('DELETE
					FROM log
					WHERE level >= ' . LOG_LEVEL_DEBUG, 'log');
			}
		}


		// process this once a week
		if ($last_dbmaint_run_in_weeks > 1) {

			// clean up user meta
			$obj_type = META_TYPE_USER;
			dbmaint_query_and_log("DELETE 
				FROM meta 
				WHERE obj_type = {$obj_type} 
				AND obj_id NOT IN ({$user_list})", 'meta');

			// dont need sysstat for non-existent servers
			dbmaint_query_and_log("DELETE 
				FROM sysstat 
				WHERE server_id != 0 
				AND server_id NOT IN ({$server_list})", 'sysstat');
		}

		// TODO:
		// check to make sure that mapped users make sense
		// if the type is for polling, then make sure that user exists in the last polled info

	} catch (Exception $e) {

		log_error(LOG_TYPE_SYSTEM, 'dbmaint_subsys() caught exception: ' . $e->getMessage());
		log_error(LOG_TYPE_SYSTEM, 'dbmaint_subsys() db error: ' . one_line_print_r($db->get_errors()));
	}

	$cbargs = array();
	do_callbacks(CALLBACK_DBMAINT, $cbargs);

	set_sysstat_data('dbmaint_subsys', SUBSYS_OK);
}

// pass by ref the retention policies in seconds
function dbmaint_get_retention_policies(&$log, &$polled) {

	// IT WAS NEVER MEANT TO BE KEPT FOREVER

	$log = get_option('log_retention', DEFAULT_LOG_RETENTION);
	if (is_numeric($log) && $log > 0) {
		$log *= 86400;
	}
	else {
		$log = 86400 * DEFAULT_LOG_RETENTION;
	}

	$polled = get_option('polled_retention', DEFAULT_POLLED_RETENTION);
	if (is_numeric($polled) && $polled > 0) {
		$polled *= 86400;
	}
	else {
		$polled = 86400 * DEFAULT_POLLED_RETENTION;
	}

	log_debug(LOG_TYPE_SYSTEM, "Using log_retention: [$log], polled_retention: [$polled]");
}

// wrapper function to query and log meaningful output
// now batches in deletes of $delete_chunk, and returns recursive count
function dbmaint_query_and_log($sql, $table_name, $bind_array = null) {

	global $db;
	$delete_chunk = 2500;
	$result = $db->exec_query("$sql LIMIT {$delete_chunk}", $bind_array);

	if (is_numeric($result)) {
		if ($result == $delete_chunk) {
			$result += dbmaint_query_and_log($sql, $table_name, $bind_array);
		}
	}
	else {
		log_error(LOG_TYPE_SYSTEM, "dbmaint_query_and_log() unable to gather query data: {$sql}");
		return false;
	}

	log_debug(LOG_TYPE_SYSTEM, "dbmaint_query_and_log() deleted {$result} rows from {$table_name}");
	return $result;
}

function get_server_list() {

	global $db;

	$total_servers = $db->exec_query('SELECT count(server_id) as total_servers FROM servers');
	if (isset($total_servers[0]['total_servers'])) {
		$total_servers = intval($total_servers[0]['total_servers']);
	} else {
		$total_servers = 0;
	}

	$server_list = 'SELECT server_id FROM servers';
	if ($total_servers < 200) {

		$server_rows = $db->exec_query('SELECT server_id FROM servers');
		if (is_array($server_rows) && count($server_rows) > 0) {

			$server_list = '';

			foreach ($server_rows as $i => $row) {
				$server_list .= $row['server_id'] . ',';
			}

			$server_list = rtrim($server_list, ',');
		}
	}

	return $server_list;
}

function get_user_list() {

	global $db;

	$total_users = $db->exec_query('SELECT count(user_id) as total_users FROM users');
	if (isset($total_users[0]['total_users'])) {
		$total_users = intval($total_users[0]['total_users']);
	} else {
		$total_users = 0;
	}

	$user_list = 'SELECT user_id FROM users';
	if ($total_users < 200) {

		$user_rows = $db->exec_query('SELECT user_id FROM users');
		if (is_array($user_rows) && count($user_rows) > 0) {

			$user_list = '';

			foreach ($user_rows as $i => $row) {
				$user_list .= $row['user_id'] . ',';
			}

			$user_list = rtrim($user_list, ',');
		}
	}

	return $user_list;
}
