<?php

namespace CleantalkSP\SpbctWP;

use CleantalkSP\Variables\Server;
use CleantalkSP\Security\Firewall\Result;
use CleantalkSP\SpbctWP\Firewall\FirewallState;

/**
 * CleanTalk SpamFireWall base class.
 * Compatible with any CMS.
 *
 * @depends       \CleantalkSP\SpbctWP\Helper class
 * @depends       \CleantalkSP\SpbctWP\API class
 * @depends       \CleantalkSP\SpbctWP\DB class
 *
 * @version       4.0
 * @author        Cleantalk team (welcome@cleantalk.org)
 * @copyright (C) 2014 CleanTalk team (http://cleantalk.org)
 * @license       GNU/GPL: http://www.gnu.org/copyleft/gpl.html
 * @see           https://github.com/CleanTalk/php-antispam
 */
class Firewall extends \CleantalkSP\Security\Firewall
{
    /**
     * Creates Database driver instance.
     *
     * @param mixed $db database handler
     */
    public function __construct($db = null)
    {
        $this->db = DB::getInstance();
        parent::__construct($db);
    }

    /**
     * Use this method to handle logs updating by the module.
     *
     * @param Result $fw_result
     *
     * @return void
     */
    public function updateLog(Result $fw_result)
    {
        if (FirewallState::$is_admin) {
            return;
        }

        // Increasing counter
        Counters\FirewallCounter::increment(stripos($fw_result->status, 'pass') !== false ? 'pass' : 'deny');

        //single quote escaping
        foreach ( $fw_result->pattern as &$pattern ) {
            $pattern = str_replace(array("'", '"'), array("ESC_S_QUOTE", "ESC_D_QUOTE"), $pattern);
        }
        unset($pattern);

        // Truncate before escaping — addslashes-then-substr left a dangling \ at the cut (SQLi).
        $page_url = substr(
            (Server::getString('HTTPS') !== 'off' ? 'https://' : 'http://')
            . Server::getString('HTTP_HOST')
            . Server::getString('REQUEST_URI'),
            0,
            4096
        );
        $http_user_agent = Server::getString('HTTP_USER_AGENT')
            ? htmlspecialchars(substr(Server::getString('HTTP_USER_AGENT'), 0, 300))
            : 'unknown';
        $request_method  = substr(Server::getString('REQUEST_METHOD'), 0, 5);
        $x_forwarded_for = htmlspecialchars(substr(Server::getString('HTTP_X_FORWARDED_FOR'), 0, 15));
        $pattern         = ! empty($fw_result->pattern) ? json_encode($fw_result->pattern) : '';
        $triggered_for   = ! empty($fw_result->triggered_for) ? substr($fw_result->triggered_for, 0, 100) : '';
        $signature_id    = (int) $fw_result->signature_id;
        $network         = ($fw_result->network === '' || $fw_result->network === null)
            ? ''
            : (string) $fw_result->network;
        $mask            = ($fw_result->mask === '' || $fw_result->mask === null)
            ? ''
            : (string) $fw_result->mask;
        $country_code    = (string) $fw_result->country_code;
        $is_personal     = (int) $fw_result->is_personal;
        $status          = (string) $fw_result->status;
        $ip              = (string) $fw_result->ip;
        $time            = time();

        $signature_chunk = ! empty($fw_result->signature_id) ? (string) $fw_result->signature_id : '';
        $id              = md5(
            $fw_result->ip . $http_user_agent . $fw_result->status . $fw_result->waf_action . $signature_chunk
        );

        $query = "INSERT INTO " . SPBC_TBL_FIREWALL_LOG . " SET
				entry_id        = %s,
				ip_entry        = %s,
				entry_timestamp = %d,
				status          = %s,
				pattern         = NULLIF(%s, ''),
				signature_id    = NULLIF(%d, 0),
				triggered_for   = NULLIF(%s, ''),
				requests        = 1,
				page_url        = %s,
				http_user_agent = %s,
				request_method  = %s,
				x_forwarded_for = NULLIF(%s, ''),
				network         = NULLIF(%s, ''),
				mask            = NULLIF(%s, ''),
				country_code    = NULLIF(%s, ''),
				is_personal     = %d
			ON DUPLICATE KEY UPDATE
				ip_entry        = ip_entry,
				entry_timestamp = %d,
				status          = %s,
				pattern         = NULLIF(%s, ''),
				signature_id    = NULLIF(%d, 0),
				triggered_for   = NULLIF(%s, ''),
				requests        = requests + 1,
				page_url        = %s,
				http_user_agent = http_user_agent,
				request_method  = %s,
				x_forwarded_for = NULLIF(%s, ''),
				network         = NULLIF(%s, ''),
				mask            = NULLIF(%s, ''),
				country_code    = NULLIF(%s, ''),
				is_personal     = %d";

        $this->db->prepare(
            $query,
            array(
                $id,
                $ip,
                $time,
                $status,
                $pattern,
                $signature_id,
                $triggered_for,
                $page_url,
                $http_user_agent,
                $request_method,
                $x_forwarded_for,
                $network,
                $mask,
                $country_code,
                $is_personal,
                $time,
                $status,
                $pattern,
                $signature_id,
                $triggered_for,
                $page_url,
                $request_method,
                $x_forwarded_for,
                $network,
                $mask,
                $country_code,
                $is_personal,
            )
        )->execute();
    }

    /**
     * Check if we should pass the firewall check for all modules base on request and surrounding.
     *
     * @return bool
     */
    public static function isException()
    {
        return Server::inUri('elementor/v1/globals') &&
               (
                   spbc_is_plugin_active('elementor/elementor.php') ||
                   spbc_is_plugin_active('elementor-pro/elementor-pro.php')
               );
    }
}
