💾 Archived View for code.lanterne.chilliet.eu › vendor › mcmic › gemini-server › src › Files › Reque… captured on 2022-06-03 at 23:34:15.

View Raw

More Information

⬅️ Previous capture (2021-12-17)

-=-=-=-=-=-=-

<?php

declare(strict_types=1);

namespace MCMic\Gemini\Files;

use MCMic\Gemini;

class RequestHandler extends Gemini\RequestHandler
{
    protected string $rootPath;
    /**
     * @var array<string>
     */
    protected array $filePatterns;
    protected ?string $title = null;
    protected string $lang = '';

    /**
     * @param array<string> $filePatterns
     */
    public function __construct(string $host, string $certPath, string $rootPath, array $filePatterns, ?string $title = null, string $lang = '')
    {
        parent::__construct($host, $certPath);
        $realRootPath = realpath($rootPath);
        if ($realRootPath !== false) {
            $this->rootPath = $realRootPath;
        } else {
            $this->rootPath = $rootPath;
        }
        $this->filePatterns = $filePatterns;
        $this->title = $title;
        $this->lang = $lang;
    }

    /**
     * @throws \MCMic\Gemini\Exception
     */
    public function handle(Gemini\Request $request): Gemini\Response
    {
        $path = $request->path ?? '/';
        $path = substr($path, 1);

        $files = [];

        chdir($this->rootPath);
        foreach ($this->filePatterns as $pattern) {
            $glob = glob($pattern);
            if ($glob !== false) {
                $files = array_merge($files, $glob);
            }
        }

        $files = array_unique($files);
        $lang = $this->lang;

        if (is_dir($this->rootPath . '/' . $path)) {
            if (is_file($this->rootPath . '/' . $path . '/index.gmi')) {
                $path .= '/index.gmi';
            } elseif (is_file($this->rootPath . '/' . $path . '/index.php')) {
                ob_start();
                include($this->rootPath . '/' . $path . '/index.php');
                $content = ob_get_contents();
                ob_end_clean();
                if (!is_string($content)) {
                    throw new Gemini\Exception('', 42);
                }
                return new Gemini\Response\Success($content, 'text/gemini', $lang);
            } else {
                return new Gemini\Response\Success($this->buildIndex($path, $files), 'text/gemini', $lang);
            }
        } elseif (!in_array($path, $files, true)) {
            throw new Gemini\Exception('File not found', 51);
        }

        $content = file_get_contents($this->rootPath . '/' . $path);

        if ($content !== false) {
            if (static::str_ends_with($path, '.gmi')) {
                $mimetype = 'text/gemini';
                if (preg_match('/\.([0-9a-z]{1,8}([-,][0-9a-z]{1,8})*)\.gmi$/i', $path, $m) === 1) {
                    $lang = $m[1];
                }
            } else {
                $mimetype = mime_content_type($this->rootPath . '/' . $path);
                if ($mimetype === false) {
                    $mimetype = 'text/plain';
                }
            }
            return new Gemini\Response\Success($content, $mimetype);
        } else {
            throw new Gemini\Exception('Could not open ' . $path, 59);
        }
    }

    /**
     * @param array<string> $files
     * @throws \MCMic\Gemini\Exception
     */
    protected function buildIndex(string $path, array $files): string
    {
        if (isset($this->title)) {
            $output = '# ' . $this->title . "\r\n";
            $output .= "\r\n";
            if ($path != '') {
                $output = '## ' . '/' . $path . "\r\n";
                $output .= "\r\n";
            }
        } else {
            $output = '# ' . '/' . $path . "\r\n";
            $output .= "\r\n";
        }
        $found  = false;
        foreach ($files as $file) {
            if (($path === '') || static::str_starts_with($file, $path . '/')) {
                $found = true;
                $output .= '=> ' . Gemini\Request::urlEncode('/' . $file) . ' ' . $file . "\r\n";
            }
        }
        if ($found) {
            return $output;
        } else {
            throw new Gemini\Exception('File not found', 51);
        }
    }

    /**
     * From https://github.com/symfony/polyfill/blob/main/src/Php80/Php80.php
     * By Fabien Potencier <fabien@symfony.com>
     * License MIT
     */
    public static function str_starts_with(string $haystack, string $needle): bool
    {
        return 0 === \strncmp($haystack, $needle, \strlen($needle));
    }

    /**
     * From https://github.com/symfony/polyfill/blob/main/src/Php80/Php80.php
     * By Fabien Potencier <fabien@symfony.com>
     * License MIT
     */
    public static function str_ends_with(string $haystack, string $needle): bool
    {
        return '' === $needle || ('' !== $haystack && 0 === \substr_compare($haystack, $needle, -\strlen($needle)));
    }
}