<?php

/**
 * Matomo - free/libre analytics platform
 *
 * @link    https://matomo.org
 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 */
namespace Piwik\Plugins\Installation;

use InvalidArgumentException;
class HostPortExtractor
{
    public $host;
    public $port;
    private function __construct(string $host, string $port)
    {
        $this->host = $host;
        $this->port = $port;
    }
    /**
     * Extracts the correctly formatted host and port values from the user-provided
     * database server host.
     *
     * @param string $dbHost The user provided host to extract values from
     * @return HostPortExtractor|null Extracted Host and Port, null if nothing can be extracted
     */
    public static function extract(string $dbHost) : ?\Piwik\Plugins\Installation\HostPortExtractor
    {
        try {
            if (self::isIPv6($dbHost)) {
                return self::extractIPv6($dbHost);
            } elseif (self::isUnixSocket($dbHost)) {
                return self::extractUnixSocket($dbHost);
            } elseif (self::isIPWithPort($dbHost)) {
                return self::extractIPAndPort($dbHost);
            } else {
                return null;
            }
        } catch (InvalidArgumentException $e) {
            return null;
        }
    }
    /**
     * Determines if the provided host is correctly formatted IPv6
     *
     * @param string $dbHost The user provided database server host
     * @return boolean Whether the provided host is correct IPv6
     */
    private static function isIPv6(string $dbHost) : bool
    {
        // filter_var requires potential IPv6 addresses to not be encased
        preg_match_all('/^\\[(.*?)\\]/', $dbHost, $matches);
        $listOfTextInsideSquareBrackets = $matches[1];
        /*
         * Only return true if there is some text inside square brackets,
         * and that text is considered valid IPv6 as per filter_var()
         */
        if (count($listOfTextInsideSquareBrackets) > 0 && filter_var($listOfTextInsideSquareBrackets[0], \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6) !== \false) {
            return \true;
        }
        return \false;
    }
    /**
     * Extracts the Host and Port from a user provided database server host,
     * assuming the provided host is a IPv6 address, with or without a port
     *
     * @param string $dbHost The user provided database server host
     * @throws InvalidArgumentException if the provided dbHost is not a valid IPv6
     * @return HostPortExtractor The extracted Host & Port
     */
    private static function extractIPv6(string $dbHost) : \Piwik\Plugins\Installation\HostPortExtractor
    {
        if (!self::isIPv6($dbHost)) {
            throw new InvalidArgumentException('$dbHost must be a valid IPv6 address');
        }
        // find and extract the text inside square brackets: []
        preg_match_all('/\\[(.*?)\\]/', $dbHost, $matches);
        $listOfTextInsideSquareBrackets = $matches[1];
        $port = '';
        // extract text after closing bracket to search for port
        $components = explode(']', $dbHost);
        if (count($components) > 1 && strlen($components[1]) > 0) {
            // check if the text is a valid port e.g. ':3000'
            preg_match('/^:(\\d+)$/', $components[1], $portMatches);
            if (count($portMatches) > 0) {
                $port = $portMatches[1];
            } else {
                throw new InvalidArgumentException('$dbHost port is invalid');
            }
        }
        // db connector requires IPv6 to be encased in square brackets
        $host = '[' . $listOfTextInsideSquareBrackets[0] . ']';
        return new \Piwik\Plugins\Installation\HostPortExtractor($host, $port);
    }
    /**
     * Determines if the provided host is a Unix Socket
     *
     * @param string $dbHost The user provided database server host
     * @return boolean Whether the provided host is a unix socket
     */
    private static function isUnixSocket(string $dbHost) : bool
    {
        return strpos($dbHost, '/') !== \false;
    }
    /**
     * Extracts the host & port from the user provided database server host,
     * assuming the provided host is a unix socket
     *
     * @param string $dbHost The user provided database server host
     * @throws InvalidArgumentException if the provided dbHost is not a valid Unix Socket
     * @return HostPortExtractor The extracted Host & Port
     */
    private static function extractUnixSocket(string $dbHost) : \Piwik\Plugins\Installation\HostPortExtractor
    {
        if (!self::isUnixSocket($dbHost)) {
            throw new InvalidArgumentException('$dbHost must be a valid Unix Socket');
        }
        /*
         * The DB connector requires unix sockets to be provided as ports in
         * order to connect successfully.
         */
        $portIndex = strpos($dbHost, '/');
        $port = substr($dbHost, $portIndex);
        return new \Piwik\Plugins\Installation\HostPortExtractor('', $port);
    }
    /**
     * Determines if the provided host is a standard web address or IP with a port
     *
     * @param string $dbHost The user provided database server host
     * @return boolean Whether the provided host is a standard address or IP with port
     */
    private static function isIPWithPort(string $dbHost) : bool
    {
        $numColons = substr_count($dbHost, ':');
        return $numColons === 1;
    }
    /**
     * Extracts the host & port from the user provided database server host,
     * assuming the provided host is a standard address or IP with a port
     *
     * @param string $dbHost The user provided database server host
     * @throws InvalidArgumentException if the provided dbHost is not a valid address with a port
     * @return HostPortExtractor The extracted host & port
     */
    private static function extractIPAndPort(string $dbHost) : \Piwik\Plugins\Installation\HostPortExtractor
    {
        if (!self::isIPWithPort($dbHost)) {
            throw new InvalidArgumentException('$dbHost must be a valid address');
        }
        [$host, $port] = explode(':', $dbHost);
        return new \Piwik\Plugins\Installation\HostPortExtractor($host, $port);
    }
}
