<?php
/**
 * Pi Engine (http://piengine.org)
 *
 * @link            http://code.piengine.org for the Pi Engine source repository
 * @copyright       Copyright (c) Pi Engine http://piengine.org
 * @license         http://piengine.org/license.txt BSD 3-Clause License
 */

namespace Pi\Security;

/**
 * Security handler with variety of adapters
 *
 *
 * Policy with different result from each adapter:
 *
 * - true: following evaluations will be terminated and current request
 *      is approved
 * - false: following evaluations will be terminated and current request
 *      is denied
 * - null: continue
 *
 * @author Taiwen Jiang <taiwenjiang@tsinghua.org.cn>
 */
class Security
{
    /**
     * Check against IPs
     *
     * @param array $options
     *
     * @return bool|null
     */
    public static function ip($options = [])
    {
        $clientIp = [];
        if (!empty($_SERVER['REMOTE_ADDR'])) {
            $clientIp[] = $_SERVER['REMOTE_ADDR'];
        }

        // Find out IP behind proxy
        if (!empty($options['checkProxy'])) {
            if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
                $clientIp[] = $_SERVER['HTTP_CLIENT_IP'];
            }
            if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                $clientIp[] = $_SERVER['HTTP_X_FORWARDED_FOR'];
            }
        }
        $clientIp = array_unique($clientIp);

        // Check upon bad IPs
        if (!empty($options['bad'])) {
            $pattern = is_array($options['bad'])
                ? implode('|', $options['bad']) : $options['bad'];
            foreach ($clientIp as $ip) {
                if (preg_match('/' . $pattern . '/', $ip)) {
                    return false;
                }
            }
        }

        // Check upon good IPs
        if (!empty($options['good'])) {
            $pattern = is_array($options['good'])
                ? implode('|', $options['good']) : $options['good'];
            foreach ($clientIp as $ip) {
                if (preg_match('/' . $pattern . '/', $ip)) {
                    return true;
                }
            }
        }

        return null;
    }

    /**
     * Check against super globals contamination
     *
     * @param array $globals Name of super globals to check
     *
     * @return bool|null
     */
    public static function globals($globals = [])
    {
        array_walk($globals, 'trim');
        $items = array_filter($globals);
        foreach ($items as $item) {
            if (isset($_REQUEST[$item])) {
                return false;
            }
        }

        return null;
    }

    /**
     * Filter content against XSS
     *
     * @param string $content
     *
     * @return string
     */
    public static function filter($content)
    {
        $class   = __NAMESPACE__ . '\\Xss';
        $content = $class::checkXss($content, true);

        return $content;
    }

    /**
     * Magic method to access against custom security adapters
     *
     * @param string $method Security adapter to be checked
     * @param array  $args   Arguments for the setting
     *
     * @return bool|null
     */
    public static function __callStatic($method, $args = [])
    {
        $class = __NAMESPACE__ . '\\' . ucfirst($method);
        if (class_exists($class)
            && is_subclass_of($class, __NAMESPACE__ . '\AbstractAdapter')
        ) {
            $options = $args[0];
            return $class::check($options);
        }

        return;
    }
}
