<?php

class ITSEC_Malware {

	private
		$settings,
		$module_path;

	function run() {

		global $itsec_malware;

		$itsec_malware = $this;

		$this->settings    = get_site_option( 'itsec_malware' );
		$this->module_path = ITSEC_Lib::get_module_path( __FILE__ );

		add_filter( 'itsec_logger_modules', array( $this, 'itsec_logger_modules' ) );
		add_filter( 'itsec_sync_modules', array( $this, 'itsec_sync_modules' ) ); //register sync modules

	}

	/**
	 * Register malware for logger
	 *
	 * @since 4.4
	 *
	 * @param  array $logger_modules array of logger modules
	 *
	 * @return array                   array of logger modules
	 */
	public function itsec_logger_modules( $logger_modules ) {

		$logger_modules['malware'] = array(
			'type'     => 'malware',
			'function' => __( 'Malware Scan', 'it-l10n-better-wp-security' ),
		);

		return $logger_modules;

	}

	/**
	 * Register malware for Sync
	 *
	 * @param  array $sync_modules array of malware modules
	 *
	 * @return array                   array of logger modules
	 */
	public function itsec_sync_modules( $sync_modules ) {

		$sync_modules['malware'] = array(
			'verbs'      => array(
				'itsec-perform-homepage-scan'    => 'Ithemes_Sync_Verb_ITSEC_Perform_Homepage_Scan',
				'itsec-get-malware-scan-results' => 'Ithemes_Sync_Verb_ITSEC_Get_Malware_Scan_Results',
			),
			'everything' => 'itsec-get-malware-scan-results',
			'path'       => dirname( __FILE__ ),
		);

		return $sync_modules;

	}

	/**
	 * request one-time scan
	 *
	 * @since 4.3
	 *
	 * @return array|mixed result of scan
	 */
	public function one_time_scan() {

		$homepage = home_url( '', 'http' );

		$request = $this->request_url_scan( $homepage );

		if ( $request !== false && $request !== 204) {

			$response = array();

			if ( isset( $request->resource ) ) {
				$response['resource'] = $request->resource;
			}

			if ( isset( $request->response_code ) ) {
				$response['response_code'] = $request->response_code;
			}

			return $response;
		}

		return $request;

	}

	/**
	 * Retrieve's report for a one-time scan
	 *
	 * @since 4.3
	 *
	 * @param mixed $resource file or URL scanned, null to retrieve homepage
	 *
	 * @return array|mixed result of scan
	 */
	public function scan_report( $resource = null ) {

		if ( $resource === null ) {
			$resource = home_url( '', 'http' );
		}

		$request = $this->request_report( $resource );

		if ( $request !== false ) {

			$response      = array();
			$scan_complete = false;

			if ( isset( $request->positives ) ) {

				$response['positives'] = $request->positives;
				$scan_complete         = true;

			}

			if ( $scan_complete === false ) {

				if ( isset( $request->resource ) ) {
					$response['resource'] = $request->resource;
				}

				if ( isset( $request->response_code ) ) {
					$response['response_code'] = $request->response_code;
				}

			}

			return $response;

		}

		return $request;

	}

	/**
	 * Request a malware report for a given url
	 *
	 * @since 4.3
	 *
	 * @param string $current_resource the url to retrieve the report of or null
	 * @param string $type             type of resource, url or id
	 *
	 * @return mixed the response from the server or false if the submission was not successful
	 */
	private function request_report( $current_resource = null, $type = 'url' ) {

		global $itsec_logger;

		$url = 'https://www.virustotal.com/vtapi/v2/url/report';

		if ( $type === 'url' ) {
			//Use the supplied URL if present
			if ( ITSEC_Lib::validate_url( $current_resource ) === true ) {

				$resource = esc_url( $current_resource );

			} else {

				$resource = esc_url( $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );

			}

		} elseif ( $type === 'id' ) {

			$resource = sanitize_text_field( $current_resource );

		} else {

			return false;

		}

		$args['body'] = array(
			'resource' => $resource,
			'apikey'   => $this->settings['api_key'],
		);

		$response = wp_remote_post( $url, $args );

		if ( is_array( $response ) && isset( $response['response']['code'] ) && $response['response']['code'] == 200 && isset( $response['body'] ) ) {

			$user = wp_get_current_user();
			$body = json_decode( sanitize_text_field( $response['body'] ) );

			if ( $body->response_code === 1 ) {

				$scans = get_object_vars( $body->scans );

				foreach ( $scans as $scan => $data ) {

					$scans[$scan] = get_object_vars( $data );

				}

				$scan_info = array(
					'type'     => 'Malware Scan Report',
					'resource' => $resource,
					'report'   => array(
						'positives' => $body->positives,
						'total'     => $body->total,
						'scans'     => $scans,
					),
				);

			} else {

				$scan_info = array(
					'type'     => 'Malware Scan Report',
					'resource' => $resource,
					'report'   => array(
						'Resource still queued for scanning',
					),
				);

			}

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

			return $body; //Valid request

		} elseif ( is_array( $response ) && isset( $response['response']['code'] ) && $response['response']['code'] == 204 ) {

			return 204; //Rate limit exceeded

		} elseif ( is_array( $response ) && isset( $response['response']['code'] ) && $response['response']['code'] == 403 ) {

			return 403; //Permission denied

		} else {

			return false; //unspecified failure

		}

	}

