<?php

class CV_Monitor_Site {

	private $plugin;
	private $plugin_slug;
	protected $service_path = 'https://apps.coursevector.com/monitor/service.php';

	public function __construct( $plugin ) {
		$this->plugin = $plugin;
		$this->plugin_slug = $plugin->plugin_slug;
	}

	//--------------------------------------
	//  Public
	//--------------------------------------

	/**
	*	Returns all wordpress data (versions, database, comments)
	*/
	public function get_data( ) {
		global $wpdb;

		$data = new stdClass();

		// Allowed tags
		$allowed_tags = array(
							'a' => array( 'href' => array(), 'title' => array() ),
							'abbr' => array( 'title' => array() ),
							'acronym' => array( 'title' => array() ),
							'code' => array(),
							'em' => array(),
							'strong' => array()
							);

		// Site Data
		//--------------------------------------

		// Check if log_errors is enabled in PHP
		$log_errors = ini_get( 'log_errors' );

		$php_mem_limit = get_cfg_var( 'memory_limit' );
		if ( false === $php_mem_limit ) $php_mem_limit = 'unknown';

		//$cpanel = explode( '/', $_SERVER['DOCUMENT_ROOT'] );
		//$cpanel = array_key_exists(2, $cpanel) ? $cpanel[2] : '';

		$data->site = array(
			'url' => get_site_url(),
			'ip' => $this->get_public_ip(),
			'size' => $this->plugin->get_dir_size( ABSPATH ),
			'document_root' => $_SERVER['DOCUMENT_ROOT'],
			'php_version' => phpversion(),
			'php_mem_limit' => $php_mem_limit,
			'php_max_upload' => ini_get( 'upload_max_filesize' ),
			'php_max_post' => ini_get( 'post_max_size' ),
			'log_errors_disabled' => ( ! $log_errors ? true : false),
			'has_dual_auth' => $this->is_dual_auth(),
			'has_protect_htaccess' => false,
			'has_deny_xmlrpc' => false,
			'has_robots' => $this->is_robots_found(),
			'has_wptwin' => $this->is_wptwin_found(),
		);

		// Database Data
		//--------------------------------------

		$data->database = array(
			'type' => $this->get_db_type(),
			'version' => $this->get_db_version(),
			'overhead' => $this->get_db_overhead(),
			'size' => $this->get_db_size()
		);

		// WordPress Data
		//--------------------------------------

		$update_wordpress = get_site_transient( 'update_core' );
		$post_revisions = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_type = 'revision';" );
		$post_count = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_type = 'post';" );
		$page_count = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_type = 'page';" );

		$data->wordpress = array(
			'abs_path' => ABSPATH,
			'version' => $update_wordpress->version_checked,
			'latest_version' => $update_wordpress->updates[0]->current,
			'admin_email' => get_bloginfo( 'admin_email' ),
			'post_revisions' => count( $post_revisions ),
			'post_count' => count( $post_count ),
			'page_count' => count( $page_count ),
			'spam_count' => $this->get_comments( 'spam' ),
			'pending_count' => $this->get_comments( '0' )
		);

		// Theme Data
		//--------------------------------------

		// Get Themes
		$themes = wp_get_themes();
		$current_theme = function_exists( 'wp_get_theme' ) ? wp_get_theme()->get( 'Name' ) : get_current_theme();

		// Get updates to themes //
		if ( false === ( $update_themes = get_site_transient( 'update_themes' ) ) ) {
			$update_response = array();
		} else {
			$update_response = property_exists( $update_themes, 'response' ) ? $update_themes->response : array();
		}

		foreach ( $themes as $theme_file => $theme_data ) {
			// Sanitize all displayed data
			$o = array();
			$o['Name'] = mb_convert_encoding( wp_kses( $theme_data['Name'], $allowed_tags ), "UTF-8");
			$o['ThemeURI'] = array_key_exists('ThemeURI', $data) ? wp_kses( $theme_data['ThemeURI'], $allowed_tags ) : '';
			$o['AuthorURI'] = array_key_exists('AuthorURI', $data) ? wp_kses( $theme_data['AuthorURI'], $allowed_tags ) : '';
			$o['Version'] = wp_kses( $theme_data['Version'], $allowed_tags );
			$o['Author'] = mb_convert_encoding( wp_kses( $theme_data['Author'], $allowed_tags ), "UTF-8");
			$o['Status'] = wp_kses( $theme_data['Status'], $allowed_tags );
			$o['Template'] = wp_kses( $theme_data['Template'], $allowed_tags );

			$latest_version = array_key_exists( $theme_file, $update_response ) ? $update_response[$theme_file]['new_version'] : $o['Version'];
			if ( array_key_exists( $o['Template'], $themes ) && $o['Template'] != $theme_file ) {
				$o['Template'] = $themes[$o['Template']]['Name'];
			} else {
				$o['Template'] = '';
			}

			$t = new stdClass();
			$t->name = $o['Name'];
			$t->slug = $theme_file;
			//$t->slug = substr($theme_file, 0, strpos($theme_file, '/'));
			$t->version = $o['Version'];
			$t->latest_version = $latest_version;
			$t->author = $o['Author'];
			$t->author_url = $o['AuthorURI'];
			$t->theme_url = $o['ThemeURI'];
			$t->theme_parent = $o['Template'];
			$t->active = ($current_theme == $o['Name']);
			$data->themes[] = $t;
		}

		// Plugin Data
		//--------------------------------------

		// Check if get_plugins() function exists. This is required on the front end of the
		// site, since it is in a file that is normally only loaded in the admin.
		if ( ! function_exists( 'get_plugins' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		// Get current installed plugins //
		$plugins = get_plugins();

		// Get updates to plugins //
		if ( false === ( $update_plugins = get_site_transient( 'update_plugins' ) ) ) {
			$update_response = array();
		} else {
			$update_response = property_exists( $update_plugins, 'response' ) ? $update_plugins->response : array();
		}

		foreach ( $plugins as $plugin_file => $plugin_data ) {
			// Sanitize all displayed data
			$o = array();
            $o['Title'] = mb_convert_encoding( wp_kses( $plugin_data['Title'], $allowed_tags ), "UTF-8");
			$o['PluginURI'] = array_key_exists('PluginURI', $data) ? wp_kses( $plugin_data['PluginURI'], $allowed_tags ) : '';
			$o['AuthorURI'] = array_key_exists('AuthorURI', $data) ? wp_kses( $plugin_data['AuthorURI'], $allowed_tags ) : '';
			$o['Version'] = wp_kses( $plugin_data['Version'], $allowed_tags );
			$o['Author'] = mb_convert_encoding( wp_kses( $plugin_data['Author'], $allowed_tags ), "UTF-8");

			$latest_version = array_key_exists( $plugin_file, $update_response ) ? $update_response[$plugin_file]->new_version : $o['Version'];

			$p = new stdClass();
			$p->title = $o['Title'];
			$p->slug = substr($plugin_file, 0, strpos($plugin_file, '/'));
			$p->version = $o['Version'];
			$p->latest_version = $latest_version;
			$p->author = $o['Author'];
			$p->author_url = $o['AuthorURI'];
			$p->plugin_url = $o['PluginURI'];
			$p->active = is_plugin_active( $plugin_file );
			$data->plugins[] = $p;
		}

		// Error Log Data
		//--------------------------------------

		// Get all logs
		$logs = $this->get_error_logs( '..' );

		// Check if there is a error_log path specified
		$error_log = @ini_get( 'error_log' );
		if ( 'error_log' !== $error_log && false !== $error_log ) {
			$logs[] = array( $error_log, $this->plugin->get_file_size( $error_log ), filemtime( $error_log ) );
		}

		// Get error log statistics
		$data->errors_logs = array();
		foreach ( $logs as $log ) {
			if ( is_readable( $log[0] ) ) {
				$data->errors_logs[] = array( 'file'=>$log[0], 'size'=>$log[1], 'date'=>$log[2] );
			}
		}

		// Plugin Settings Data
		//--------------------------------------

		$data->settings = array();
		$data->settings['simple-history'] = $this->get_simple_history();
		$data->settings['updraft-plus'] = $this->get_updraft_plus();
		$data->settings['word-fence'] = $this->get_word_fence();
		//$data->settings['revision-control'] = $this->get_revision_control();

		return $data;
	}

	public function get_database_size() {
		global $wpdb;
		$size = 0;
		$rows = $wpdb->get_results( 'SHOW TABLE STATUS', ARRAY_A );

		if ( $wpdb->num_rows > 0 ) {
			foreach ( $rows as $row ) {
				$size += $row['Data_length'] + $row['Index_length'];
			}
		}

		return (int) $size;
	}

	public function get_scheduled_tasks() {
		$cron = _get_cron_array();
		$schedules = wp_get_schedules();
		$all_tasks = array();

		foreach( (array) $cron as $timestamp => $cronhooks ) {
			foreach( (array) $cronhooks as $hook => $events ) {
				foreach( (array) $events as $event ) {
					// If the frequency exist
					if ( $event['schedule'] ) {
						if ( !empty( $schedules[$event['schedule']] ) ) {
							$frequency = $schedules[$event['schedule']]['display'];
						} else {
							$frequency = 'Unknown';
						}
					} else {
						$frequency = "One-off event";
					}

					// If the task has not been added yet, add it and initiate its info
					if ( empty($all_tasks[$hook]) ) {
						$all_tasks[$hook] = array('belongs_to' => '', 'sites' => array());
					}

					// Add info of the task according to the current site
					$all_tasks[$hook] = array( 'frequency' => $frequency, 'args' => implode(',', $event['args']), 'next_run' => $timestamp );
				}
			}
		}

		return $all_tasks;
	}

	public function send_data() {
		try {
			$update_plugins = get_site_transient( 'update_plugins' );

			// If too old of wordpress, just send data
			if ( isset( $update_plugins->response ) || version_compare( get_bloginfo('version'), '2.8', '<' ) ) {
				$data = $this->get_data();
                // [01-Sep-2017 13:48:42 UTC] PHP Warning: htmlentities() expects parameter 1 to be string, object given in /home/poweryoga/public_html/wp-content/plugins/cv-monitor/includes/class-site.php on line 254
                    // [23-Nov-2016 20:38:34 UTC] PHP Warning: json_encode(): Invalid UTF-8 sequence in argument in /home/minutementoring/public_html/wp-content/plugins/cv-monitor/includes/class-site.php on line 253
                    //$data = htmlentities( $data, ENT_QUOTES, 'utf-8', FALSE );
                    //$data = mb_convert_encoding( $data, 'UTF-8', 'UTF-8' );
                    //$json = json_encode( $data );
				$json = json_encode( $data, JSON_UNESCAPED_UNICODE );
				$salt = '9[5@-Z+y9<,;%Q|YAwu%b`$.T.z:;_`E/8X]x!LZmr0`3:lKP|_+%}g10#-|3/(B';

				$key = sha1( $data->site['url'] . $salt );
				$body = $json;
				$headers = array(
								'Content-Type' => 'application/json',
								'CV-Access-Key' => $key
								);
				$request = new WP_Http();
				$result = $request->request( $this->service_path, array( 'method' => 'POST', 'headers' => $headers, 'body' => $body ) );

				if ( is_wp_error( $result ) ) {
					return $result->get_error_message();
				} else if ( 200 != $result['response']['code'] ) {
					return $result['response']['message'];
				} else {
					update_option( 'cv_monitor_last_checked', time() );
					return true;
				}
				return true;
			}
			return 'Plugin data not available yet';
		} catch (Exception $e ) {
			return $e->getMessage();
		}
	}

	////////////////////////////////////////////////////

	public function check_db( $table = null ) {
		global $wpdb;
		$tables = $wpdb->get_col( "SHOW TABLES", 0 );
		if ($table && in_array($table, $tables)) {
			$query = "CHECK TABLE $table";
		} else {
			$query = "CHECK TABLE " . implode(', ', $tables);
		}
		return $wpdb->get_results( $query );
	}

	public function delete_comments( $type ) {
		global $wpdb;
		$section = 1;
		$total = 0;
		while ( $section ) {
			$sql = "DELETE FROM $wpdb->comments WHERE comment_approved = '$type' LIMIT 200";
			$section = $wpdb->query( $sql );
			$total += $section;
			if ( $section ) { usleep( 100000 ); }
		}
		return $total;
	}

	public function delete_error_log( $path ) {
		$path = realpath( $path );

		// Don't let anything past the root directory
		if ( 0 === strpos( $path, '../../' ) ) {
			return __( 'Invalid file path to error log.', $this->plugin_slug );
		}

		// Don't delete anything not named error_log
		if ( '/error_log' !== substr( $path, -strlen( '/error_log' ) ) && '/debug.log' !== substr( $path, -strlen( '/debug.log' )) ) {
			return __( 'Invalid file path, file is not an error_log file.', $this->plugin_slug );
		}

		$error_log = $path;
		if ( @is_file( $error_log ) ) {
			if ( @unlink( $error_log ) ) {
				//echo 'PHP Error Log deleted';

				// Force plugin to re-index error logs
				delete_transient( 'cv_error_logs' );
				return true;
			} else {
				return __( "There has been an error trying to delete the PHP Error log $error_log.", $this->plugin_slug );
			}
		} else {
			return __( "The log cannot be found in the specified route $error_log.", $this->plugin_slug );
		}
	}

	public function get_absolute_path($path) {
		$path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
		$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
		$absolutes = array();
		foreach ($parts as $part) {
			if ('.' == $part) continue;
			if ('..' == $part) {
				array_pop($absolutes);
			} else {
				$absolutes[] = $part;
			}
		}
		return implode(DIRECTORY_SEPARATOR, $absolutes);
	}

	// Returns the total number of approved comments
	public function get_comments( $type ) {
		global $wpdb;
		$sql = "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_approved = '$type'";
		return $wpdb->get_var( $sql );
	}

	public function get_account_disk_usage() {
		/*
		Format 1 (LW)
		Disk quotas for user coursev (uid 504):
			 Filesystem  blocks   quota   limit   grace   files   quota   limit   grace
			  /dev/vda3 1983296  10240000 10240000           29569       0       0

		Format 1 (HostGator)
		Disk quotas for user o3vets (uid 4047):
		     Filesystem  blocks   quota   limit   grace   files   quota   limit   grace
		      /dev/vda3       4  10240000 10240000               3       0       0
		      /dev/vda7 1653164  10240000 10240000           20286       0       0
		      /dev/vda1       0  10240000 10240000               1       0       0

		Format 2
		  Your home directory on the NFS server resides on the /N/fs5 file system.

		  Your disk quota is set to 10000 blocks.

		  You currently have 378 files and are using 4991 blocks.
		 */
		if ( $this->is_func_enabled('shell_exec') ) {
			$account_disk_usage = wp_cache_get( 'account_disk_usage' );
			if ( false === $account_disk_usage ) {
				$raw_disk_usage = shell_exec("quota -u " . get_current_user());
				/*echo '<pre>';
				var_dump($raw_disk_usage);
				echo '</pre>';*/
				if ( !is_null($raw_disk_usage) ) {
					$arr_lines = explode("\n", $raw_disk_usage);
					// Format 1
					if ( strrpos($arr_lines[0], 'Disk', -strlen($arr_lines[0])) !== false ) {
						array_shift($arr_lines);

						$data_cols = array();
						$blocks = 0;
						$limit = 0;
						foreach ($arr_lines as $line) {
							$data_cols = preg_split('/\s+/', $line);
							if ( $data_cols[2] > $blocks ) {
								$blocks = $data_cols[2];
								$limit = $data_cols[4];
							}
						}

						// Usage / Limit
						$account_disk_usage = array($blocks * 1024, $limit * 1024);

					// Format 2
					// UNTESTED possibly on Windows servers?
					} else {
						$account_disk_usage = array();
						// Usage
						$arr_file_lines = explode("files", $arr_lines[1]);
						$account_disk_usage[] = preg_replace("/[^0-9]/", "", $arr_file_lines[1]) * 1024;
						// Limit
						$account_disk_usage[] = preg_replace("/[^0-9]/", "", $arr_lines[1]) * 1024;
					}
					wp_cache_set( 'account_disk_usage', $account_disk_usage );
					return $account_disk_usage;
				} else {
					return null;
				}
			} else {
				return $account_disk_usage;
			}
		} else {
			return null;
		}
	}

	public function get_core_count() {
		if( $this->is_func_enabled('shell_exec') ) {
			$cpu_core_count = wp_cache_get( 'cpu_core_count' );
			if( false === $cpu_core_count ) {
				//$cpu_core_count = shell_exec("echo \"$((`cat /proc/cpuinfo | grep cores | grep -o '[0-9]' | uniq` * `cat /proc/cpuinfo |grep 'physical id' | sort | uniq | wc -l`))\"");
				$cpu_core_count = shell_exec("nproc");
				wp_cache_set( 'cpu_core_count', $cpu_core_count );
				return $cpu_core_count;
			} else {
				return $cpu_core_count;
			}
		} else {
			return 0;
		}
	}

	public function get_cpu_count() {
		if( $this->is_func_enabled('shell_exec') ) {
			$cpu_count = wp_cache_get( 'cpu_count' );
			if( false === $cpu_count ) {
				//$cpu_count = shell_exec('cat /proc/cpuinfo |grep "physical id" | sort | uniq | wc -l');
				$cpu_count = shell_exec('nproc --all');
				/*echo '<pre>';
				echo shell_exec('cat /proc/cpuinfo');
				echo '</pre>';*/
				/*echo '<pre>';
				echo shell_exec('nproc');
				echo '</pre>';
				echo '<pre>';
				echo shell_exec('nproc --all');
				echo '</pre>';
				echo '<pre>';
				echo shell_exec('getconf _NPROCESSORS_ONLN');
				echo '</pre>';
				echo '<pre>';
				echo shell_exec('lscpu');
				echo '</pre>';*/

				wp_cache_set( 'cpu_count', $cpu_count );
				return $cpu_count;
			} else {
				return $cpu_count;
			}
		} else {
			return 'unknown';
		}
	}

	// http://php.net/manual/en/function.realpath.php
	/*private function normalize_path($path) {
		$parts = array();// Array to build a new path from the good parts
		$path = str_replace('\\', '/', $path);// Replace backslashes with forwardslashes
		$path = preg_replace('/\/+/', '/', $path);// Combine multiple slashes into a single slash
		$segments = explode('/', $path);// Collect path segments
		$test = '';// Initialize testing variable
		foreach ($segments as $segment) {
			if ($segment != '.') {
				$test = array_pop($parts);
				if (is_null($test)) {
					$parts[] = $segment;
				} else if ($segment == '..') {
					if ($test == '..') $parts[] = $test;
					if ($test == '..' || $test == '') $parts[] = $segment;
				} else {
					$parts[] = $test;
					$parts[] = $segment;
				}
			}
		}
		return implode('/', $parts);
	}*/

	/**
	 * Reads and displays an error log file
	 *
	 * @param  string $path   The path to the error_log file
	 * @param  boolean $return If the data should be returned as a string or echoed
	 * @return string         The error log data if $return is true
	 */
	public function get_error_log( $path ) {
		$html_newline = ''; //'&#13;&#10;';
		$arrLines = array();
		$orig_path = $path;
		$path = realpath( $path );

		// If realpath fails, use alternative
		/*if ( false === $path ) {
			$path = $this->normalize_path( $orig_path );
		}*/

		// Ensure that only log files can be read and not things like wp-config
		if ( '/error_log' !== substr( $path, -strlen( '/error_log' ) ) && '/debug.log' !== substr( $path, -strlen( '/debug.log' ) )) {
			return __( 'Error: Invalid path ' . $orig_path, $this->plugin_slug );
		}

		if ( ! empty( $path ) ) {
			if ( @is_file( $path ) ) {
				$size = $this->plugin->get_file_size( $path );

				// If file is larger than 1MB
				if ( $size > 1048576 ) {
					$handle = fopen( $path, 'r' );
					if ( false !== $handle ) {
						// Seek to within 1MB of the end of the file
						fseek( $handle, $size - 1048576 );

						// Read each line til you get to the end
						while ( false !== ( $line = fgets( $handle, 4096 ) ) ) {
							$arrLines[] = $line;
						}

						$arrLines[] = __( '***Note: File is too large to load completely, last megabyte loaded.***' . PHP_EOL, $this->plugin_slug );
						if ( ! feof( $handle ) ) {
							$arrLines[] = __( 'Error: unexpected fgets() fail' . PHP_EOL, $this->plugin_slug );
						}
						fclose( $handle );

						// Reverse so last is on top
						//$arrLines = array_reverse( $arrLines );
					} else {
						return __( 'Error: unexpected fopen() fail', $this->plugin_slug );
					}
				} else {
					//$arrLines = array_reverse( file( $path ) );
					$arrLines = file( $path );
				}
				// In some instances returns a blank string
				//return esc_html( implode( $html_newline, $arrLines ) );
				// This fixed it by allowing it to ignore errors
				return htmlspecialchars( implode( $html_newline, $arrLines ), ENT_QUOTES | ENT_SUBSTITUTE );
			} else {
				return __( 'No errors found... yet.', $this->plugin_slug );
			}
		}
		return __( 'No errors found... yet.', $this->plugin_slug );
	}

	/**
	 * Scans directory for error_log files
	 *
	 * @param  string $directory The directory path to search within
	 * @return array            Array of file paths
	 */
	public function get_error_logs( $directory, $recurse = false ) {
		// Try to load cached results so it's not scanning everything
		if ( $recurse or (false === ( $result = get_transient( 'cv_error_logs' ) ) ) ) {
			$result = array();

			// Warning: scandir(../.metadata): failed to open dir: Permission denied in /homepages/4/d376364254/htdocs/app618685465/wp-content/plugins/cv-monitor/includes/class-site.php on line 569
			// Warning: scandir(): (errno 13): Permission denied in /homepages/4/d376364254/htdocs/app618685465/wp-content/plugins/cv-monitor/includes/class-site.php on line 569
			// Warning: Invalid argument supplied for foreach() in /homepages/4/d376364254/htdocs/app618685465/wp-content/plugins/cv-monitor/includes/class-site.php on line 571
			if ( is_readable($directory) ) {
				$entries = scandir( $directory );
				foreach ( $entries as $entry ) {
					$path = $directory . DIRECTORY_SEPARATOR . $entry;

					if ( '.' === $entry || '..' === $entry || is_link( $path ) ) continue;

					if ( is_file( $path ) ) {
						if ( 0 === strpos( $entry, 'error_log' ) ) $result[] = array( $path, $this->plugin->get_file_size( $path ), filemtime( $path ) );
						// /opt/bitnami/apps/wordpress/htdocs/wp-content/debug.log
						if ( 0 === strpos( $entry, 'debug.log' ) ) $result[] = array( $path, $this->plugin->get_file_size( $path ), filemtime( $path ) );
						continue;
					}

					// Need to check if directory, symbolic link was getting past and throwing errors
					if ( is_dir( $path ) ) {
						foreach ( $this->get_error_logs( $path, true ) as $entry ) {
							$result[] = $entry;
						}
					}
				}
			}

			if ( !$recurse ) set_transient( 'cv_error_logs', $result, 30 );
		}
		return $result;
	}

	public function get_db_type() {
		global $wpdb;
		return $wpdb->get_var( "SHOW VARIABLES LIKE 'version_comment'", 1 );
	}

	public function get_db_version() {
		global $wpdb;
		return $wpdb->get_var( "SELECT VERSION() AS version" );
	}



	public function get_server_load_linux_data() {
		if (is_readable("/proc/stat")) {
			$stats = @file_get_contents("/proc/stat");

			if ($stats !== false) {
				// Remove double spaces to make it easier to extract values with explode()
				$stats = preg_replace("/[[:blank:]]+/", " ", $stats);

				// Separate lines
				$stats = str_replace(array("\r\n", "\n\r", "\r"), "\n", $stats);
				$stats = explode("\n", $stats);

				// Separate values and find line for main CPU load
				foreach ($stats as $statLine) {
					$statLineData = explode(" ", trim($statLine));

					// Found!
					if ( (count($statLineData) >= 5) && ($statLineData[0] == "cpu") ) {
						return array(
							$statLineData[1],
							$statLineData[2],
							$statLineData[3],
							$statLineData[4],
						);
					}
				}
			}
		}

		return null;
	}

	// Returns server load in percent (just number, without percent sign)
	public function get_server_load() {
		$load = null;

		if (stristr(PHP_OS, "win")) {
			$cmd = "wmic cpu get loadpercentage /all";
			@exec($cmd, $output);

			if ($output) {
				foreach ($output as $line) {
					if ($line && preg_match("/^[0-9]+\$/", $line)) {
						$load = $line;
						break;
					}
				}
			}
		} else {
			if ( is_readable("/proc/stat") ) {
				// Collect 2 samples - each with 1 second period
				// See: https://de.wikipedia.org/wiki/Load#Der_Load_Average_auf_Unix-Systemen
				$statData1 = $this->get_server_load_linux_data();

				// If we can't get the stats, return 0
				if (is_null($statData1)) return 0;

				sleep(1);
				$statData2 = $this->get_server_load_linux_data();

				if ( (!is_null($statData1)) && (!is_null($statData2)) ) {
					// Get difference
					$statData2[0] -= $statData1[0];
					$statData2[1] -= $statData1[1];
					$statData2[2] -= $statData1[2];
					$statData2[3] -= $statData1[3];

					// Sum up the 4 values for User, Nice, System and Idle and calculate
					// the percentage of idle time (which is part of the 4 values!)
					$cpuTime = $statData2[0] + $statData2[1] + $statData2[2] + $statData2[3];

					// Invert percentage to get CPU time, not idle time
					$load = 100 - ($statData2[3] * 100 / $cpuTime);
				}
			}
		}

		return $load;
	}

	public function get_server_location() {
		//$ip = $_SERVER['SERVER_ADDR']; // Private IP
		$ip = $this->get_public_ip(); // Public IP
		if ($ip === '?') $ip = $_SERVER['SERVER_ADDR']; // Private IP

		$server_location = wp_cache_get( 'server_location' );
		if( false === $server_location ) {
			$query = @unserialize( $this->file_get_contents_curl( 'http://ip-api.com/php/' . $ip ) );
			if ( $query && $query['status'] == 'success' ) {
				$server_location = $query['city'] . ', ' . $query['region'] . ' - ' . $query['country'];
				wp_cache_set( 'server_location', $server_location );
				return $server_location;
			} else {
				return '?';
			}
		} else {
			return $server_location;
		}
	}

	public function get_server_memory() {
		if ( is_readable("/proc/meminfo") ) {
			$data = explode("\n", file_get_contents("/proc/meminfo"));
			$meminfo = array();
			foreach ($data as $line) {
				// Occurs on AWS Server
				// [07-Oct-2019 14:49:40 UTC] PHP Notice:  Undefined offset: 1 in /home/blossburg/public_html/wp-content/plugins/cv-monitor/includes/class-site.php on line 732
				if ("" === $line) continue;

				list($key, $val) = explode(":", $line);
				$meminfo[$key] = intval(trim($val)) * 1024; // kB -> b
			}

			// Occurs on AWS Server
			// [07-Oct-2019 14:49:40 UTC] PHP Notice:  A non well formed numeric value encountered in /home/blossburg/public_html/wp-content/plugins/cv-monitor/includes/class-site.php on line 735
			// fixed with intval above
			return array(($meminfo["MemTotal"] - $meminfo["MemFree"]), $meminfo["MemTotal"]);
		} else {
			return null;
		}
	}

	public function get_suhosin( $prop, $default ) {
		$is_installed = false;
		if ( extension_loaded('suhosin') ) {
			$is_installed = true;
		}/* else {
			if ( dl('suhosin.so') ) {
				$is_installed = true;
			}
		}*/

		if ( defined("SUHOSIN_PATCH") ) {
			$is_installed = true;
		}

		if ( $is_installed ) {
			$result = ini_get( $prop );
			if ( false === $result ) return $default;
			return $result;
		}
		return 'Not Installed';
	}

	// MyISAM to InnoDB engine converter
	public function convert_db( $table = null ) {
		global $wpdb;
		$results = array();
		$tables = $wpdb->get_col( "SHOW TABLES", 0 );
		foreach ($tables as $table) {
			foreach ( $wpdb->get_results( $wpdb->prepare("SHOW TABLE STATUS WHERE Name = %s;", $table) ) as $table_data ) {
				if ('MyISAM' === $table_data->Engine) {
					// convert
					$result = $wpdb->query("ALTER TABLE `{$table}` ENGINE = InnoDB;");
					$result_obj = new stdClass();
					$result_obj->Table = $table;
					$result_obj->Msg_text = (1 === $result ? 'Table was using the ' . $table_data->Engine . ' Engine' : $wpdb->last_error);
					$result_obj->Op = 'alter';
					$result_obj->Msg_type = 'status';
					array_push($results, $result_obj);
				} else {
					// ignore
					$result_obj = new stdClass();
					$result_obj->Table = $table;
					$result_obj->Msg_text = 'Table is already using the ' . $table_data->Engine . ' Engine';
					$result_obj->Op = 'alter';
					$result_obj->Msg_type = 'status';
					array_push($results, $result_obj);
				}
			}
		}

		return $results;
	}

	public function optimize_db( $table = null ) {
		global $wpdb;
		$tables = $wpdb->get_col( "SHOW TABLES", 0 );
		if ($table && in_array($table, $tables)) {
			$query = "OPTIMIZE TABLE $table";
		} else {
			$query = "OPTIMIZE TABLE " . implode(', ', $tables);
		}
		return $wpdb->get_results( $query );
	}

	public function repair_db( $table = null ) {
		global $wpdb;
		$tables = $wpdb->get_col( "SHOW TABLES", 0 );
		if ($table && in_array($table, $tables)) {
			$query = "REPAIR TABLE $table";
		} else {
			$query = "REPAIR TABLE " . implode(', ', $tables);
		}
		return $wpdb->get_results( $query );
	}

	//--------------------------------------
	//  Private
	//--------------------------------------

	// Returns the total overhead for the MySQL database, with a flag to clear the overhead
	private function get_db_overhead( $clear = false ) {
		global $wpdb;

		$total_gain = 0;
		$table_string = '';

		// Avoid doing optmize on InnoDB as it can cause more issues than help
		foreach ( $wpdb->get_col( "SHOW TABLES", 0 ) as $table ) {
			foreach ( $wpdb->get_results( "SHOW TABLE STATUS LIKE '" . $table . "';" ) as $table_data ) {
				if ( isset( $table_data->Engine ) && in_array( $table_data->Engine, array(
					'MyISAM',
					'ISAM',
					'HEAP',
					'MEMORY',
					'ARCHIVE'
				) ) ) {
					if ( $wpdb->base_prefix != $wpdb->prefix ) {
						if ( preg_match( '/^' . $wpdb->prefix . '*/Ui', $table_data->Name ) ) {
							if ( $table_data->Data_free > 0 ) {
								$total_gain += $table_data->Data_free;// / 1024;
								$table_string .= $table_data->Name . ",";
							}
						}
					} else if ( preg_match( '/^' . $wpdb->prefix . '[0-9]{1,20}_*/Ui', $table_data->Name ) ) {
						continue;
					} else {
						if ( $table_data->Data_free > 0 ) {
							$total_gain += $table_data->Data_free;// / 1024;
							$table_string .= $table_data->Name . ",";
						}
					}
				} elseif ( isset( $table_data->Engine ) && $table_data->Engine == 'InnoDB' ) {
					//$total_gain +=  $table_data->Data_free > 100*1024*1024 ? $table_data->Data_free / 1024 : 0;
				}
			}
		}

		if ( $clear ) {
			if ( '' === $table_string ) { return true; } // No tables to optimize
			$table_string = substr( $table_string, 0, strlen( $table_string ) - 1 ); //remove last ,
			$table_string = rtrim( $table_string );
			$query = "OPTIMIZE TABLE $table_string";
			$optimize = $wpdb->query( $query );
			return $optimize === FALSE ? false : true;
		} else {
			//return $this->parse_file_size( $total_gain );
			return $total_gain;
		}
	}

	// Returns the total data size of the MySQL database
	private function get_db_size() {
		global $wpdb;

		$total_gain = 0;

		// Avoid doing optimize on InnoDB as it can cause more issues than help
		foreach ( $wpdb->get_col( "SHOW TABLES", 0 ) as $table ) {
			foreach ( $wpdb->get_results( "SHOW TABLE STATUS LIKE '" . $table . "';" ) as $table_data ) {
				if ( isset( $table_data->Engine ) && in_array( $table_data->Engine, array(
					'MyISAM',
					'ISAM',
					'HEAP',
					'MEMORY',
					'ARCHIVE'
				) ) ) {
					if ( $wpdb->base_prefix != $wpdb->prefix ) {
						if ( preg_match('/^' . $wpdb->prefix . '*/Ui', $table_data->Name ) ) {
							if ( $table_data->Data_length > 0 || $table_data->Index_length > 0 ) {
								$total_gain += $table_data->Data_length + $table_data->Index_length;
							}
						}
					} else if ( preg_match( '/^' . $wpdb->prefix . '[0-9]{1,20}_*/Ui', $table_data->Name ) ) {
						continue;
					} else {
						if ( $table_data->Data_length > 0 || $table_data->Index_length > 0 ) {
							$total_gain += $table_data->Data_length + $table_data->Index_length;
						}
					}
				} elseif ( isset( $table_data->Engine ) && $table_data->Engine == 'InnoDB' ) {
					//$total_gain +=  $table_data->Data_free > 100*1024*1024 ? $table_data->Data_free / 1024 : 0;
				}
			}
		}
		//return $this->parse_file_size( $total_gain );
		return $total_gain;
	}

	private function get_revision_control() {
		return array( 'options' => get_option( 'revision-control' ) );
	}

	public function get_public_ip() {
		$public_ip = wp_cache_get( 'public_ip' );
		if ( false === $public_ip) {
			//$data = file_get_contents_curl('http://checkip.dyndns.com/');
			//preg_match('/Current IP Address: \[?([:.0-9a-fA-F]+)\]?/', $data, $m);
			//return = $m[1];
			$public_ip = $this->file_get_contents_curl('https://ipecho.net/plain');

			/*if ( $this->is_func_enabled('shell_exec') ) {
				$public_ip = preg_replace("/[^0-9.]/", "", shell_exec('dig +short myip.opendns.com @resolver1.opendns.com'));
			} elseif ( php_uname('n') ) {
				$public_ip = gethostbyname( php_uname('n') );
			} else {
				$root_url = function_exists('get_site_url') ? get_site_url() : get_option('siteurl');
				$parse = parse_url($root_url);
				$public_ip = gethostbyname( $parse['host'] );
			}*/
			wp_cache_set( 'public_ip', $public_ip );
			return $public_ip;
		}
		return $public_ip;
	}

	private function file_get_contents_curl($url) {
		$ch = curl_init();

		curl_setopt($ch, CURLOPT_HEADER, 0);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_URL, $url);

		$data = curl_exec($ch);
		curl_close($ch);

		return $data;
	}

	private function get_simple_history() {
		$rss_secret = get_option( "simple_history_rss_secret" );
		if ( false === $rss_secret ) {
			$rss_address = '';
		} else {
			$rss_address = add_query_arg( array( "simple_history_get_rss" => "1", "rss_secret" => $rss_secret ), get_bloginfo("url") . "/" );
			$rss_address = htmlspecialchars( $rss_address, ENT_COMPAT, "UTF-8" );
		}

		$rss_enabled = get_option( "simple_history_enable_rss_feed" );

		return array(
			'rss_enabled' => $rss_enabled,
			'rss_address' => $rss_address
		);
	}

	private function get_updraft_plus() {
		return array(
			'updraft_starttime_files' => get_option( 'updraft_starttime_files' ),
			'updraft_startday_files' => get_option( 'updraft_startday_files' ),
			'updraft_interval' => get_option( 'updraft_interval' ),
			'updraft_retain' => get_option( 'updraft_retain' ),
			'updraft_starttime_db' => get_option( 'updraft_starttime_db' ),
			'updraft_startday_db' => get_option( 'updraft_startday_db' ),
			'updraft_interval_database' => get_option( 'updraft_interval_database' ),
			'updraft_retain_db' => get_option( 'updraft_retain_db' )
		);
	}

	private function get_word_fence() {
		global $wpdb;
		$prefix = $wpdb->base_prefix;

		// Check if wfBlockedIPLog table exists
		if ( $wpdb->get_var("SHOW TABLES LIKE '{$prefix}wfBlockedIPLog'") != "{$prefix}wfBlockedIPLog" ) {
			$blocked_ips = array();
		} else {
			/* Get all blocked IPs in the past week and summarize them */
			// WordFence rotates weekly
			$blocked_ips = $wpdb->get_results( "SELECT IP, countryCode, blockCount, unixday, SUM(blockCount) as totalBlockCount FROM {$prefix}wfBlockedIPLog GROUP BY IP ORDER BY IP ASC" );
			//$blocked_ips = $wpdb->get_results( "SELECT IP, countryCode, blockCount, unixday FROM {$prefix}wfBlockedIPLog" );
			foreach ( $blocked_ips as $key=>$value ) {
				$ip = floatval( hexdec( bin2hex( $value->IP ) ) );
				$day = intval($value->unixday);
				$blocked_ips[$key]->IP = ($ip >> 24 & 0xFF) . '.' . ($ip >> 16 & 0xFF) . '.' . ($ip >> 8 & 0xFF) . '.'  . ($ip & 0xFF);
				$blocked_ips[$key]->unixday = $day * 86400; // unixtimestamp
			}
		}

		// Check if wfConfig table exists
		if ( $wpdb->get_var("SHOW TABLES LIKE '{$prefix}wfConfig'") != "{$prefix}wfConfig" ) {
			$scheduled_scans = 0;
			$disable_code_uploads = 0;
		} else {
			$scheduled_scans = @$wpdb->get_var( "SELECT val FROM {$prefix}wfConfig WHERE name='scheduledScansEnabled'" );
			$disable_code_uploads = @$wpdb->get_var( "SELECT val FROM {$prefix}wfConfig WHERE name='disableCodeExecutionUploads'" );
		}

		return array(
			'blocked_ips' => $blocked_ips,
			'scheduled_scans' => $scheduled_scans,
			'disable_code_uploads' => $disable_code_uploads
		);
	}

	// Returns whether the site is protected by dual auth or not
	private function is_dual_auth() {
		// /home/coursev/public_html
		// /home/coursev/.htaccess
		$result = false;
		$search = '<FilesMatch "wp-login.php">';
		//$search = 'AuthType Basic';
		$arr_site_root = explode( "/public_html", $_SERVER['DOCUMENT_ROOT'] );
		$site_root = $arr_site_root[0];
		$sub_folder = $arr_site_root[1]; // May be empty
		$htaccess_path = $site_root . '/.htaccess';

		if ( file_exists ( $htaccess_path ) ) {
			$file = fopen( $htaccess_path, 'r' );
			while ( ! feof( $file  ) && false !== ( $line = fgets( $file ) ) ) {
				if ( false !== strpos( $line, $search )  ) {
					$result = true;
					break;
				}
			}
			fclose( $file );
		}

		return $result;
	}

	private function is_func_enabled($func) {
		return is_callable($func) && false === stripos(ini_get('disable_functions'), $func);
	}

	private function is_robots_found() {
		// /home/coursev/public_html/robots.txt
		return file_exists ( $_SERVER['DOCUMENT_ROOT'] . '/robots.txt' );
	}

	private function is_wptwin_found() {
		// /home/coursev/public_html/wptwin.php
		return file_exists ( $_SERVER['DOCUMENT_ROOT'] . '/wptwin.php' );
	}
}