Spade

Mini Shell

Directory:~$ /home/lmsyaran/public_html/joomla4/
Upload File

[Home] [System Details] [Kill Me]
Current File:~$ /home/lmsyaran/public_html/joomla4/typo3.tar

phar-stream-wrapper/composer.json000064400000001503151156263400013171
0ustar00{
    "name": "typo3/phar-stream-wrapper",
    "description": "Interceptors for PHP's native
phar:// stream handling",
    "type": "library",
    "license": "MIT",
    "homepage": "https://typo3.org/",
    "keywords": ["php", "phar",
"stream-wrapper", "security"],
    "require": {
        "php": "^5.3.3 || ^7.0",
        "ext-json": "*",
        "brumann/polyfill-unserialize": "^1.0 || ^2.0"
    },
    "require-dev": {
        "ext-xdebug": "*",
        "phpunit/phpunit": "^4.8.36"
    },
    "suggest": {
        "ext-fileinfo": "For PHP builtin file type guessing,
otherwise uses internal processing"
    },
    "autoload": {
        "psr-4": {
            "TYPO3\\PharStreamWrapper\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "TYPO3\\PharStreamWrapper\\Tests\\":
"tests/"
        }
    }
}
phar-stream-wrapper/src/Assertable.php000064400000001042151156263400014032
0ustar00<?php
namespace TYPO3\PharStreamWrapper;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

interface Assertable
{
    /**
     * @param string $path
     * @param string $command
     * @return bool
     */
    public function assert($path, $command);
}
phar-stream-wrapper/src/Behavior.php000064400000006213151156263400013511
0ustar00<?php
namespace TYPO3\PharStreamWrapper;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

class Behavior implements Assertable
{
    const COMMAND_DIR_OPENDIR = 'dir_opendir';
    const COMMAND_MKDIR = 'mkdir';
    const COMMAND_RENAME = 'rename';
    const COMMAND_RMDIR = 'rmdir';
    const COMMAND_STEAM_METADATA = 'stream_metadata';
    const COMMAND_STREAM_OPEN = 'stream_open';
    const COMMAND_UNLINK = 'unlink';
    const COMMAND_URL_STAT = 'url_stat';

    /**
     * @var string[]
     */
    private $availableCommands = array(
        self::COMMAND_DIR_OPENDIR,
        self::COMMAND_MKDIR,
        self::COMMAND_RENAME,
        self::COMMAND_RMDIR,
        self::COMMAND_STEAM_METADATA,
        self::COMMAND_STREAM_OPEN,
        self::COMMAND_UNLINK,
        self::COMMAND_URL_STAT,
    );

    /**
     * @var Assertable[]
     */
    private $assertions;

    /**
     * @param Assertable $assertable
     * @return static
     */
    public function withAssertion(Assertable $assertable)
    {
        $commands = func_get_args();
        array_shift($commands);
        $this->assertCommands($commands);
        $commands = $commands ?: $this->availableCommands;

        $target = clone $this;
        foreach ($commands as $command) {
            $target->assertions[$command] = $assertable;
        }
        return $target;
    }

    /**
     * @param string $path
     * @param string $command
     * @return bool
     */
    public function assert($path, $command)
    {
        $this->assertCommand($command);
        $this->assertAssertionCompleteness();

        return $this->assertions[$command]->assert($path, $command);
    }

    /**
     * @param array $commands
     */
    private function assertCommands(array $commands)
    {
        $unknownCommands = array_diff($commands,
$this->availableCommands);
        if (empty($unknownCommands)) {
            return;
        }
        throw new \LogicException(
            sprintf(
                'Unknown commands: %s',
                implode(', ', $unknownCommands)
            ),
            1535189881
        );
    }

    private function assertCommand($command)
    {
        if (in_array($command, $this->availableCommands, true)) {
            return;
        }
        throw new \LogicException(
            sprintf(
                'Unknown command "%s"',
                $command
            ),
            1535189882
        );
    }

    private function assertAssertionCompleteness()
    {
        $undefinedAssertions = array_diff(
            $this->availableCommands,
            array_keys($this->assertions)
        );
        if (empty($undefinedAssertions)) {
            return;
        }
        throw new \LogicException(
            sprintf(
                'Missing assertions for commands: %s',
                implode(', ', $undefinedAssertions)
            ),
            1535189883
        );
    }
}
phar-stream-wrapper/src/Collectable.php000064400000001663151156263420014171
0ustar00<?php
namespace TYPO3\PharStreamWrapper;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\PharStreamWrapper\Resolver\PharInvocation;

interface Collectable
{
    /**
     * @param PharInvocation $invocation
     * @return bool
     */
    public function has(PharInvocation $invocation);

    /**
     * @param PharInvocation $invocation
     * @param null $flags
     * @return bool
     */
    public function collect(PharInvocation $invocation, $flags = null);

    /**
     * @param callable $callback
     * @param bool $reverse
     * @return null|PharInvocation
     */
    public function findByCallback($callback, $reverse = false);
}
phar-stream-wrapper/src/Exception.php000064400000000655151156263420013716
0ustar00<?php
namespace TYPO3\PharStreamWrapper;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

class Exception extends \RuntimeException
{
}
phar-stream-wrapper/src/Helper.php000064400000014013151156263420013170
0ustar00<?php
namespace TYPO3\PharStreamWrapper;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

/**
 * Helper provides low-level tools on file name resolving. However it does
not
 * (and should not) maintain any runtime state information. In order to
resolve
 * Phar archive paths according resolvers have to be used.
 *
 * @see \TYPO3\PharStreamWrapper\Resolvable::resolve()
 */
class Helper
{
    /*
     * Resets PHP's OPcache if enabled as work-around for issues in
`include()`
     * or `require()` calls and OPcache delivering wrong results.
     *
     * @see https://bugs.php.net/bug.php?id=66569
     */
    public static function resetOpCache()
    {
        if (function_exists('opcache_reset')
            && function_exists('opcache_get_status')
        ) {
            $status = @opcache_get_status();
            if (!empty($status['opcache_enabled'])) {
                @opcache_reset();
            }
        }
    }

    /**
     * Determines base file that can be accessed using the regular file
system.
     * For e.g. "phar:///home/user/bundle.phar/content.txt" that
would result
     * into "/home/user/bundle.phar".
     *
     * @param string $path
     * @return string|null
     */
    public static function determineBaseFile($path)
    {
        $parts = explode('/', static::normalizePath($path));

        while (count($parts)) {
            $currentPath = implode('/', $parts);
            if (@is_file($currentPath) && realpath($currentPath)
!== false) {
                return $currentPath;
            }
            array_pop($parts);
        }

        return null;
    }