	/**
	 * Request a malware scan
	 *
	 * @since 4.3
	 *
	 * @param mixed $current_file the file path to scan or null
	 *
	 * @return mixed the response from the server or false if the submission was not successful
	 */
	private function request_file_scan( $current_file = null ) {

		global $itsec_logger;

		$url = 'https://www.virustotal.com/vtapi/v2/file/scan';

		if ( $current_file === null ) {

			return false;

		} else {

			$current_file = trailingslashit( ABSPATH ) . $current_file;

		}

		//Use the supplied URL if present
		if ( is_file( $current_file ) === true ) {

			$file_contents = file_get_contents( $current_file );

		} else {

			$current_file  = trailingslashit( ABSPATH ) . 'index.php';
			$file_contents = file_get_contents( $current_file );

			//return false;

		}

		$boundary = wp_generate_password( 24 ); // Just a random string, use something better than wp_generate_password() though.
		$headers  = array(
			'content-type' => 'multipart/form-data; boundary=' . $boundary
		);

		$payload = '';
		$payload .= '--' . $boundary;
		$payload .= PHP_EOL;
		$payload .= 'Content-Disposition: form-data; name="apikey"' . "\r\n\r\n";
		$payload .= $this->settings['api_key'];
		$payload .= PHP_EOL;

		$payload .= '--' . $boundary;
		$payload .= PHP_EOL;
		$payload .= 'Content-Disposition: form-data; name="file"; filename="' . basename( $current_file ) . '"' . PHP_EOL;
		$payload .= 'Content-Type: ' . filetype( $current_file ) . PHP_EOL; // If you know the mime-type
		$payload .= PHP_EOL;
		$payload .= file_get_contents( $current_file );
		$payload .= PHP_EOL;

		$payload .= '--' . $boundary . '--';

		$args['body'] = $payload;

		$response = wp_remote_post( $url, array(
			'headers' => $headers,
			'body'    => $payload,
		) );

		$scan_info = array(
			'type' => 'Malware Scan Requested',
			'file' => $current_file
		);

		$user = wp_get_current_user();

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

		if ( is_array( $response ) && isset( $response['response']['code'] ) && $response['response']['code'] == 200 && isset( $response['body'] ) ) {

			return json_decode( sanitize_text_field( $response['body'] ) ); //Valid request

		} elseif ( is_array( $response ) && isset( $response['response']['code'] ) && $response['response']['code'] == 204 ) {

			return 204; //Rate limit exceeded

		} elseif ( is_array( $response ) && isset( $response['response']['code'] ) && $response['response']['code'] == 403 ) {

			return 403; //Permission denied

		} else {

			return false; //unspecified failure

		}

	}

	/**
	 * Request a malware scan
	 *
	 * @since 4.3
	 *
	 * @param mixed $current_url the url to scan or null
	 *
	 * @return mixed the response from the server or false if the submission was not successful
	 */
	private function request_url_scan( $current_url = null ) {

		global $itsec_logger;

		$url = 'https://www.virustotal.com/vtapi/v2/url/scan';

		//Use the supplied URL if present
		if ( ITSEC_Lib::validate_url( $current_url ) === true ) {

			$current_url = esc_url( $current_url );

		} else {

			$current_url = esc_url( $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );

		}

		$args['body'] = array(
			'url'    => $current_url, //The URL to scan
			'apikey' => $this->settings['api_key'], //API Key
		);

		$response = wp_remote_post( $url, $args );

		if ( is_array( $response ) && isset( $response['response']['code'] ) && $response['response']['code'] == 200 && isset( $response['body'] ) ) {

			$scan_info = array(
				'type'     => 'Malware Scan Requested',
				'resource' => $current_url,
			);

			$user = wp_get_current_user();

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

			return json_decode( sanitize_text_field( $response['body'] ) ); //Valid request

		} elseif ( is_array( $response ) && isset( $response['response']['code'] ) && $response['response']['code'] == 204 ) {

			return 204; //Rate limit exceeded

		} elseif ( is_array( $response ) && isset( $response['response']['code'] ) && $response['response']['code'] == 403 ) {

			return 403; //Permission denied

		} else {

			return false; //unspecified failure

		}

	}

	/**
	 * request scheduled scan on given resource
	 *
	 * @since 4.3
	 *
	 * @param int    $type     type of scan 0 for url, 1 for file
	 * @param string $resource the resouce to scan
	 *
	 * @return array|mixed result of scan or false
	 */
	public function scheduled_scan( $type, $resource ) {

		if ( $type === 0 ) {

			$request = $this->request_url_scan( $resource );

		} elseif ( $type === 1 ) {

			$request = $this->request_file_scan( $resource );

		} else {
			return false;
		}

		if ( $request !== false ) {

			$response = array();

			if ( isset( $request->resource ) ) {
				$response['resource'] = $request->resource;
			}

			if ( isset( $request->response_code ) ) {
				$response['response_code'] = $request->response_code;
			}

			return $response;
		}

		return $request;

	}

}