<?php

final class ITSEC_Malware_Scanner {
	protected static $transient_name = 'itsec_cached_sucuri_scan';

	public static function scan() {

		/** @var ITSEC_Logger $itsec_logger */
		global $itsec_logger;


		$results = self::get_scan_results();

		if ( is_array( $results ) && isset( $results['cached'] ) && $results['cached'] ) {
			return $results;
		}


		$user = wp_get_current_user();
		$itsec_logger->log_event( 'malware', 3, $results, ITSEC_Lib::get_ip(), $user->user_login, $user->ID );

		return $results;
	}

	/**
	 * This attempts to determine if this is a temporary Sucuri error or something the user needs to take action on.
	 *
	 * @param WP_Error|array $results
	 *
	 * @return bool
	 */
	public static function is_sucuri_error( $results ) {
		if ( ! is_wp_error( $results ) ) {
			return false;
		}

		$code = $results->get_error_code();

		// Networking error probably due to a server issue.
		if ( strpos( $code, 'itsec' ) === false ) {
			return false;
		}

		$plugin_conflict_codes = array(
			'itsec-malware-scanner-wp-remote-get-response-malformed',
			'itsec-malware-scanner-wp-remote-get-response-missing-body',
			'itsec-malware-scanner-wp-remote-get-response-empty-body',
		);

		// Probably a plugin conflict.
		if ( in_array( $code, $plugin_conflict_codes, true ) ) {
			return false;
		}

		return true;
	}

	protected static function get_scan_results() {

		$response = get_site_transient( self::$transient_name );
		$cached = true;

		if ( defined( 'ITSEC_TEST_MALWARE_SCAN_SKIP_CACHE' ) && ITSEC_TEST_MALWARE_SCAN_SKIP_CACHE ) {
			$cached = false;
			$response = false;
		}


		if ( false === $response ) {
			$cached = false;

			$scanner_url = 'https://sitecheck.sucuri.net/';
			$site_url = apply_filters( 'itsec_test_malware_scan_site_url', get_site_url() );

			if ( defined( 'ITSEC_TEST_MALWARE_SCAN_SITE_URL' ) ) {
				$site_url = ITSEC_TEST_MALWARE_SCAN_SITE_URL;
			}

			$site_url = preg_replace( '|^https?://|i', '', $site_url );

			$query_args = array(
				'scan'  => $site_url,
				'p'     => 'ithemes',
				'clear' => 1,
				'json'  => 1,
				'time'  => time(),
			);

			$key = apply_filters( 'itsec_sucuri_key', '' );

			if ( defined( 'ITSEC_SUCURI_KEY' ) ) {
				$key = ITSEC_SUCURI_KEY;
			}

			if ( ! empty( $key ) ) {
				$query_args['k'] = $key;
			}

			$scan_url = "$scanner_url?" . http_build_query( $query_args, '', '&' );

			$req_args = array(
				'connect_timeout' => 30, // Placeholder for when WordPress implements support.
				'timeout'         => 300,
			);

			if ( defined( 'ITSEC_TEST_MALWARE_SCAN_DISABLE_SSLVERIFY' ) && ITSEC_TEST_MALWARE_SCAN_DISABLE_SSLVERIFY ) {
				$req_args['sslverify'] = false;

				// Ensure that another plugin isn't preventing the disabling of sslverify from working.
				add_filter( 'https_local_ssl_verify', '__return_false', 999999 );
				add_filter( 'https_ssl_verify', '__return_false', 999999 );
			}

			$response = wp_remote_get( $scan_url, $req_args );

			if ( defined( 'ITSEC_TEST_MALWARE_SCAN_DISABLE_SSLVERIFY' ) && ITSEC_TEST_MALWARE_SCAN_DISABLE_SSLVERIFY ) {
				remove_filter( 'https_local_ssl_verify', '__return_false', 999999 );
				remove_filter( 'https_ssl_verify', '__return_false', 999999 );
			}

			if ( is_wp_error( $response ) ) {
				return $response;
			}
		}


		if ( isset( $response['body'] ) ) {
			$body = $response['body'];
		} else {
			return new WP_Error( 'itsec-malware-scanner-wp-remote-get-response-missing-body', __( 'The scan failed due to an unexpected technical error. The response from the wp_remote_get function does not contain a body entry. Since the body entry contains the response for the request to Sucuri\'s servers, the response cannot be processed. This could indicate a plugin/theme compatibility issue or a problem in WordPress.', 'it-l10n-ithemes-security-pro' ), $response );
		}

		if ( empty( $body ) ) {
			return new WP_Error( 'itsec-malware-scanner-wp-remote-get-response-empty-body', __( 'The scan failed due to an unexpected technical error. The response from the wp_remote_get function contains an empty body entry. Since the body entry contains the response for the request to Sucuri\'s servers, the response cannot be processed. This could indicate a plugin/theme compatibility issue or a problem in WordPress.', 'it-l10n-ithemes-security-pro' ), $response );
		}

		$body = @json_decode( $body, true );

		if ( is_null( $body ) && isset( $response['headers'], $response['headers']['content-type'] ) ) {
			if ( 'application/json' === $response['headers']['content-type'] ) {
				return new WP_Error( 'itsec-malware-scanner-invalid-json-data-in-scan-response', __( 'The scan did not complete successfully. The Sucuri server should send its response in JSON encoding. The response indicates that the encoding is JSON, but the data could not be decoded. This problem could be due to a temporary Sucuri server issue or a compatibility issue on your server. If the problem continues, please contact iThemes Security support.', 'it-l10n-ithemes-security-pro' ), $response );
			} else {
				return new WP_Error( 'itsec-malware-scanner-invalid-content-type-in-scan-response', sprintf( __( 'The scan did not complete successfully. The Sucuri server should send its response in JSON encoding. The data received from the Sucuri server could not be decoded. In addition, a content type of <code>%s</code> was received when a content type of <code>application/json</code> was expected. This could indicate a temporary issue with the Sucuri servers.', 'it-l10n-ithemes-security-pro' ), esc_html( $response['headers']['content-type'] ) ), $response );
			}
		} else if ( ! is_array( $body ) ) {
			if ( 'ERROR' === substr( $response['body'], 0, 5 ) ) {
				return new WP_Error( 'itsec-malware-scanner-error-received', sprintf( __( 'The scan did not complete successfully. Sucuri sent the following error: %s', 'it-l10n-ithemes-security-pro' ), '<code>' . $response['body'] . '</code>' ), $response );
			}

			if ( ! empty( $response['response'] ) && ! empty( $response['response']['code'] ) ) {
				return new WP_Error( 'itsec-malware-scanner-unknown-scan-error', sprintf( __( 'An unknown error prevented the scan from completing successfully. The Sucuri server responded with a <code>%s</code> error code.', 'it-l10n-ithemes-security-pro' ), $response['response']['code'] ), $response );
			}

			return new WP_Error( 'itsec-malware-scanner-wp-remote-get-response-malformed', __( 'The scan failed due to an unexpected technical error. The response from the wp_remote_get function is missing some critical information that is needed in order to properly process the response from Sucuri\'s servers. This could indicate a plugin/theme compatibility issue or a problem in WordPress.', 'it-l10n-ithemes-security-pro' ), $response );
		}


		if ( ! $cached ) {
			set_site_transient( self::$transient_name, $response, 10 * MINUTE_IN_SECONDS );
		}

		if ( is_array( $body ) ) {
			$body['cached'] = $cached;
		}

		return $body;
	}
}