    /**
     * @param string $path
     * @return bool
     */
    public static function hasPharPrefix($path)
    {
        return stripos($path, 'phar://') === 0;
    }

    /**
     * @param string $path
     * @return string
     */
    public static function removePharPrefix($path)
    {
        $path = trim($path);
        if (!static::hasPharPrefix($path)) {
            return $path;
        }
        return substr($path, 7);
    }

    /**
     * Normalizes a path, removes phar:// prefix, fixes Windows directory
     * separators. Result is without trailing slash.
     *
     * @param string $path
     * @return string
     */
    public static function normalizePath($path)
    {
        return rtrim(
            static::normalizeWindowsPath(
                static::removePharPrefix($path)
            ),
            '/'
        );
    }

    /**
     * Fixes a path for windows-backslashes and reduces double-slashes to
single slashes
     *
     * @param string $path File path to process
     * @return string
     */
    public static function normalizeWindowsPath($path)
    {
        return str_replace('\\', '/', $path);
    }

    /**
     * Resolves all dots, slashes and removes spaces after or before a
path...
     *
     * @param string $path Input string
     * @return string Canonical path, always without trailing slash
     */
    private static function getCanonicalPath($path)
    {
        $path = static::normalizeWindowsPath($path);

        $absolutePathPrefix = '';
        if (static::isAbsolutePath($path)) {
            if (static::isWindows() && strpos($path,
':/') === 1) {
                $absolutePathPrefix = substr($path, 0, 3);
                $path = substr($path, 3);
            } else {
                $path = ltrim($path, '/');
                $absolutePathPrefix = '/';
            }
        }

        $pathParts = explode('/', $path);
        $pathPartsLength = count($pathParts);
        for ($partCount = 0; $partCount < $pathPartsLength;
$partCount++) {
            // double-slashes in path: remove element
            if ($pathParts[$partCount] === '') {
                array_splice($pathParts, $partCount, 1);
                $partCount--;
                $pathPartsLength--;
            }
            // "." in path: remove element
            if ((isset($pathParts[$partCount]) ? $pathParts[$partCount] :
'') === '.') {
                array_splice($pathParts, $partCount, 1);
                $partCount--;
                $pathPartsLength--;
            }
            // ".." in path:
            if ((isset($pathParts[$partCount]) ? $pathParts[$partCount] :
'') === '..') {
                if ($partCount === 0) {
                    array_splice($pathParts, $partCount, 1);
                    $partCount--;
                    $pathPartsLength--;
                } elseif ($partCount >= 1) {
                    // Rremove this and previous element
                    array_splice($pathParts, $partCount - 1, 2);
                    $partCount -= 2;
                    $pathPartsLength -= 2;
                } elseif ($absolutePathPrefix) {
                    // can't go higher than root dir
                    // simply remove this part and continue
                    array_splice($pathParts, $partCount, 1);
                    $partCount--;
                    $pathPartsLength--;
                }
            }
        }

        return $absolutePathPrefix . implode('/', $pathParts);
    }

    /**
     * Checks if the $path is absolute or relative (detecting either
'/' or
     * 'x:/' as first part of string) and returns TRUE if so.
     *
     * @param string $path File path to evaluate
     * @return bool
     */
    private static function isAbsolutePath($path)
    {
        // Path starting with a / is always absolute, on every system
        // On Windows also a path starting with a drive letter is absolute:
X:/
        return (isset($path[0]) ? $path[0] : null) === '/'
            || static::isWindows() && (
                strpos($path, ':/') === 1
                || strpos($path, ':\\') === 1
            );
    }

    /**
     * @return bool
     */
    private static function isWindows()
    {
        return stripos(PHP_OS, 'WIN') === 0;
    }
}
phar-stream-wrapper/src/Interceptor/ConjunctionInterceptor.php000064400000004250151156263420020761
0ustar00<?php
namespace TYPO3\PharStreamWrapper\Interceptor;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\PharStreamWrapper\Assertable;
use TYPO3\PharStreamWrapper\Exception;

class ConjunctionInterceptor implements Assertable
{
    /**
     * @var Assertable[]
     */
    private $assertions;

    public function __construct(array $assertions)
    {
        $this->assertAssertions($assertions);
        $this->assertions = $assertions;
    }

    /**
     * Executes assertions based on all contained assertions.
     *
     * @param string $path
     * @param string $command
     * @return bool
     * @throws Exception
     */
    public function assert($path, $command)
    {
        if ($this->invokeAssertions($path, $command)) {
            return true;
        }
        throw new Exception(
            sprintf(
                'Assertion failed in "%s"',
                $path
            ),
            1539625084
        );
    }

    /**
     * @param Assertable[] $assertions
     */
    private function assertAssertions(array $assertions)
    {
        foreach ($assertions as $assertion) {
            if (!$assertion instanceof Assertable) {
                throw new \InvalidArgumentException(
                    sprintf(
                        'Instance %s must implement Assertable',
                        get_class($assertion)
                    ),
                    1539624719
                );
            }
        }
    }

    /**
     * @param string $path
     * @param string $command
     * @return bool
     */
    private function invokeAssertions($path, $command)
    {
        try {
            foreach ($this->assertions as $assertion) {
                if (!$assertion->assert($path, $command)) {
                    return false;
                }
            }
        } catch (Exception $exception) {
            return false;
        }
        return true;
    }
}
phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.php000064400000002717151156263420021265
0ustar00<?php
namespace TYPO3\PharStreamWrapper\Interceptor;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\PharStreamWrapper\Assertable;
use TYPO3\PharStreamWrapper\Exception;
use TYPO3\PharStreamWrapper\Manager;

class PharExtensionInterceptor implements Assertable
{
    /**
     * Determines whether the base file name has a ".phar"
suffix.
     *
     * @param string $path
     * @param string $command
     * @return bool
     * @throws Exception
     */
    public function assert($path, $command)
    {
        if ($this->baseFileContainsPharExtension($path)) {
            return true;
        }
        throw new Exception(
            sprintf(
                'Unexpected file extension in "%s"',
                $path
            ),
            1535198703
        );
    }

    /**
     * @param string $path
     * @return bool
     */
    private function baseFileContainsPharExtension($path)
    {
        $invocation = Manager::instance()->resolve($path);
        if ($invocation === null) {
            return false;
        }
        $fileExtension = pathinfo($invocation->getBaseName(),
PATHINFO_EXTENSION);
        return strtolower($fileExtension) === 'phar';
    }
}
phar-stream-wrapper/src/Interceptor/PharMetaDataInterceptor.php000064400000004213151156263440020764
0ustar00<?php
namespace TYPO3\PharStreamWrapper\Interceptor;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\PharStreamWrapper\Assertable;
use TYPO3\PharStreamWrapper\Exception;
use TYPO3\PharStreamWrapper\Manager;
use TYPO3\PharStreamWrapper\Phar\DeserializationException;
use TYPO3\PharStreamWrapper\Phar\Reader;

/**
 * @internal Experimental implementation of checking against serialized
objects in Phar meta-data
 * @internal This functionality has not been 100% pentested...
 */
class PharMetaDataInterceptor implements Assertable
{
    /**
     * Determines whether the according Phar archive contains
     * (potential insecure) serialized objects.
     *
     * @param string $path
     * @param string $command
     * @return bool
     * @throws Exception
     */
    public function assert($path, $command)
    {
        if ($this->baseFileDoesNotHaveMetaDataIssues($path)) {
            return true;
        }
        throw new Exception(
            sprintf(
                'Problematic meta-data in "%s"',
                $path
            ),
            1539632368
        );
    }

    /**
     * @param string $path
     * @return bool
     */
    private function baseFileDoesNotHaveMetaDataIssues($path)
    {
        $invocation = Manager::instance()->resolve($path);
        if ($invocation === null) {
            return false;
        }
        // directly return in case invocation was checked before
        if ($invocation->getVariable(__CLASS__) === true) {
            return true;
        }
        // otherwise analyze meta-data
        try {
            $reader = new Reader($invocation->getBaseName());
           
$reader->resolveContainer()->getManifest()->deserializeMetaData();
            $invocation->setVariable(__CLASS__, true);
        } catch (DeserializationException $exception) {
            return false;
        }
        return true;
    }
}
phar-stream-wrapper/src/Manager.php000064400000006012151156263450013326
0ustar00<?php
namespace TYPO3\PharStreamWrapper;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\PharStreamWrapper\Resolver\PharInvocation;
use TYPO3\PharStreamWrapper\Resolver\PharInvocationCollection;
use TYPO3\PharStreamWrapper\Resolver\PharInvocationResolver;

class Manager
{
    /**
     * @var self
     */
    private static $instance;

    /**
     * @var Behavior
     */
    private $behavior;

    /**
     * @var Resolvable
     */
    private $resolver;

    /**
     * @var Collectable
     */
    private $collection;

    /**
     * @param Behavior $behaviour
     * @param Resolvable $resolver
     * @param Collectable $collection
     * @return self
     */
    public static function initialize(
        Behavior $behaviour,
        Resolvable $resolver = null,
        Collectable $collection = null
    ) {
        if (self::$instance === null) {
            self::$instance = new self($behaviour, $resolver, $collection);
            return self::$instance;
        }
        throw new \LogicException(
            'Manager can only be initialized once',
            1535189871
        );
    }

    /**
     * @return self
     */
    public static function instance()
    {
        if (self::$instance !== null) {
            return self::$instance;
        }
        throw new \LogicException(
            'Manager needs to be initialized first',
            1535189872
        );
    }

    /**
     * @return bool
     */
    public static function destroy()
    {
        if (self::$instance === null) {
            return false;
        }
        self::$instance = null;
        return true;
    }

    /**
     * @param Behavior $behaviour
     * @param Resolvable $resolver
     * @param Collectable $collection
     */
    private function __construct(
        Behavior $behaviour,
        Resolvable $resolver = null,
        Collectable $collection = null
    ) {
        if ($collection === null) {
            $collection = new PharInvocationCollection();
        }
        if ($resolver === null) {
            $resolver = new PharInvocationResolver();
        }
        $this->collection = $collection;
        $this->resolver = $resolver;
        $this->behavior = $behaviour;
    }

    /**
     * @param string $path
     * @param string $command
     * @return bool
     */
    public function assert($path, $command)
    {
        return $this->behavior->assert($path, $command);
    }

    /**
     * @param string $path
     * @param null|int $flags
     * @return null|PharInvocation
     */
    public function resolve($path, $flags = null)
    {
        return $this->resolver->resolve($path, $flags);
    }

    /**
     * @return Collectable
     */
    public function getCollection()
    {
        return $this->collection;
    }
}
phar-stream-wrapper/src/Phar/Container.php000064400000002143151156263460014572
0ustar00<?php
namespace TYPO3\PharStreamWrapper\Phar;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

class Container
{
    /**
     * @var Stub
     */
    private $stub;

    /**
     * @var Manifest
     */
    private $manifest;

    /**
     * @param Stub $stub
     * @param Manifest $manifest
     */
    public function __construct(Stub $stub, Manifest $manifest)
    {
        $this->stub = $stub;
        $this->manifest = $manifest;
    }

    /**
     * @return Stub
     */
    public function getStub()
    {
        return $this->stub;
    }

    /**
     * @return Manifest
     */
    public function getManifest()
    {
        return $this->manifest;
    }

    /**
     * @return string
     */
    public function getAlias()
    {
        return $this->manifest->getAlias() ?:
$this->stub->getMappedAlias();
    }
}
phar-stream-wrapper/src/Phar/DeserializationException.php000064400000000741151156263460017657
0ustar00<?php
namespace TYPO3\PharStreamWrapper\Phar;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\PharStreamWrapper\Exception;

class DeserializationException extends Exception
{
}
phar-stream-wrapper/src/Phar/Manifest.php000064400000007121151156263460014417
0ustar00<?php
namespace TYPO3\PharStreamWrapper\Phar;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use Brumann\Polyfill\Unserialize;

class Manifest
{
    /**
     * @param string $content
     * @return self
     * @see http://php.net/manual/en/phar.fileformat.phar.php
     */
    public static function fromContent($content)
    {
        $target = new static();
        $target->manifestLength =
Reader::resolveFourByteLittleEndian($content, 0);
        $target->amountOfFiles =
Reader::resolveFourByteLittleEndian($content, 4);
        $target->flags = Reader::resolveFourByteLittleEndian($content,
10);
        $target->aliasLength =
Reader::resolveFourByteLittleEndian($content, 14);
        $target->alias = substr($content, 18, $target->aliasLength);
        $target->metaDataLength =
Reader::resolveFourByteLittleEndian($content, 18 +
$target->aliasLength);
        $target->metaData = substr($content, 22 +
$target->aliasLength, $target->metaDataLength);

        $apiVersionNibbles = Reader::resolveTwoByteBigEndian($content, 8);
        $target->apiVersion = implode('.', array(
            ($apiVersionNibbles & 0xf000) >> 12,
            ($apiVersionNibbles & 0x0f00) >> 8,
            ($apiVersionNibbles & 0x00f0) >> 4,
        ));

        return $target;
    }

    /**
     * @var int
     */
    private $manifestLength;

    /**
     * @var int
     */
    private $amountOfFiles;

    /**
     * @var string
     */
    private $apiVersion;

    /**
     * @var int
     */
    private $flags;

    /**
     * @var int
     */
    private $aliasLength;

    /**
     * @var string
     */
    private $alias;

    /**
     * @var int
     */
    private $metaDataLength;

    /**
     * @var string
     */
    private $metaData;

    /**
     * Avoid direct instantiation.
     */
    private function __construct()
    {
    }

    /**
     * @return int
     */
    public function getManifestLength()
    {
        return $this->manifestLength;
    }

    /**
     * @return int
     */
    public function getAmountOfFiles()
    {
        return $this->amountOfFiles;
    }

    /**
     * @return string
     */
    public function getApiVersion()
    {
        return $this->apiVersion;
    }

    /**
     * @return int
     */
    public function getFlags()
    {
        return $this->flags;
    }

    /**
     * @return int
     */
    public function getAliasLength()
    {
        return $this->aliasLength;
    }

    /**
     * @return string
     */
    public function getAlias()
    {
        return $this->alias;
    }

    /**
     * @return int
     */
    public function getMetaDataLength()
    {
        return $this->metaDataLength;
    }

    /**
     * @return string
     */
    public function getMetaData()
    {
        return $this->metaData;
    }

    /**
     * @return mixed|null
     */
    public function deserializeMetaData()
    {
        if (empty($this->metaData)) {
            return null;
        }

        $result = Unserialize::unserialize($this->metaData,
array('allowed_classes' => false));

        $serialized = json_encode($result);
        if (strpos($serialized, '__PHP_Incomplete_Class_Name')
!== false) {
            throw new DeserializationException(
                'Meta-data contains serialized object',
                1539623382
            );
        }

        return $result;
    }
}
phar-stream-wrapper/src/Phar/Reader.php000064400000017264151156263460014064
0ustar00<?php
namespace TYPO3\PharStreamWrapper\Phar;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

class Reader
{
    /**
     * @var string
     */
    private $fileName;

    /**
     * Mime-type in order to use zlib, bzip2 or no compression.
     * In case ext-fileinfo is not present only the relevant types
     * 'application/x-gzip' and 'application/x-bzip2'
are assigned
     * to this class property.
     *
     * @var string
     */
    private $fileType;

    /**
     * @param string $fileName
     */
    public function __construct($fileName)
    {
        if (strpos($fileName, '://') !== false) {
            throw new ReaderException(
                'File name must not contain stream prefix',
                1539623708
            );
        }

        $this->fileName = $fileName;
        $this->fileType = $this->determineFileType();
    }

    /**
     * @return Container
     */
    public function resolveContainer()
    {
        $data = $this->extractData($this->resolveStream() .
$this->fileName);

        if ($data['stubContent'] === null) {
            throw new ReaderException(
                'Cannot resolve stub',
                1547807881
            );
        }
        if ($data['manifestContent'] === null ||
$data['manifestLength'] === null) {
            throw new ReaderException(
                'Cannot resolve manifest',
                1547807882
            );
        }
        if (strlen($data['manifestContent']) <
$data['manifestLength']) {
            throw new ReaderException(
                sprintf(
                    'Exected manifest length %d, got %d',
                    strlen($data['manifestContent']),
                    $data['manifestLength']
                ),
                1547807883
            );
        }

        return new Container(
            Stub::fromContent($data['stubContent']),
            Manifest::fromContent($data['manifestContent'])
        );
    }

    /**
     * @param string $fileName e.g. '/path/file.phar' or
'compress.zlib:///path/file.phar'
     * @return array
     */
    private function extractData($fileName)
    {
        $stubContent = null;
        $manifestContent = null;
        $manifestLength = null;

        $resource = fopen($fileName, 'r');
        if (!is_resource($resource)) {
            throw new ReaderException(
                sprintf('Resource %s could not be opened',
$fileName),
                1547902055
            );
        }

        while (!feof($resource)) {
            $line = fgets($resource);
            // stop reading file when manifest can be extracted
            if ($manifestLength !== null && $manifestContent !==
null && strlen($manifestContent) >= $manifestLength) {
                break;
            }

            $manifestPosition = strpos($line,
'__HALT_COMPILER();');

            // first line contains start of manifest
            if ($stubContent === null && $manifestContent === null
&& $manifestPosition !== false) {
                $stubContent = substr($line, 0, $manifestPosition - 1);
                $manifestContent =
preg_replace('#^.*__HALT_COMPILER\(\);(?>[
\n]\?>(?>\r\n|\n)?)?#', '', $line);
                $manifestLength =
$this->resolveManifestLength($manifestContent);
            // line contains start of stub
            } elseif ($stubContent === null) {
                $stubContent = $line;
            // line contains start of manifest
            } elseif ($manifestContent === null &&
$manifestPosition !== false) {
                $manifestContent =
preg_replace('#^.*__HALT_COMPILER\(\);(?>[
\n]\?>(?>\r\n|\n)?)?#', '', $line);
                $manifestLength =
$this->resolveManifestLength($manifestContent);
            // manifest has been started (thus is cannot be stub anymore),
add content
            } elseif ($manifestContent !== null) {
                $manifestContent .= $line;
                $manifestLength =
$this->resolveManifestLength($manifestContent);
            // stub has been started (thus cannot be manifest here, yet),
add content
            } elseif ($stubContent !== null) {
                $stubContent .= $line;
            }
        }
        fclose($resource);

        return array(
            'stubContent' => $stubContent,
            'manifestContent' => $manifestContent,
            'manifestLength' => $manifestLength,
        );
    }

    /**
     * Resolves stream in order to handle compressed Phar archives.
     *
     * @return string
     */
    private function resolveStream()
    {
        if ($this->fileType === 'application/x-gzip' ||
$this->fileType === 'application/gzip') {
            return 'compress.zlib://';
        } elseif ($this->fileType === 'application/x-bzip2') {
            return 'compress.bzip2://';
        }
        return '';
    }

    /**
     * @return string
     */
    private function determineFileType()
    {
        if (class_exists('\\finfo')) {
            $fileInfo = new \finfo();
            return $fileInfo->file($this->fileName,
FILEINFO_MIME_TYPE);
        }
        return $this->determineFileTypeByHeader();
    }

    /**
     * In case ext-fileinfo is not present only the relevant types
     * 'application/x-gzip' and 'application/x-bzip2'
are resolved.
     *
     * @return string
     */
    private function determineFileTypeByHeader()
    {
        $resource = fopen($this->fileName, 'r');
        if (!is_resource($resource)) {
            throw new ReaderException(
                sprintf('Resource %s could not be opened',
$this->fileName),
                1557753055
            );
        }
        $header = fgets($resource, 4);
        fclose($resource);
        $mimeType = '';
        if (strpos($header, "\x42\x5a\x68") === 0) {
            $mimeType = 'application/x-bzip2';
        } elseif (strpos($header, "\x1f\x8b") === 0) {
            $mimeType = 'application/x-gzip';
        }
        return $mimeType;
    }

    /**
     * @param string $content
     * @return int|null
     */
    private function resolveManifestLength($content)
    {
        if (strlen($content) < 4) {
            return null;
        }
        return static::resolveFourByteLittleEndian($content, 0);
    }

    /**
     * @param string $content
     * @param int $start
     * @return int
     */
    public static function resolveFourByteLittleEndian($content, $start)
    {
        $payload = substr($content, $start, 4);
        if (!is_string($payload)) {
            throw new ReaderException(
                sprintf('Cannot resolve value at offset %d',
$start),
                1539614260
            );
        }

        $value = unpack('V', $payload);
        if (!isset($value[1])) {
            throw new ReaderException(
                sprintf('Cannot resolve value at offset %d',
$start),
                1539614261
            );
        }
        return $value[1];
    }

    /**
     * @param string $content
     * @param int $start
     * @return int
     */
    public static function resolveTwoByteBigEndian($content, $start)
    {
        $payload = substr($content, $start, 2);
        if (!is_string($payload)) {
            throw new ReaderException(
                sprintf('Cannot resolve value at offset %d',
$start),
                1539614263
            );
        }

        $value = unpack('n', $payload);
        if (!isset($value[1])) {
            throw new ReaderException(
                sprintf('Cannot resolve value at offset %d',
$start),
                1539614264
            );
        }
        return $value[1];
    }
}
phar-stream-wrapper/src/Phar/ReaderException.php000064400000000730151156263460015731
0ustar00<?php
namespace TYPO3\PharStreamWrapper\Phar;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\PharStreamWrapper\Exception;

class ReaderException extends Exception
{
}
phar-stream-wrapper/src/Phar/Stub.php000064400000002613151156263460013567
0ustar00<?php
namespace TYPO3\PharStreamWrapper\Phar;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

/**
 * @internal Experimental implementation of Phar archive internals
 */
class Stub
{
    /**
     * @param string $content
     * @return self
     */
    public static function fromContent($content)
    {
        $target = new static();
        $target->content = $content;

        if (
            stripos($content, 'Phar::mapPhar(') !== false
            && preg_match('#Phar\:\:mapPhar\(([^)]+)\)#',
$content, $matches)
        ) {
            // remove spaces, single & double quotes
            // @todo `'my' . 'alias' .
'.phar'` is not evaluated here
            $target->mappedAlias = trim($matches[1], '
\'"');
        }

        return $target;
    }

    /**
     * @var string
     */
    private $content;

    /**
     * @var string
     */
    private $mappedAlias = '';

    /**
     * @return string
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * @return string
     */
    public function getMappedAlias()
    {
        return $this->mappedAlias;
    }
}
phar-stream-wrapper/src/PharStreamWrapper.php000064400000030607151156263460015373
0ustar00<?php
namespace TYPO3\PharStreamWrapper;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\PharStreamWrapper\Resolver\PharInvocation;

class PharStreamWrapper
{
    /**
     * Internal stream constants that are not exposed to PHP, but used...
     * @see
https://github.com/php/php-src/blob/e17fc0d73c611ad0207cac8a4a01ded38251a7dc/main/php_streams.h
     */
    const STREAM_OPEN_FOR_INCLUDE = 128;

    /**
     * @var resource
     */
    public $context;

    /**
     * @var resource
     */
    protected $internalResource;

    /**
     * @var PharInvocation
     */
    protected $invocation;

    /**
     * @return bool
     */
    public function dir_closedir()
    {
        if (!is_resource($this->internalResource)) {
            return false;
        }

        $this->invokeInternalStreamWrapper(
            'closedir',
            $this->internalResource
        );
        return !is_resource($this->internalResource);
    }

    /**
     * @param string $path
     * @param int $options
     * @return bool
     */
    public function dir_opendir($path, $options)
    {
        $this->assert($path, Behavior::COMMAND_DIR_OPENDIR);
        $this->internalResource = $this->invokeInternalStreamWrapper(
            'opendir',
            $path,
            $this->context
        );
        return is_resource($this->internalResource);
    }

    /**
     * @return string|false
     */
    public function dir_readdir()
    {
        return $this->invokeInternalStreamWrapper(
            'readdir',
            $this->internalResource
        );
    }

    /**
     * @return bool
     */
    public function dir_rewinddir()
    {
        if (!is_resource($this->internalResource)) {
            return false;
        }

        $this->invokeInternalStreamWrapper(
            'rewinddir',
            $this->internalResource
        );
        return is_resource($this->internalResource);
    }

    /**
     * @param string $path
     * @param int $mode
     * @param int $options
     * @return bool
     */
    public function mkdir($path, $mode, $options)
    {
        $this->assert($path, Behavior::COMMAND_MKDIR);
        return $this->invokeInternalStreamWrapper(
            'mkdir',
            $path,
            $mode,
            (bool) ($options & STREAM_MKDIR_RECURSIVE),
            $this->context
        );
    }

    /**
     * @param string $path_from
     * @param string $path_to
     * @return bool
     */
    public function rename($path_from, $path_to)
    {
        $this->assert($path_from, Behavior::COMMAND_RENAME);
        $this->assert($path_to, Behavior::COMMAND_RENAME);
        return $this->invokeInternalStreamWrapper(
            'rename',
            $path_from,
            $path_to,
            $this->context
        );
    }

    /**
     * @param string $path
     * @param int $options
     * @return bool
     */
    public function rmdir($path, $options)
    {
        $this->assert($path, Behavior::COMMAND_RMDIR);
        return $this->invokeInternalStreamWrapper(
            'rmdir',
            $path,
            $this->context
        );
    }

    /**
     * @param int $cast_as
     */
    public function stream_cast($cast_as)
    {
        throw new Exception(
            'Method stream_select() cannot be used',
            1530103999
        );
    }

    public function stream_close()
    {
        $this->invokeInternalStreamWrapper(
            'fclose',
            $this->internalResource
        );
    }

    /**
     * @return bool
     */
    public function stream_eof()
    {
        return $this->invokeInternalStreamWrapper(
            'feof',
            $this->internalResource
        );
    }

    /**
     * @return bool
     */
    public function stream_flush()
    {
        return $this->invokeInternalStreamWrapper(
            'fflush',
            $this->internalResource
        );
    }

    /**
     * @param int $operation
     * @return bool
     */
    public function stream_lock($operation)
    {
        return $this->invokeInternalStreamWrapper(
            'flock',
            $this->internalResource,
            $operation
        );
    }

    /**
     * @param string $path
     * @param int $option
     * @param string|int $value
     * @return bool
     */
    public function stream_metadata($path, $option, $value)
    {
        $this->assert($path, Behavior::COMMAND_STEAM_METADATA);
        if ($option === STREAM_META_TOUCH) {
            return call_user_func_array(
                array($this, 'invokeInternalStreamWrapper'),
                array_merge(array('touch', $path), (array)
$value)
            );
        }
        if ($option === STREAM_META_OWNER_NAME || $option ===
STREAM_META_OWNER) {
            return $this->invokeInternalStreamWrapper(
                'chown',
                $path,
                $value
            );
        }
        if ($option === STREAM_META_GROUP_NAME || $option ===
STREAM_META_GROUP) {
            return $this->invokeInternalStreamWrapper(
                'chgrp',
                $path,
                $value
            );
        }
        if ($option === STREAM_META_ACCESS) {
            return $this->invokeInternalStreamWrapper(
                'chmod',
                $path,
                $value
            );
        }
        return false;
    }

    /**
     * @param string $path
     * @param string $mode
     * @param int $options
     * @param string|null $opened_path
     * @return bool
     */
    public function stream_open(
        $path,
        $mode,
        $options,
        &$opened_path = null
    ) {
        $this->assert($path, Behavior::COMMAND_STREAM_OPEN);
        $arguments = array($path, $mode, (bool) ($options &
STREAM_USE_PATH));
        // only add stream context for non include/require calls
        if (!($options & static::STREAM_OPEN_FOR_INCLUDE)) {
            $arguments[] = $this->context;
        // work around https://bugs.php.net/bug.php?id=66569
        // for including files from Phar stream with OPcache enabled
        } else {
            Helper::resetOpCache();
        }
        $this->internalResource = call_user_func_array(
            array($this, 'invokeInternalStreamWrapper'),
            array_merge(array('fopen'), $arguments)
        );
        if (!is_resource($this->internalResource)) {
            return false;
        }
        if ($opened_path !== null) {
            $metaData = stream_get_meta_data($this->internalResource);
            $opened_path = $metaData['uri'];
        }
        return true;
    }

    /**
     * @param int $count
     * @return string
     */
    public function stream_read($count)
    {
        return $this->invokeInternalStreamWrapper(
            'fread',
            $this->internalResource,
            $count
        );
    }

    /**
     * @param int $offset
     * @param int $whence
     * @return bool
     */
    public function stream_seek($offset, $whence = SEEK_SET)
    {
        return $this->invokeInternalStreamWrapper(
            'fseek',
            $this->internalResource,
            $offset,
            $whence
        ) !== -1;
    }

    /**
     * @param int $option
     * @param int $arg1
     * @param int $arg2
     * @return bool
     */
    public function stream_set_option($option, $arg1, $arg2)
    {
        if ($option === STREAM_OPTION_BLOCKING) {
            return $this->invokeInternalStreamWrapper(
                'stream_set_blocking',
                $this->internalResource,
                $arg1
            );
        }
        if ($option === STREAM_OPTION_READ_TIMEOUT) {
            return $this->invokeInternalStreamWrapper(
                'stream_set_timeout',
                $this->internalResource,
                $arg1,
                $arg2
            );
        }
        if ($option === STREAM_OPTION_WRITE_BUFFER) {
            return $this->invokeInternalStreamWrapper(
                'stream_set_write_buffer',
                $this->internalResource,
                $arg2
            ) === 0;
        }
        return false;
    }

    /**
     * @return array
     */
    public function stream_stat()
    {
        return $this->invokeInternalStreamWrapper(
            'fstat',
            $this->internalResource
        );
    }

    /**
     * @return int
     */
    public function stream_tell()
    {
        return $this->invokeInternalStreamWrapper(
            'ftell',
            $this->internalResource
        );
    }

    /**
     * @param int $new_size
     * @return bool
     */
    public function stream_truncate($new_size)
    {
        return $this->invokeInternalStreamWrapper(
            'ftruncate',
            $this->internalResource,
            $new_size
        );
    }

    /**
     * @param string $data
     * @return int
     */
    public function stream_write($data)
    {
        return $this->invokeInternalStreamWrapper(
            'fwrite',
            $this->internalResource,
            $data
        );
    }

    /**
     * @param string $path
     * @return bool
     */
    public function unlink($path)
    {
        $this->assert($path, Behavior::COMMAND_UNLINK);
        return $this->invokeInternalStreamWrapper(
            'unlink',
            $path,
            $this->context
        );
    }

    /**
     * @param string $path
     * @param int $flags
     * @return array|false
     */
    public function url_stat($path, $flags)
    {
        $this->assert($path, Behavior::COMMAND_URL_STAT);
        $functionName = $flags & STREAM_URL_STAT_QUIET ?
'@stat' : 'stat';
        return $this->invokeInternalStreamWrapper($functionName, $path);
    }

    /**
     * @param string $path
     * @param string $command
     */
    protected function assert($path, $command)
    {
        if (Manager::instance()->assert($path, $command) === true) {
            $this->collectInvocation($path);
            return;
        }

        throw new Exception(
            sprintf(
                'Denied invocation of "%s" for command
"%s"',
                $path,
                $command
            ),
            1535189880
        );
    }

    /**
     * @param string $path
     */
    protected function collectInvocation($path)
    {
        if (isset($this->invocation)) {
            return;
        }

        $manager = Manager::instance();
        $this->invocation = $manager->resolve($path);
        if ($this->invocation === null) {
            throw new Exception(
                'Expected invocation could not be resolved',
                1556389591
            );
        }
        // confirm, previous interceptor(s) validated invocation
        $this->invocation->confirm();
        $collection = $manager->getCollection();
        if (!$collection->has($this->invocation)) {
            $collection->collect($this->invocation);
        }
    }

    /**
     * @return Manager|Assertable
     * @deprecated Use Manager::instance() directly
     */
    protected function resolveAssertable()
    {
        return Manager::instance();
    }

    /**
     * Invokes commands on the native PHP Phar stream wrapper.
     *
     * @param string $functionName
     * @param mixed ...$arguments
     * @return mixed
     */
    private function invokeInternalStreamWrapper($functionName)
    {
        $arguments = func_get_args();
        array_shift($arguments);
        $silentExecution = $functionName[0] === '@';
        $functionName = ltrim($functionName, '@');
        $this->restoreInternalSteamWrapper();

        try {
            if ($silentExecution) {
                $result = @call_user_func_array($functionName, $arguments);
            } else {
                $result = call_user_func_array($functionName, $arguments);
            }
        } catch (\Exception $exception) {
            $this->registerStreamWrapper();
            throw $exception;
        } catch (\Throwable $throwable) {
            $this->registerStreamWrapper();
            throw $throwable;
        }

        $this->registerStreamWrapper();
        return $result;
    }

    private function restoreInternalSteamWrapper()
    {
        stream_wrapper_restore('phar');
    }

    private function registerStreamWrapper()
    {
        stream_wrapper_unregister('phar');
        stream_wrapper_register('phar', get_class($this));
    }
}
phar-stream-wrapper/src/Resolvable.php000064400000001155151156263500014051
0ustar00<?php
namespace TYPO3\PharStreamWrapper;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\PharStreamWrapper\Resolver\PharInvocation;

interface Resolvable
{
    /**
     * @param string $path
     * @param null|int $flags
     * @return null|PharInvocation
     */
    public function resolve($path, $flags = null);
}
phar-stream-wrapper/src/Resolver/PharInvocation.php000064400000004654151156263500016507
0ustar00<?php
namespace TYPO3\PharStreamWrapper\Resolver;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\PharStreamWrapper\Exception;

class PharInvocation
{
    /**
     * @var string
     */
    private $baseName;

    /**
     * @var string
     */
    private $alias;

    /**
     * @var bool
     * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation()
     */
    private $confirmed = false;

    /**
     * Arbitrary variables to be used by interceptors as registry
     * (e.g. in order to avoid duplicate processing and assertions)
     *
     * @var array
     */
    private $variables;

    /**
     * @param string $baseName
     * @param string $alias
     */
    public function __construct($baseName, $alias = '')
    {
        if ($baseName === '') {
            throw new Exception(
                'Base-name cannot be empty',
                1551283689
            );
        }
        $this->baseName = $baseName;
        $this->alias = $alias;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return $this->baseName;
    }

    /**
     * @return string
     */
    public function getBaseName()
    {
        return $this->baseName;
    }

    /**
     * @return null|string
     */
    public function getAlias()
    {
        return $this->alias;
    }

    /**
     * @return bool
     */
    public function isConfirmed()
    {
        return $this->confirmed;
    }

    public function confirm()
    {
        $this->confirmed = true;
    }

    /**
     * @param string $name
     * @return mixed|null
     */
    public function getVariable($name)
    {
        if (!isset($this->variables[$name])) {
            return null;
        }
        return $this->variables[$name];
    }

    /**
     * @param string $name
     * @param mixed $value
     */
    public function setVariable($name, $value)
    {
        $this->variables[$name] = $value;
    }

    /**
     * @param PharInvocation $other
     * @return bool
     */
    public function equals(PharInvocation $other)
    {
        return $other->baseName === $this->baseName
            && $other->alias === $this->alias;
    }
}phar-stream-wrapper/src/Resolver/PharInvocationCollection.php000064400000011145151156263500020514
0ustar00<?php
namespace TYPO3\PharStreamWrapper\Resolver;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\PharStreamWrapper\Collectable;

class PharInvocationCollection implements Collectable
{
    const UNIQUE_INVOCATION = 1;
    const UNIQUE_BASE_NAME = 2;
    const DUPLICATE_ALIAS_WARNING = 32;

    /**
     * @var PharInvocation[]
     */
    private $invocations = array();

    /**
     * @param PharInvocation $invocation
     * @return bool
     */
    public function has(PharInvocation $invocation)
    {
        return in_array($invocation, $this->invocations, true);
    }

    /**
     * @param PharInvocation $invocation
     * @param null|int $flags
     * @return bool
     */
    public function collect(PharInvocation $invocation, $flags = null)
    {
        if ($flags === null) {
            $flags = static::UNIQUE_INVOCATION |
static::DUPLICATE_ALIAS_WARNING;
        }
        if ($invocation->getBaseName() === ''
            || $invocation->getAlias() === ''
            || !$this->assertUniqueBaseName($invocation, $flags)
            || !$this->assertUniqueInvocation($invocation, $flags)
        ) {
            return false;
        }
        if ($flags & static::DUPLICATE_ALIAS_WARNING) {
            $this->triggerDuplicateAliasWarning($invocation);
        }

        $this->invocations[] = $invocation;
        return true;
    }

    /**
     * @param callable $callback
     * @param bool $reverse
     * @return null|PharInvocation
     */
    public function findByCallback($callback, $reverse = false)
    {
        foreach ($this->getInvocations($reverse) as $invocation) {
            if (call_user_func($callback, $invocation) === true) {
                return $invocation;
            }
        }
        return null;
    }

    /**
     * Asserts that base-name is unique. This disallows having multiple
invocations for
     * same base-name but having different alias names.
     *
     * @param PharInvocation $invocation
     * @param int $flags
     * @return bool
     */
    private function assertUniqueBaseName(PharInvocation $invocation,
$flags)
    {
        if (!($flags & static::UNIQUE_BASE_NAME)) {
            return true;
        }
        return $this->findByCallback(
                function (PharInvocation $candidate) use ($invocation) {
                    return $candidate->getBaseName() ===
$invocation->getBaseName();
                }
            ) === null;
    }

    /**
     * Asserts that combination of base-name and alias is unique. This
allows having multiple
     * invocations for same base-name but having different alias names (for
whatever reason).
     *
     * @param PharInvocation $invocation
     * @param int $flags
     * @return bool
     */
    private function assertUniqueInvocation(PharInvocation $invocation,
$flags)
    {
        if (!($flags & static::UNIQUE_INVOCATION)) {
            return true;
        }
        return $this->findByCallback(
                function (PharInvocation $candidate) use ($invocation) {
                    return $candidate->equals($invocation);
                }
            ) === null;
    }

    /**
     * Triggers warning for invocations with same alias and same
confirmation state.
     *
     * @param PharInvocation $invocation
     * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation()
     */
    private function triggerDuplicateAliasWarning(PharInvocation
$invocation)
    {
        $sameAliasInvocation = $this->findByCallback(
            function (PharInvocation $candidate) use ($invocation) {
                return $candidate->isConfirmed() ===
$invocation->isConfirmed()
                    && $candidate->getAlias() ===
$invocation->getAlias();
            },
            true
        );
        if ($sameAliasInvocation === null) {
            return;
        }
        trigger_error(
            sprintf(
                'Alias %s cannot be used by %s, already used by
%s',
                $invocation->getAlias(),
                $invocation->getBaseName(),
                $sameAliasInvocation->getBaseName()
            ),
            E_USER_WARNING
        );
    }

    /**
     * @param bool $reverse
     * @return PharInvocation[]
     */
    private function getInvocations($reverse = false)
    {
        if ($reverse) {
            return array_reverse($this->invocations);
        }
        return $this->invocations;
    }
}phar-stream-wrapper/src/Resolver/PharInvocationResolver.php000064400000016677151156263510020242
0ustar00<?php
namespace TYPO3\PharStreamWrapper\Resolver;

/*
 * This file is part of the TYPO3 project.
 *
 * It is free software; you can redistribute it and/or modify it under the
terms
 * of the MIT License (MIT). For the full copyright and license
information,
 * please read the LICENSE file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

use TYPO3\PharStreamWrapper\Helper;
use TYPO3\PharStreamWrapper\Manager;
use TYPO3\PharStreamWrapper\Phar\Reader;
use TYPO3\PharStreamWrapper\Phar\ReaderException;
use TYPO3\PharStreamWrapper\Resolvable;

class PharInvocationResolver implements Resolvable
{
    const RESOLVE_REALPATH = 1;
    const RESOLVE_ALIAS = 2;
    const ASSERT_INTERNAL_INVOCATION = 32;

    /**
     * @var string[]
     */
    private $invocationFunctionNames = array(
        'include',
        'include_once',
        'require',
        'require_once'
    );

    /**
     * Contains resolved base names in order to reduce file IO.
     *
     * @var string[]
     */
    private $baseNames = array();

    /**
     * Resolves PharInvocation value object (baseName and optional alias).
     *
     * Phar aliases are intended to be used only inside Phar archives,
however
     * PharStreamWrapper needs this information exposed outside of Phar as
well
     * It is possible that same alias is used for different $baseName
values.
     * That's why PharInvocationCollection behaves like a stack when
resolving
     * base-name for a given alias. On the other hand it is not possible
that
     * one $baseName is referring to multiple aliases.
     * @see https://secure.php.net/manual/en/phar.setalias.php
     * @see https://secure.php.net/manual/en/phar.mapphar.php
     *
     * @param string $path
     * @param int|null $flags
     * @return null|PharInvocation
     */
    public function resolve($path, $flags = null)
    {
        $hasPharPrefix = Helper::hasPharPrefix($path);
        if ($flags === null) {
            $flags = static::RESOLVE_REALPATH | static::RESOLVE_ALIAS;
        }

        if ($hasPharPrefix && $flags & static::RESOLVE_ALIAS) {
            $invocation = $this->findByAlias($path);
            if ($invocation !== null) {
                return $invocation;
            }
        }

        $baseName = $this->resolveBaseName($path, $flags);
        if ($baseName === null) {
            return null;
        }

        if ($flags & static::RESOLVE_REALPATH) {
            $baseName = $this->baseNames[$baseName];
        }

        return $this->retrieveInvocation($baseName, $flags);
    }

    /**
     * Retrieves PharInvocation, either existing in collection or created
on demand
     * with resolving a potential alias name used in the according Phar
archive.
     *
     * @param string $baseName
     * @param int $flags
     * @return PharInvocation
     */
    private function retrieveInvocation($baseName, $flags)
    {
        $invocation = $this->findByBaseName($baseName);
        if ($invocation !== null) {
            return $invocation;
        }

        if ($flags & static::RESOLVE_ALIAS) {
            $reader = new Reader($baseName);
            $alias = $reader->resolveContainer()->getAlias();
        } else {
            $alias = '';
        }
        // add unconfirmed(!) new invocation to collection
        $invocation = new PharInvocation($baseName, $alias);
        Manager::instance()->getCollection()->collect($invocation);
        return $invocation;
    }

    /**
     * @param string $path
     * @param int $flags
     * @return null|string
     */
    private function resolveBaseName($path, $flags)
    {
        $baseName = $this->findInBaseNames($path);
        if ($baseName !== null) {
            return $baseName;
        }

        $baseName = Helper::determineBaseFile($path);
        if ($baseName !== null) {
            $this->addBaseName($baseName);
            return $baseName;
        }

        $possibleAlias = $this->resolvePossibleAlias($path);
        if (!($flags & static::RESOLVE_ALIAS) || $possibleAlias ===
null) {
            return null;
        }

        $trace = debug_backtrace();
        foreach ($trace as $item) {
            if (!isset($item['function']) ||
!isset($item['args'][0])
                || !in_array($item['function'],
$this->invocationFunctionNames, true)) {
                continue;
            }
            $currentPath = $item['args'][0];
            if (Helper::hasPharPrefix($currentPath)) {
                continue;
            }
            $currentBaseName = Helper::determineBaseFile($currentPath);
            if ($currentBaseName === null) {
                continue;
            }
            // ensure the possible alias name (how we have been called
initially) matches
            // the resolved alias name that was retrieved by the current
possible base name
            try {
                $reader = new Reader($currentBaseName);
                $currentAlias =
$reader->resolveContainer()->getAlias();
            } catch (ReaderException $exception) {
                // most probably that was not a Phar file
                continue;
            }
            if (empty($currentAlias) || $currentAlias !== $possibleAlias) {
                continue;
            }
            $this->addBaseName($currentBaseName);
            return $currentBaseName;
        }

        return null;
    }

    /**
     * @param string $path
     * @return null|string
     */
    private function resolvePossibleAlias($path)
    {
        $normalizedPath = Helper::normalizePath($path);
        return strstr($normalizedPath, '/', true) ?: null;
    }

    /**
     * @param string $baseName
     * @return null|PharInvocation
     */
    private function findByBaseName($baseName)
    {
        return Manager::instance()->getCollection()->findByCallback(
            function (PharInvocation $candidate) use ($baseName) {
                return $candidate->getBaseName() === $baseName;
            },
            true
        );
    }

    /**
     * @param string $path
     * @return null|string
     */
    private function findInBaseNames($path)
    {
        // return directly if the resolved base name was submitted
        if (in_array($path, $this->baseNames, true)) {
            return $path;
        }

        $parts = explode('/', Helper::normalizePath($path));

        while (count($parts)) {
            $currentPath = implode('/', $parts);
            if (isset($this->baseNames[$currentPath])) {
                return $currentPath;
            }
            array_pop($parts);
        }

        return null;
    }

    /**
     * @param string $baseName
     */
    private function addBaseName($baseName)
    {
        if (isset($this->baseNames[$baseName])) {
            return;
        }
        $this->baseNames[$baseName] = Helper::normalizeWindowsPath(
            realpath($baseName)
        );
    }

    /**
     * Finds confirmed(!) invocations by alias.
     *
     * @param string $path
     * @return null|PharInvocation
     * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation()
     */
    private function findByAlias($path)
    {
        $possibleAlias = $this->resolvePossibleAlias($path);
        if ($possibleAlias === null) {
            return null;
        }
        return Manager::instance()->getCollection()->findByCallback(
            function (PharInvocation $candidate) use ($possibleAlias) {
                return $candidate->isConfirmed() &&
$candidate->getAlias() === $possibleAlias;
            },
            true
        );
    }
}