PK %qP> SECURITY.mdnu W+A # Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 1.0.x | :white_check_mark: |
| 2.0.x | :x: |
## Reporting a Vulnerability
When you've encountered a security vulnerability, please disclose it securely.
The security process is described at:
[https://flysystem.thephpleague.com/docs/security/](https://flysystem.thephpleague.com/docs/security/)
PK %qPK deprecations.mdnu W+A # Deprecations
This document lists all the planned deprecations.
## Handlers will be removed in 2.0
The `Handler` type and associated calls will be removed in version 2.0.
### Upgrade path
You should create your own implementation for handling OOP usage,
but it's recommended to move away from using an OOP-style wrapper entirely.
The reason for this is that it's too easy for implementation details (for
your application this is Flysystem) to leak into the application. The most
important part for Flysystem is that it improves portability and creates a
solid boundary between your application core and the infrastructure you use.
The OOP-style handling breaks this principle, therefore I want to stop
promoting it.
PK %qP֎D; src/RootViolationException.phpnu W+A ''];
}
/**
* Normalize a dirname return value.
*
* @param string $dirname
*
* @return string normalized dirname
*/
public static function normalizeDirname($dirname)
{
return $dirname === '.' ? '' : $dirname;
}
/**
* Get a normalized dirname from a path.
*
* @param string $path
*
* @return string dirname
*/
public static function dirname($path)
{
return static::normalizeDirname(dirname($path));
}
/**
* Map result arrays.
*
* @param array $object
* @param array $map
*
* @return array mapped result
*/
public static function map(array $object, array $map)
{
$result = [];
foreach ($map as $from => $to) {
if ( ! isset($object[$from])) {
continue;
}
$result[$to] = $object[$from];
}
return $result;
}
/**
* Normalize path.
*
* @param string $path
*
* @throws LogicException
*
* @return string
*/
public static function normalizePath($path)
{
return static::normalizeRelativePath($path);
}
/**
* Normalize relative directories in a path.
*
* @param string $path
*
* @throws LogicException
*
* @return string
*/
public static function normalizeRelativePath($path)
{
$path = str_replace('\\', '/', $path);
$path = static::removeFunkyWhiteSpace($path);
$parts = [];
foreach (explode('/', $path) as $part) {
switch ($part) {
case '':
case '.':
break;
case '..':
if (empty($parts)) {
throw new LogicException(
'Path is outside of the defined root, path: [' . $path . ']'
);
}
array_pop($parts);
break;
default:
$parts[] = $part;
break;
}
}
return implode('/', $parts);
}
/**
* Removes unprintable characters and invalid unicode characters.
*
* @param string $path
*
* @return string $path
*/
protected static function removeFunkyWhiteSpace($path)
{
// We do this check in a loop, since removing invalid unicode characters
// can lead to new characters being created.
while (preg_match('#\p{C}+|^\./#u', $path)) {
$path = preg_replace('#\p{C}+|^\./#u', '', $path);
}
return $path;
}
/**
* Normalize prefix.
*
* @param string $prefix
* @param string $separator
*
* @return string normalized path
*/
public static function normalizePrefix($prefix, $separator)
{
return rtrim($prefix, $separator) . $separator;
}
/**
* Get content size.
*
* @param string $contents
*
* @return int content size
*/
public static function contentSize($contents)
{
return defined('MB_OVERLOAD_STRING') ? mb_strlen($contents, '8bit') : strlen($contents);
}
/**
* Guess MIME Type based on the path of the file and it's content.
*
* @param string $path
* @param string|resource $content
*
* @return string|null MIME Type or NULL if no extension detected
*/
public static function guessMimeType($path, $content)
{
$mimeType = MimeType::detectByContent($content);
if ( ! (empty($mimeType) || in_array($mimeType, ['application/x-empty', 'text/plain', 'text/x-asm']))) {
return $mimeType;
}
return MimeType::detectByFilename($path);
}
/**
* Emulate directories.
*
* @param array $listing
*
* @return array listing with emulated directories
*/
public static function emulateDirectories(array $listing)
{
$directories = [];
$listedDirectories = [];
foreach ($listing as $object) {
list($directories, $listedDirectories) = static::emulateObjectDirectories($object, $directories, $listedDirectories);
}
$directories = array_diff(array_unique($directories), array_unique($listedDirectories));
foreach ($directories as $directory) {
$listing[] = static::pathinfo($directory) + ['type' => 'dir'];
}
return $listing;
}
/**
* Ensure a Config instance.
*
* @param null|array|Config $config
*
* @return Config config instance
*
* @throw LogicException
*/
public static function ensureConfig($config)
{
if ($config === null) {
return new Config();
}
if ($config instanceof Config) {
return $config;
}
if (is_array($config)) {
return new Config($config);
}
throw new LogicException('A config should either be an array or a Flysystem\Config object.');
}
/**
* Rewind a stream.
*
* @param resource $resource
*/
public static function rewindStream($resource)
{
if (ftell($resource) !== 0 && static::isSeekableStream($resource)) {
rewind($resource);
}
}
public static function isSeekableStream($resource)
{
$metadata = stream_get_meta_data($resource);
return $metadata['seekable'];
}
/**
* Get the size of a stream.
*
* @param resource $resource
*
* @return int|null stream size
*/
public static function getStreamSize($resource)
{
$stat = fstat($resource);
if ( ! is_array($stat) || ! isset($stat['size'])) {
return null;
}
return $stat['size'];
}
/**
* Emulate the directories of a single object.
*
* @param array $object
* @param array $directories
* @param array $listedDirectories
*
* @return array
*/
protected static function emulateObjectDirectories(array $object, array $directories, array $listedDirectories)
{
if ($object['type'] === 'dir') {
$listedDirectories[] = $object['path'];
}
if ( ! isset($object['dirname']) || trim($object['dirname']) === '') {
return [$directories, $listedDirectories];
}
$parent = $object['dirname'];
while (isset($parent) && trim($parent) !== '' && ! in_array($parent, $directories)) {
$directories[] = $parent;
$parent = static::dirname($parent);
}
if (isset($object['type']) && $object['type'] === 'dir') {
$listedDirectories[] = $object['path'];
return [$directories, $listedDirectories];
}
return [$directories, $listedDirectories];
}
/**
* Returns the trailing name component of the path.
*
* @param string $path
*
* @return string
*/
private static function basename($path)
{
$separators = DIRECTORY_SEPARATOR === '/' ? '/' : '\/';
$path = rtrim($path, $separators);
$basename = preg_replace('#.*?([^' . preg_quote($separators, '#') . ']+$)#', '$1', $path);
if (DIRECTORY_SEPARATOR === '/') {
return $basename;
}
// @codeCoverageIgnoreStart
// Extra Windows path munging. This is tested via AppVeyor, but code
// coverage is not reported.
// Handle relative paths with drive letters. c:file.txt.
while (preg_match('#^[a-zA-Z]{1}:[^\\\/]#', $basename)) {
$basename = substr($basename, 2);
}
// Remove colon for standalone drive letter names.
if (preg_match('#^[a-zA-Z]{1}:$#', $basename)) {
$basename = rtrim($basename, ':');
}
return $basename;
// @codeCoverageIgnoreEnd
}
}
PK %qPJT T src/File.phpnu W+A filesystem->has($this->path);
}
/**
* Read the file.
*
* @return string|false file contents
*/
public function read()
{
return $this->filesystem->read($this->path);
}
/**
* Read the file as a stream.
*
* @return resource|false file stream
*/
public function readStream()
{
return $this->filesystem->readStream($this->path);
}
/**
* Write the new file.
*
* @param string $content
*
* @return bool success boolean
*/
public function write($content)
{
return $this->filesystem->write($this->path, $content);
}
/**
* Write the new file using a stream.
*
* @param resource $resource
*
* @return bool success boolean
*/
public function writeStream($resource)
{
return $this->filesystem->writeStream($this->path, $resource);
}
/**
* Update the file contents.
*
* @param string $content
*
* @return bool success boolean
*/
public function update($content)
{
return $this->filesystem->update($this->path, $content);
}
/**
* Update the file contents with a stream.
*
* @param resource $resource
*
* @return bool success boolean
*/
public function updateStream($resource)
{
return $this->filesystem->updateStream($this->path, $resource);
}
/**
* Create the file or update if exists.
*
* @param string $content
*
* @return bool success boolean
*/
public function put($content)
{
return $this->filesystem->put($this->path, $content);
}
/**
* Create the file or update if exists using a stream.
*
* @param resource $resource
*
* @return bool success boolean
*/
public function putStream($resource)
{
return $this->filesystem->putStream($this->path, $resource);
}
/**
* Rename the file.
*
* @param string $newpath
*
* @return bool success boolean
*/
public function rename($newpath)
{
if ($this->filesystem->rename($this->path, $newpath)) {
$this->path = $newpath;
return true;
}
return false;
}
/**
* Copy the file.
*
* @param string $newpath
*
* @return File|false new file or false
*/
public function copy($newpath)
{
if ($this->filesystem->copy($this->path, $newpath)) {
return new File($this->filesystem, $newpath);
}
return false;
}
/**
* Get the file's timestamp.
*
* @return string|false The timestamp or false on failure.
*/
public function getTimestamp()
{
return $this->filesystem->getTimestamp($this->path);
}
/**
* Get the file's mimetype.
*
* @return string|false The file mime-type or false on failure.
*/
public function getMimetype()
{
return $this->filesystem->getMimetype($this->path);
}
/**
* Get the file's visibility.
*
* @return string|false The visibility (public|private) or false on failure.
*/
public function getVisibility()
{
return $this->filesystem->getVisibility($this->path);
}
/**
* Get the file's metadata.
*
* @return array|false The file metadata or false on failure.
*/
public function getMetadata()
{
return $this->filesystem->getMetadata($this->path);
}
/**
* Get the file size.
*
* @return int|false The file size or false on failure.
*/
public function getSize()
{
return $this->filesystem->getSize($this->path);
}
/**
* Delete the file.
*
* @return bool success boolean
*/
public function delete()
{
return $this->filesystem->delete($this->path);
}
}
PK %qP
src/Handler.phpnu W+A path = $path;
$this->filesystem = $filesystem;
}
/**
* Check whether the entree is a directory.
*
* @return bool
*/
public function isDir()
{
return $this->getType() === 'dir';
}
/**
* Check whether the entree is a file.
*
* @return bool
*/
public function isFile()
{
return $this->getType() === 'file';
}
/**
* Retrieve the entree type (file|dir).
*
* @return string file or dir
*/
public function getType()
{
$metadata = $this->filesystem->getMetadata($this->path);
return $metadata ? $metadata['type'] : 'dir';
}
/**
* Set the Filesystem object.
*
* @param FilesystemInterface $filesystem
*
* @return $this
*/
public function setFilesystem(FilesystemInterface $filesystem)
{
$this->filesystem = $filesystem;
return $this;
}
/**
* Retrieve the Filesystem object.
*
* @return FilesystemInterface
*/
public function getFilesystem()
{
return $this->filesystem;
}
/**
* Set the entree path.
*
* @param string $path
*
* @return $this
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Retrieve the entree path.
*
* @return string path
*/
public function getPath()
{
return $this->path;
}
/**
* Plugins pass-through.
*
* @param string $method
* @param array $arguments
*
* @return mixed
*/
public function __call($method, array $arguments)
{
array_unshift($arguments, $this->path);
$callback = [$this->filesystem, $method];
try {
return call_user_func_array($callback, $arguments);
} catch (BadMethodCallException $e) {
throw new BadMethodCallException(
'Call to undefined method '
. get_called_class()
. '::' . $method
);
}
}
}
PK %qPu: src/FileExistsException.phpnu W+A path = $path;
parent::__construct('File already exists at path: ' . $this->getPath(), $code, $previous);
}
/**
* Get the path which was found.
*
* @return string
*/
public function getPath()
{
return $this->path;
}
}
PK %qP~ ~ src/Adapter/SynologyFtp.phpnu W+A [
'public' => 0644,
'private' => 0600,
],
'dir' => [
'public' => 0755,
'private' => 0700,
],
];
/**
* @var string
*/
protected $pathSeparator = DIRECTORY_SEPARATOR;
/**
* @var array
*/
protected $permissionMap;
/**
* @var int
*/
protected $writeFlags;
/**
* @var int
*/
private $linkHandling;
/**
* Constructor.
*
* @param string $root
* @param int $writeFlags
* @param int $linkHandling
* @param array $permissions
*
* @throws LogicException
*/
public function __construct($root, $writeFlags = LOCK_EX, $linkHandling = self::DISALLOW_LINKS, array $permissions = [])
{
$root = is_link($root) ? realpath($root) : $root;
$this->permissionMap = array_replace_recursive(static::$permissions, $permissions);
$this->ensureDirectory($root);
if ( ! is_dir($root) || ! is_readable($root)) {
throw new LogicException('The root path ' . $root . ' is not readable.');
}
$this->setPathPrefix($root);
$this->writeFlags = $writeFlags;
$this->linkHandling = $linkHandling;
}
/**
* Ensure the root directory exists.
*
* @param string $root root directory path
*
* @return void
*
* @throws Exception in case the root directory can not be created
*/
protected function ensureDirectory($root)
{
if ( ! is_dir($root)) {
$umask = umask(0);
if ( ! @mkdir($root, $this->permissionMap['dir']['public'], true)) {
$mkdirError = error_get_last();
}
umask($umask);
clearstatcache(false, $root);
if ( ! is_dir($root)) {
$errorMessage = isset($mkdirError['message']) ? $mkdirError['message'] : '';
throw new Exception(sprintf('Impossible to create the root directory "%s". %s', $root, $errorMessage));
}
}
}
/**
* @inheritdoc
*/
public function has($path)
{
$location = $this->applyPathPrefix($path);
return file_exists($location);
}
/**
* @inheritdoc
*/
public function write($path, $contents, Config $config)
{
$location = $this->applyPathPrefix($path);
$this->ensureDirectory(dirname($location));
if (($size = file_put_contents($location, $contents, $this->writeFlags)) === false) {
return false;
}
$type = 'file';
$result = compact('contents', 'type', 'size', 'path');
if ($visibility = $config->get('visibility')) {
$result['visibility'] = $visibility;
$this->setVisibility($path, $visibility);
}
return $result;
}
/**
* @inheritdoc
*/
public function writeStream($path, $resource, Config $config)
{
$location = $this->applyPathPrefix($path);
$this->ensureDirectory(dirname($location));
$stream = fopen($location, 'w+b');
if ( ! $stream || stream_copy_to_stream($resource, $stream) === false || ! fclose($stream)) {
return false;
}
$type = 'file';
$result = compact('type', 'path');
if ($visibility = $config->get('visibility')) {
$this->setVisibility($path, $visibility);
$result['visibility'] = $visibility;
}
return $result;
}
/**
* @inheritdoc
*/
public function readStream($path)
{
$location = $this->applyPathPrefix($path);
$stream = fopen($location, 'rb');
return ['type' => 'file', 'path' => $path, 'stream' => $stream];
}
/**
* @inheritdoc
*/
public function updateStream($path, $resource, Config $config)
{
return $this->writeStream($path, $resource, $config);
}
/**
* @inheritdoc
*/
public function update($path, $contents, Config $config)
{
$location = $this->applyPathPrefix($path);
$size = file_put_contents($location, $contents, $this->writeFlags);
if ($size === false) {
return false;
}
$type = 'file';
$result = compact('type', 'path', 'size', 'contents');
if ($mimetype = $config->get('mimetype') ?: Util::guessMimeType($path, $contents)) {
$result['mimetype'] = $mimetype;
}
return $result;
}
/**
* @inheritdoc
*/
public function read($path)
{
$location = $this->applyPathPrefix($path);
$contents = @file_get_contents($location);
if ($contents === false) {
return false;
}
return ['type' => 'file', 'path' => $path, 'contents' => $contents];
}
/**
* @inheritdoc
*/
public function rename($path, $newpath)
{
$location = $this->applyPathPrefix($path);
$destination = $this->applyPathPrefix($newpath);
$parentDirectory = $this->applyPathPrefix(Util::dirname($newpath));
$this->ensureDirectory($parentDirectory);
return rename($location, $destination);
}
/**
* @inheritdoc
*/
public function copy($path, $newpath)
{
$location = $this->applyPathPrefix($path);
$destination = $this->applyPathPrefix($newpath);
$this->ensureDirectory(dirname($destination));
return copy($location, $destination);
}
/**
* @inheritdoc
*/
public function delete($path)
{
$location = $this->applyPathPrefix($path);
return @unlink($location);
}
/**
* @inheritdoc
*/
public function listContents($directory = '', $recursive = false)
{
$result = [];
$location = $this->applyPathPrefix($directory);
if ( ! is_dir($location)) {
return [];
}
$iterator = $recursive ? $this->getRecursiveDirectoryIterator($location) : $this->getDirectoryIterator($location);
foreach ($iterator as $file) {
$path = $this->getFilePath($file);
if (preg_match('#(^|/|\\\\)\.{1,2}$#', $path)) {
continue;
}
$result[] = $this->normalizeFileInfo($file);
}
unset($iterator);
return array_filter($result);
}
/**
* @inheritdoc
*/
public function getMetadata($path)
{
$location = $this->applyPathPrefix($path);
clearstatcache(false, $location);
$info = new SplFileInfo($location);
return $this->normalizeFileInfo($info);
}
/**
* @inheritdoc
*/
public function getSize($path)
{
return $this->getMetadata($path);
}
/**
* @inheritdoc
*/
public function getMimetype($path)
{
$location = $this->applyPathPrefix($path);
$finfo = new Finfo(FILEINFO_MIME_TYPE);
$mimetype = $finfo->file($location);
if (in_array($mimetype, ['application/octet-stream', 'inode/x-empty', 'application/x-empty'])) {
$mimetype = Util\MimeType::detectByFilename($location);
}
return ['path' => $path, 'type' => 'file', 'mimetype' => $mimetype];
}
/**
* @inheritdoc
*/
public function getTimestamp($path)
{
return $this->getMetadata($path);
}
/**
* @inheritdoc
*/
public function getVisibility($path)
{
$location = $this->applyPathPrefix($path);
clearstatcache(false, $location);
$permissions = octdec(substr(sprintf('%o', fileperms($location)), -4));
$type = is_dir($location) ? 'dir' : 'file';
foreach ($this->permissionMap[$type] as $visibility => $visibilityPermissions) {
if ($visibilityPermissions == $permissions) {
return compact('path', 'visibility');
}
}
$visibility = substr(sprintf('%o', fileperms($location)), -4);
return compact('path', 'visibility');
}
/**
* @inheritdoc
*/
public function setVisibility($path, $visibility)
{
$location = $this->applyPathPrefix($path);
$type = is_dir($location) ? 'dir' : 'file';
$success = chmod($location, $this->permissionMap[$type][$visibility]);
if ($success === false) {
return false;
}
return compact('path', 'visibility');
}
/**
* @inheritdoc
*/
public function createDir($dirname, Config $config)
{
$location = $this->applyPathPrefix($dirname);
$umask = umask(0);
$visibility = $config->get('visibility', 'public');
$return = ['path' => $dirname, 'type' => 'dir'];
if ( ! is_dir($location)) {
if (false === @mkdir($location, $this->permissionMap['dir'][$visibility], true)
|| false === is_dir($location)) {
$return = false;
}
}
umask($umask);
return $return;
}
/**
* @inheritdoc
*/
public function deleteDir($dirname)
{
$location = $this->applyPathPrefix($dirname);
if ( ! is_dir($location)) {
return false;
}
$contents = $this->getRecursiveDirectoryIterator($location, RecursiveIteratorIterator::CHILD_FIRST);
/** @var SplFileInfo $file */
foreach ($contents as $file) {
$this->guardAgainstUnreadableFileInfo($file);
$this->deleteFileInfoObject($file);
}
unset($contents);
return rmdir($location);
}
/**
* @param SplFileInfo $file
*/
protected function deleteFileInfoObject(SplFileInfo $file)
{
switch ($file->getType()) {
case 'dir':
rmdir($file->getRealPath());
break;
case 'link':
unlink($file->getPathname());
break;
default:
unlink($file->getRealPath());
}
}
/**
* Normalize the file info.
*
* @param SplFileInfo $file
*
* @return array|void
*
* @throws NotSupportedException
*/
protected function normalizeFileInfo(SplFileInfo $file)
{
if ( ! $file->isLink()) {
return $this->mapFileInfo($file);
}
if ($this->linkHandling & self::DISALLOW_LINKS) {
throw NotSupportedException::forLink($file);
}
}
/**
* Get the normalized path from a SplFileInfo object.
*
* @param SplFileInfo $file
*
* @return string
*/
protected function getFilePath(SplFileInfo $file)
{
$location = $file->getPathname();
$path = $this->removePathPrefix($location);
return trim(str_replace('\\', '/', $path), '/');
}
/**
* @param string $path
* @param int $mode
*
* @return RecursiveIteratorIterator
*/
protected function getRecursiveDirectoryIterator($path, $mode = RecursiveIteratorIterator::SELF_FIRST)
{
return new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
$mode
);
}
/**
* @param string $path
*
* @return DirectoryIterator
*/
protected function getDirectoryIterator($path)
{
$iterator = new DirectoryIterator($path);
return $iterator;
}
/**
* @param SplFileInfo $file
*
* @return array
*/
protected function mapFileInfo(SplFileInfo $file)
{
$normalized = [
'type' => $file->getType(),
'path' => $this->getFilePath($file),
];
$normalized['timestamp'] = $file->getMTime();
if ($normalized['type'] === 'file') {
$normalized['size'] = $file->getSize();
}
return $normalized;
}
/**
* @param SplFileInfo $file
*
* @throws UnreadableFileException
*/
protected function guardAgainstUnreadableFileInfo(SplFileInfo $file)
{
if ( ! $file->isReadable()) {
throw UnreadableFileException::forFileInfo($file);
}
}
}
PK %qP][; ; " src/Adapter/AbstractFtpAdapter.phpnu W+A safeStorage = new SafeStorage();
$this->setConfig($config);
}
/**
* Set the config.
*
* @param array $config
*
* @return $this
*/
public function setConfig(array $config)
{
foreach ($this->configurable as $setting) {
if ( ! isset($config[$setting])) {
continue;
}
$method = 'set' . ucfirst($setting);
if (method_exists($this, $method)) {
$this->$method($config[$setting]);
}
}
return $this;
}
/**
* Returns the host.
*
* @return string
*/
public function getHost()
{
return $this->host;
}
/**
* Set the host.
*
* @param string $host
*
* @return $this
*/
public function setHost($host)
{
$this->host = $host;
return $this;
}
/**
* Set the public permission value.
*
* @param int $permPublic
*
* @return $this
*/
public function setPermPublic($permPublic)
{
$this->permPublic = $permPublic;
return $this;
}
/**
* Set the private permission value.
*
* @param int $permPrivate
*
* @return $this
*/
public function setPermPrivate($permPrivate)
{
$this->permPrivate = $permPrivate;
return $this;
}
/**
* Returns the ftp port.
*
* @return int
*/
public function getPort()
{
return $this->port;
}
/**
* Returns the root folder to work from.
*
* @return string
*/
public function getRoot()
{
return $this->root;
}
/**
* Set the ftp port.
*
* @param int|string $port
*
* @return $this
*/
public function setPort($port)
{
$this->port = (int) $port;
return $this;
}
/**
* Set the root folder to work from.
*
* @param string $root
*
* @return $this
*/
public function setRoot($root)
{
$this->root = rtrim($root, '\\/') . $this->separator;
return $this;
}
/**
* Returns the ftp username.
*
* @return string username
*/
public function getUsername()
{
$username = $this->safeStorage->retrieveSafely('username');
return $username !== null ? $username : 'anonymous';
}
/**
* Set ftp username.
*
* @param string $username
*
* @return $this
*/
public function setUsername($username)
{
$this->safeStorage->storeSafely('username', $username);
return $this;
}
/**
* Returns the password.
*
* @return string password
*/
public function getPassword()
{
return $this->safeStorage->retrieveSafely('password');
}
/**
* Set the ftp password.
*
* @param string $password
*
* @return $this
*/
public function setPassword($password)
{
$this->safeStorage->storeSafely('password', $password);
return $this;
}
/**
* Returns the amount of seconds before the connection will timeout.
*
* @return int
*/
public function getTimeout()
{
return $this->timeout;
}
/**
* Set the amount of seconds before the connection should timeout.
*
* @param int $timeout
*
* @return $this
*/
public function setTimeout($timeout)
{
$this->timeout = (int) $timeout;
return $this;
}
/**
* Return the FTP system type.
*
* @return string
*/
public function getSystemType()
{
return $this->systemType;
}
/**
* Set the FTP system type (windows or unix).
*
* @param string $systemType
*
* @return $this
*/
public function setSystemType($systemType)
{
$this->systemType = strtolower($systemType);
return $this;
}
/**
* True to enable timestamps for FTP servers that return unix-style listings.
*
* @param bool $bool
*
* @return $this
*/
public function setEnableTimestampsOnUnixListings($bool = false)
{
$this->enableTimestampsOnUnixListings = $bool;
return $this;
}
/**
* @inheritdoc
*/
public function listContents($directory = '', $recursive = false)
{
return $this->listDirectoryContents($directory, $recursive);
}
abstract protected function listDirectoryContents($directory, $recursive = false);
/**
* Normalize a directory listing.
*
* @param array $listing
* @param string $prefix
*
* @return array directory listing
*/
protected function normalizeListing(array $listing, $prefix = '')
{
$base = $prefix;
$result = [];
$listing = $this->removeDotDirectories($listing);
while ($item = array_shift($listing)) {
if (preg_match('#^.*:$#', $item)) {
$base = preg_replace('~^\./*|:$~', '', $item);
continue;
}
$result[] = $this->normalizeObject($item, $base);
}
return $this->sortListing($result);
}
/**
* Sort a directory listing.
*
* @param array $result
*
* @return array sorted listing
*/
protected function sortListing(array $result)
{
$compare = function ($one, $two) {
return strnatcmp($one['path'], $two['path']);
};
usort($result, $compare);
return $result;
}
/**
* Normalize a file entry.
*
* @param string $item
* @param string $base
*
* @return array normalized file array
*
* @throws NotSupportedException
*/
protected function normalizeObject($item, $base)
{
$systemType = $this->systemType ?: $this->detectSystemType($item);
if ($systemType === 'unix') {
return $this->normalizeUnixObject($item, $base);
} elseif ($systemType === 'windows') {
return $this->normalizeWindowsObject($item, $base);
}
throw NotSupportedException::forFtpSystemType($systemType);
}
/**
* Normalize a Unix file entry.
*
* Given $item contains:
* '-rw-r--r-- 1 ftp ftp 409 Aug 19 09:01 file1.txt'
*
* This function will return:
* [
* 'type' => 'file',
* 'path' => 'file1.txt',
* 'visibility' => 'public',
* 'size' => 409,
* 'timestamp' => 1566205260
* ]
*
* @param string $item
* @param string $base
*
* @return array normalized file array
*/
protected function normalizeUnixObject($item, $base)
{
$item = preg_replace('#\s+#', ' ', trim($item), 7);
if (count(explode(' ', $item, 9)) !== 9) {
throw new RuntimeException("Metadata can't be parsed from item '$item' , not enough parts.");
}
list($permissions, /* $number */, /* $owner */, /* $group */, $size, $month, $day, $timeOrYear, $name) = explode(' ', $item, 9);
$type = $this->detectType($permissions);
$path = $base === '' ? $name : $base . $this->separator . $name;
if ($type === 'dir') {
return compact('type', 'path');
}
$permissions = $this->normalizePermissions($permissions);
$visibility = $permissions & 0044 ? AdapterInterface::VISIBILITY_PUBLIC : AdapterInterface::VISIBILITY_PRIVATE;
$size = (int) $size;
$result = compact('type', 'path', 'visibility', 'size');
if ($this->enableTimestampsOnUnixListings) {
$timestamp = $this->normalizeUnixTimestamp($month, $day, $timeOrYear);
$result += compact('timestamp');
}
return $result;
}
/**
* Only accurate to the minute (current year), or to the day.
*
* Inadequacies in timestamp accuracy are due to limitations of the FTP 'LIST' command
*
* Note: The 'MLSD' command is a machine-readable replacement for 'LIST'
* but many FTP servers do not support it :(
*
* @param string $month e.g. 'Aug'
* @param string $day e.g. '19'
* @param string $timeOrYear e.g. '09:01' OR '2015'
*
* @return int
*/
protected function normalizeUnixTimestamp($month, $day, $timeOrYear)
{
if (is_numeric($timeOrYear)) {
$year = $timeOrYear;
$hour = '00';
$minute = '00';
$seconds = '00';
} else {
$year = date('Y');
list($hour, $minute) = explode(':', $timeOrYear);
$seconds = '00';
}
$dateTime = DateTime::createFromFormat('Y-M-j-G:i:s', "{$year}-{$month}-{$day}-{$hour}:{$minute}:{$seconds}");
return $dateTime->getTimestamp();
}
/**
* Normalize a Windows/DOS file entry.
*
* @param string $item
* @param string $base
*
* @return array normalized file array
*/
protected function normalizeWindowsObject($item, $base)
{
$item = preg_replace('#\s+#', ' ', trim($item), 3);
if (count(explode(' ', $item, 4)) !== 4) {
throw new RuntimeException("Metadata can't be parsed from item '$item' , not enough parts.");
}
list($date, $time, $size, $name) = explode(' ', $item, 4);
$path = $base === '' ? $name : $base . $this->separator . $name;
// Check for the correct date/time format
$format = strlen($date) === 8 ? 'm-d-yH:iA' : 'Y-m-dH:i';
$dt = DateTime::createFromFormat($format, $date . $time);
$timestamp = $dt ? $dt->getTimestamp() : (int) strtotime("$date $time");
if ($size === '
') {
$type = 'dir';
return compact('type', 'path', 'timestamp');
}
$type = 'file';
$visibility = AdapterInterface::VISIBILITY_PUBLIC;
$size = (int) $size;
return compact('type', 'path', 'visibility', 'size', 'timestamp');
}
/**
* Get the system type from a listing item.
*
* @param string $item
*
* @return string the system type
*/
protected function detectSystemType($item)
{
return preg_match('/^[0-9]{2,4}-[0-9]{2}-[0-9]{2}/', $item) ? 'windows' : 'unix';
}
/**
* Get the file type from the permissions.
*
* @param string $permissions
*
* @return string file type
*/
protected function detectType($permissions)
{
return substr($permissions, 0, 1) === 'd' ? 'dir' : 'file';
}
/**
* Normalize a permissions string.
*
* @param string $permissions
*
* @return int
*/
protected function normalizePermissions($permissions)
{
if (is_numeric($permissions)) {
return ((int) $permissions) & 0777;
}
// remove the type identifier
$permissions = substr($permissions, 1);
// map the string rights to the numeric counterparts
$map = ['-' => '0', 'r' => '4', 'w' => '2', 'x' => '1'];
$permissions = strtr($permissions, $map);
// split up the permission groups
$parts = str_split($permissions, 3);
// convert the groups
$mapper = function ($part) {
return array_sum(str_split($part));
};
// converts to decimal number
return octdec(implode('', array_map($mapper, $parts)));
}
/**
* Filter out dot-directories.
*
* @param array $list
*
* @return array
*/
public function removeDotDirectories(array $list)
{
$filter = function ($line) {
return $line !== '' && ! preg_match('#.* \.(\.)?$|^total#', $line);
};
return array_filter($list, $filter);
}
/**
* @inheritdoc
*/
public function has($path)
{
return $this->getMetadata($path);
}
/**
* @inheritdoc
*/
public function getSize($path)
{
return $this->getMetadata($path);
}
/**
* @inheritdoc
*/
public function getVisibility($path)
{
return $this->getMetadata($path);
}
/**
* Ensure a directory exists.
*
* @param string $dirname
*/
public function ensureDirectory($dirname)
{
$dirname = (string) $dirname;
if ($dirname !== '' && ! $this->has($dirname)) {
$this->createDir($dirname, new Config());
}
}
/**
* @return mixed
*/
public function getConnection()
{
$tries = 0;
while ( ! $this->isConnected() && $tries < 3) {
$tries++;
$this->disconnect();
$this->connect();
}
return $this->connection;
}
/**
* Get the public permission value.
*
* @return int
*/
public function getPermPublic()
{
return $this->permPublic;
}
/**
* Get the private permission value.
*
* @return int
*/
public function getPermPrivate()
{
return $this->permPrivate;
}
/**
* Disconnect on destruction.
*/
public function __destruct()
{
$this->disconnect();
}
/**
* Establish a connection.
*/
abstract public function connect();
/**
* Close the connection.
*/
abstract public function disconnect();
/**
* Check if a connection is active.
*
* @return bool
*/
abstract public function isConnected();
}
PK %qPlu5 5 src/Adapter/Ftp.phpnu W+A transferMode = $mode;
return $this;
}
/**
* Set if Ssl is enabled.
*
* @param bool $ssl
*
* @return $this
*/
public function setSsl($ssl)
{
$this->ssl = (bool) $ssl;
return $this;
}
/**
* Set if passive mode should be used.
*
* @param bool $passive
*/
public function setPassive($passive = true)
{
$this->passive = $passive;
}
/**
* @param bool $ignorePassiveAddress
*/
public function setIgnorePassiveAddress($ignorePassiveAddress)
{
$this->ignorePassiveAddress = $ignorePassiveAddress;
}
/**
* @param bool $recurseManually
*/
public function setRecurseManually($recurseManually)
{
$this->recurseManually = $recurseManually;
}
/**
* @param bool $utf8
*/
public function setUtf8($utf8)
{
$this->utf8 = (bool) $utf8;
}
/**
* Connect to the FTP server.
*/
public function connect()
{
if ($this->ssl) {
$this->connection = @ftp_ssl_connect($this->getHost(), $this->getPort(), $this->getTimeout());
} else {
$this->connection = @ftp_connect($this->getHost(), $this->getPort(), $this->getTimeout());
}
if ( ! $this->connection) {
throw new ConnectionRuntimeException('Could not connect to host: ' . $this->getHost() . ', port:' . $this->getPort());
}
$this->login();
$this->setUtf8Mode();
$this->setConnectionPassiveMode();
$this->setConnectionRoot();
$this->isPureFtpd = $this->isPureFtpdServer();
}
/**
* Set the connection to UTF-8 mode.
*/
protected function setUtf8Mode()
{
if ($this->utf8) {
$response = ftp_raw($this->connection, "OPTS UTF8 ON");
if (substr($response[0], 0, 3) !== '200') {
throw new ConnectionRuntimeException(
'Could not set UTF-8 mode for connection: ' . $this->getHost() . '::' . $this->getPort()
);
}
}
}
/**
* Set the connections to passive mode.
*
* @throws ConnectionRuntimeException
*/
protected function setConnectionPassiveMode()
{
if (is_bool($this->ignorePassiveAddress) && defined('FTP_USEPASVADDRESS')) {
ftp_set_option($this->connection, FTP_USEPASVADDRESS, ! $this->ignorePassiveAddress);
}
if ( ! ftp_pasv($this->connection, $this->passive)) {
throw new ConnectionRuntimeException(
'Could not set passive mode for connection: ' . $this->getHost() . '::' . $this->getPort()
);
}
}
/**
* Set the connection root.
*/
protected function setConnectionRoot()
{
$root = $this->getRoot();
$connection = $this->connection;
if ($root && ! ftp_chdir($connection, $root)) {
throw new InvalidRootException('Root is invalid or does not exist: ' . $this->getRoot());
}
// Store absolute path for further reference.
// This is needed when creating directories and
// initial root was a relative path, else the root
// would be relative to the chdir'd path.
$this->root = ftp_pwd($connection);
}
/**
* Login.
*
* @throws ConnectionRuntimeException
*/
protected function login()
{
set_error_handler(function () {
});
$isLoggedIn = ftp_login(
$this->connection,
$this->getUsername(),
$this->getPassword()
);
restore_error_handler();
if ( ! $isLoggedIn) {
$this->disconnect();
throw new ConnectionRuntimeException(
'Could not login with connection: ' . $this->getHost() . '::' . $this->getPort(
) . ', username: ' . $this->getUsername()
);
}
}
/**
* Disconnect from the FTP server.
*/
public function disconnect()
{
if (is_resource($this->connection)) {
@ftp_close($this->connection);
}
$this->connection = null;
}
/**
* @inheritdoc
*/
public function write($path, $contents, Config $config)
{
$stream = fopen('php://temp', 'w+b');
fwrite($stream, $contents);
rewind($stream);
$result = $this->writeStream($path, $stream, $config);
fclose($stream);
if ($result === false) {
return false;
}
$result['contents'] = $contents;
$result['mimetype'] = $config->get('mimetype') ?: Util::guessMimeType($path, $contents);
return $result;
}
/**
* @inheritdoc
*/
public function writeStream($path, $resource, Config $config)
{
$this->ensureDirectory(Util::dirname($path));
if ( ! ftp_fput($this->getConnection(), $path, $resource, $this->transferMode)) {
return false;
}
if ($visibility = $config->get('visibility')) {
$this->setVisibility($path, $visibility);
}
$type = 'file';
return compact('type', 'path', 'visibility');
}
/**
* @inheritdoc
*/
public function update($path, $contents, Config $config)
{
return $this->write($path, $contents, $config);
}
/**
* @inheritdoc
*/
public function updateStream($path, $resource, Config $config)
{
return $this->writeStream($path, $resource, $config);
}
/**
* @inheritdoc
*/
public function rename($path, $newpath)
{
return ftp_rename($this->getConnection(), $path, $newpath);
}
/**
* @inheritdoc
*/
public function delete($path)
{
return ftp_delete($this->getConnection(), $path);
}
/**
* @inheritdoc
*/
public function deleteDir($dirname)
{
$connection = $this->getConnection();
$contents = array_reverse($this->listDirectoryContents($dirname, false));
foreach ($contents as $object) {
if ($object['type'] === 'file') {
if ( ! ftp_delete($connection, $object['path'])) {
return false;
}
} elseif ( ! $this->deleteDir($object['path'])) {
return false;
}
}
return ftp_rmdir($connection, $dirname);
}
/**
* @inheritdoc
*/
public function createDir($dirname, Config $config)
{
$connection = $this->getConnection();
$directories = explode('/', $dirname);
foreach ($directories as $directory) {
if (false === $this->createActualDirectory($directory, $connection)) {
$this->setConnectionRoot();
return false;
}
ftp_chdir($connection, $directory);
}
$this->setConnectionRoot();
return ['type' => 'dir', 'path' => $dirname];
}
/**
* Create a directory.
*
* @param string $directory
* @param resource $connection
*
* @return bool
*/
protected function createActualDirectory($directory, $connection)
{
// List the current directory
$listing = ftp_nlist($connection, '.') ?: [];
foreach ($listing as $key => $item) {
if (preg_match('~^\./.*~', $item)) {
$listing[$key] = substr($item, 2);
}
}
if (in_array($directory, $listing, true)) {
return true;
}
return (boolean) ftp_mkdir($connection, $directory);
}
/**
* @inheritdoc
*/
public function getMetadata($path)
{
if ($path === '') {
return ['type' => 'dir', 'path' => ''];
}
if (@ftp_chdir($this->getConnection(), $path) === true) {
$this->setConnectionRoot();
return ['type' => 'dir', 'path' => $path];
}
$listing = $this->ftpRawlist('-A', str_replace('*', '\\*', $path));
if (empty($listing) || in_array('total 0', $listing, true)) {
return false;
}
if (preg_match('/.* not found/', $listing[0])) {
return false;
}
if (preg_match('/^total [0-9]*$/', $listing[0])) {
array_shift($listing);
}
return $this->normalizeObject($listing[0], '');
}
/**
* @inheritdoc
*/
public function getMimetype($path)
{
if ( ! $metadata = $this->getMetadata($path)) {
return false;
}
$metadata['mimetype'] = MimeType::detectByFilename($path);
return $metadata;
}
/**
* @inheritdoc
*/
public function getTimestamp($path)
{
$timestamp = ftp_mdtm($this->getConnection(), $path);
return ($timestamp !== -1) ? ['path' => $path, 'timestamp' => $timestamp] : false;
}
/**
* @inheritdoc
*/
public function read($path)
{
if ( ! $object = $this->readStream($path)) {
return false;
}
$object['contents'] = stream_get_contents($object['stream']);
fclose($object['stream']);
unset($object['stream']);
return $object;
}
/**
* @inheritdoc
*/
public function readStream($path)
{
$stream = fopen('php://temp', 'w+b');
$result = ftp_fget($this->getConnection(), $stream, $path, $this->transferMode);
rewind($stream);
if ( ! $result) {
fclose($stream);
return false;
}
return ['type' => 'file', 'path' => $path, 'stream' => $stream];
}
/**
* @inheritdoc
*/
public function setVisibility($path, $visibility)
{
$mode = $visibility === AdapterInterface::VISIBILITY_PUBLIC ? $this->getPermPublic() : $this->getPermPrivate();
if ( ! ftp_chmod($this->getConnection(), $mode, $path)) {
return false;
}
return compact('path', 'visibility');
}
/**
* @inheritdoc
*
* @param string $directory
*/
protected function listDirectoryContents($directory, $recursive = true)
{
$directory = str_replace('*', '\\*', $directory);
if ($recursive && $this->recurseManually) {
return $this->listDirectoryContentsRecursive($directory);
}
$options = $recursive ? '-alnR' : '-aln';
$listing = $this->ftpRawlist($options, $directory);
return $listing ? $this->normalizeListing($listing, $directory) : [];
}
/**
* @inheritdoc
*
* @param string $directory
*/
protected function listDirectoryContentsRecursive($directory)
{
$listing = $this->normalizeListing($this->ftpRawlist('-aln', $directory) ?: [], $directory);
$output = [];
foreach ($listing as $item) {
$output[] = $item;
if ($item['type'] !== 'dir') {
continue;
}
$output = array_merge($output, $this->listDirectoryContentsRecursive($item['path']));
}
return $output;
}
/**
* Check if the connection is open.
*
* @return bool
*
* @throws ConnectionErrorException
*/
public function isConnected()
{
return is_resource($this->connection)
&& $this->getRawExecResponseCode('NOOP') === 200;
}
/**
* @return bool
*/
protected function isPureFtpdServer()
{
$response = ftp_raw($this->connection, 'HELP');
return stripos(implode(' ', $response), 'Pure-FTPd') !== false;
}
/**
* The ftp_rawlist function with optional escaping.
*
* @param string $options
* @param string $path
*
* @return array
*/
protected function ftpRawlist($options, $path)
{
$connection = $this->getConnection();
if ($this->isPureFtpd) {
$path = str_replace(' ', '\ ', $path);
}
return ftp_rawlist($connection, $options . ' ' . $path);
}
private function getRawExecResponseCode($command)
{
$response = @ftp_raw($this->connection, trim($command));
return (int) preg_replace('/\D/', '', implode(' ', $response));
}
}
PK %qPtU! ! - src/Adapter/Polyfill/StreamedWritingTrait.phpnu W+A stream($path, $resource, $config, 'write');
}
/**
* Update a file using a stream.
*
* @param string $path
* @param resource $resource
* @param Config $config Config object or visibility setting
*
* @return mixed false of file metadata
*/
public function updateStream($path, $resource, Config $config)
{
return $this->stream($path, $resource, $config, 'update');
}
// Required abstract methods
abstract public function write($pash, $contents, Config $config);
abstract public function update($pash, $contents, Config $config);
}
PK %qP*% % * src/Adapter/Polyfill/StreamedCopyTrait.phpnu W+A readStream($path);
if ($response === false || ! is_resource($response['stream'])) {
return false;
}
$result = $this->writeStream($newpath, $response['stream'], new Config());
if ($result !== false && is_resource($response['stream'])) {
fclose($response['stream']);
}
return $result !== false;
}
// Required abstract method
/**
* @param string $path
*
* @return resource
*/
abstract public function readStream($path);
/**
* @param string $path
* @param resource $resource
* @param Config $config
*
* @return resource
*/
abstract public function writeStream($path, $resource, Config $config);
}
PK %qP{E} } - src/Adapter/Polyfill/StreamedReadingTrait.phpnu W+A read($path)) {
return false;
}
$stream = fopen('php://temp', 'w+b');
fwrite($stream, $data['contents']);
rewind($stream);
$data['stream'] = $stream;
unset($data['contents']);
return $data;
}
/**
* Reads a file.
*
* @param string $path
*
* @return array|false
*
* @see League\Flysystem\ReadInterface::read()
*/
abstract public function read($path);
}
PK %qP\I 5 src/Adapter/Polyfill/NotSupportingVisibilityTrait.phpnu W+A 'dir', 'path' => ''];
}
if (@ftp_chdir($this->getConnection(), $path) === true) {
$this->setConnectionRoot();
return ['type' => 'dir', 'path' => $path];
}
if ( ! ($object = ftp_raw($this->getConnection(), 'STAT ' . $path)) || count($object) < 3) {
return false;
}
if (substr($object[1], 0, 5) === "ftpd:") {
return false;
}
return $this->normalizeObject($object[1], '');
}
/**
* @inheritdoc
*/
protected function listDirectoryContents($directory, $recursive = true)
{
$listing = ftp_rawlist($this->getConnection(), $directory, $recursive);
if ($listing === false || ( ! empty($listing) && substr($listing[0], 0, 5) === "ftpd:")) {
return [];
}
return $this->normalizeListing($listing, $directory);
}
}
PK %qPB src/Adapter/NullAdapter.phpnu W+A get('visibility')) {
$result['visibility'] = $visibility;
}
return $result;
}
/**
* @inheritdoc
*/
public function update($path, $contents, Config $config)
{
return false;
}
/**
* @inheritdoc
*/
public function read($path)
{
return false;
}
/**
* @inheritdoc
*/
public function rename($path, $newpath)
{
return false;
}
/**
* @inheritdoc
*/
public function delete($path)
{
return false;
}
/**
* @inheritdoc
*/
public function listContents($directory = '', $recursive = false)
{
return [];
}
/**
* @inheritdoc
*/
public function getMetadata($path)
{
return false;
}
/**
* @inheritdoc
*/
public function getSize($path)
{
return false;
}
/**
* @inheritdoc
*/
public function getMimetype($path)
{
return false;
}
/**
* @inheritdoc
*/
public function getTimestamp($path)
{
return false;
}
/**
* @inheritdoc
*/
public function getVisibility($path)
{
return false;
}
/**
* @inheritdoc
*/
public function setVisibility($path, $visibility)
{
return compact('visibility');
}
/**
* @inheritdoc
*/
public function createDir($dirname, Config $config)
{
return ['path' => $dirname, 'type' => 'dir'];
}
/**
* @inheritdoc
*/
public function deleteDir($dirname)
{
return false;
}
}
PK %qPFIO O src/Adapter/AbstractAdapter.phpnu W+A pathPrefix = null;
return;
}
$this->pathPrefix = rtrim($prefix, '\\/') . $this->pathSeparator;
}
/**
* Get the path prefix.
*
* @return string|null path prefix or null if pathPrefix is empty
*/
public function getPathPrefix()
{
return $this->pathPrefix;
}
/**
* Prefix a path.
*
* @param string $path
*
* @return string prefixed path
*/
public function applyPathPrefix($path)
{
return $this->getPathPrefix() . ltrim($path, '\\/');
}
/**
* Remove a path prefix.
*
* @param string $path
*
* @return string path without the prefix
*/
public function removePathPrefix($path)
{
return substr($path, strlen($this->getPathPrefix()));
}
}
PK %qPSF* * src/Directory.phpnu W+A filesystem->deleteDir($this->path);
}
/**
* List the directory contents.
*
* @param bool $recursive
*
* @return array|bool directory contents or false
*/
public function getContents($recursive = false)
{
return $this->filesystem->listContents($this->path, $recursive);
}
}
PK %qPMI
$ src/Util/ContentListingFormatter.phpnu W+A directory = rtrim($directory, '/');
$this->recursive = $recursive;
$this->caseSensitive = $caseSensitive;
}
/**
* Format contents listing.
*
* @param array $listing
*
* @return array
*/
public function formatListing(array $listing)
{
$listing = array_filter(array_map([$this, 'addPathInfo'], $listing), [$this, 'isEntryOutOfScope']);
return $this->sortListing(array_values($listing));
}
private function addPathInfo(array $entry)
{
return $entry + Util::pathinfo($entry['path']);
}
/**
* Determine if the entry is out of scope.
*
* @param array $entry
*
* @return bool
*/
private function isEntryOutOfScope(array $entry)
{
if (empty($entry['path']) && $entry['path'] !== '0') {
return false;
}
if ($this->recursive) {
return $this->residesInDirectory($entry);
}
return $this->isDirectChild($entry);
}
/**
* Check if the entry resides within the parent directory.
*
* @param array $entry
*
* @return bool
*/
private function residesInDirectory(array $entry)
{
if ($this->directory === '') {
return true;
}
return $this->caseSensitive
? strpos($entry['path'], $this->directory . '/') === 0
: stripos($entry['path'], $this->directory . '/') === 0;
}
/**
* Check if the entry is a direct child of the directory.
*
* @param array $entry
*
* @return bool
*/
private function isDirectChild(array $entry)
{
return $this->caseSensitive
? $entry['dirname'] === $this->directory
: strcasecmp($this->directory, $entry['dirname']) === 0;
}
/**
* @param array $listing
*
* @return array
*/
private function sortListing(array $listing)
{
usort($listing, function ($a, $b) {
return strcasecmp($a['path'], $b['path']);
});
return $listing;
}
}
PK %qPGm]% ]% src/Util/MimeType.phpnu W+A 'application/mac-binhex40',
'cpt' => 'application/mac-compactpro',
'csv' => 'text/csv',
'bin' => 'application/octet-stream',
'dms' => 'application/octet-stream',
'lha' => 'application/octet-stream',
'lzh' => 'application/octet-stream',
'exe' => 'application/octet-stream',
'class' => 'application/octet-stream',
'psd' => 'application/x-photoshop',
'so' => 'application/octet-stream',
'sea' => 'application/octet-stream',
'dll' => 'application/octet-stream',
'oda' => 'application/oda',
'pdf' => 'application/pdf',
'ai' => 'application/pdf',
'eps' => 'application/postscript',
'epub' => 'application/epub+zip',
'ps' => 'application/postscript',
'smi' => 'application/smil',
'smil' => 'application/smil',
'mif' => 'application/vnd.mif',
'xls' => 'application/vnd.ms-excel',
'xlt' => 'application/vnd.ms-excel',
'xla' => 'application/vnd.ms-excel',
'ppt' => 'application/powerpoint',
'pot' => 'application/vnd.ms-powerpoint',
'pps' => 'application/vnd.ms-powerpoint',
'ppa' => 'application/vnd.ms-powerpoint',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'potm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
'wbxml' => 'application/wbxml',
'wmlc' => 'application/wmlc',
'dcr' => 'application/x-director',
'dir' => 'application/x-director',
'dxr' => 'application/x-director',
'dvi' => 'application/x-dvi',
'gtar' => 'application/x-gtar',
'gz' => 'application/x-gzip',
'gzip' => 'application/x-gzip',
'php' => 'application/x-httpd-php',
'php4' => 'application/x-httpd-php',
'php3' => 'application/x-httpd-php',
'phtml' => 'application/x-httpd-php',
'phps' => 'application/x-httpd-php-source',
'js' => 'application/javascript',
'swf' => 'application/x-shockwave-flash',
'sit' => 'application/x-stuffit',
'tar' => 'application/x-tar',
'tgz' => 'application/x-tar',
'z' => 'application/x-compress',
'xhtml' => 'application/xhtml+xml',
'xht' => 'application/xhtml+xml',
'rdf' => 'application/rdf+xml',
'zip' => 'application/x-zip',
'rar' => 'application/x-rar',
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mpga' => 'audio/mpeg',
'mp2' => 'audio/mpeg',
'mp3' => 'audio/mpeg',
'aif' => 'audio/x-aiff',
'aiff' => 'audio/x-aiff',
'aifc' => 'audio/x-aiff',
'ram' => 'audio/x-pn-realaudio',
'rm' => 'audio/x-pn-realaudio',
'rpm' => 'audio/x-pn-realaudio-plugin',
'ra' => 'audio/x-realaudio',
'rv' => 'video/vnd.rn-realvideo',
'wav' => 'audio/x-wav',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpe' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'svg' => 'image/svg+xml',
'css' => 'text/css',
'html' => 'text/html',
'htm' => 'text/html',
'shtml' => 'text/html',
'txt' => 'text/plain',
'text' => 'text/plain',
'log' => 'text/plain',
'rtx' => 'text/richtext',
'rtf' => 'text/rtf',
'xml' => 'application/xml',
'xsl' => 'application/xml',
'dmn' => 'application/octet-stream',
'bpmn' => 'application/octet-stream',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpe' => 'video/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',
'avi' => 'video/x-msvideo',
'movie' => 'video/x-sgi-movie',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'docm' => 'application/vnd.ms-word.template.macroEnabled.12',
'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
'dot' => 'application/msword',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'word' => 'application/msword',
'xl' => 'application/excel',
'eml' => 'message/rfc822',
'json' => 'application/json',
'pem' => 'application/x-x509-user-cert',
'p10' => 'application/x-pkcs10',
'p12' => 'application/x-pkcs12',
'p7a' => 'application/x-pkcs7-signature',
'p7c' => 'application/pkcs7-mime',
'p7m' => 'application/pkcs7-mime',
'p7r' => 'application/x-pkcs7-certreqresp',
'p7s' => 'application/pkcs7-signature',
'crt' => 'application/x-x509-ca-cert',
'crl' => 'application/pkix-crl',
'der' => 'application/x-x509-ca-cert',
'kdb' => 'application/octet-stream',
'pgp' => 'application/pgp',
'gpg' => 'application/gpg-keys',
'sst' => 'application/octet-stream',
'csr' => 'application/octet-stream',
'rsa' => 'application/x-pkcs7',
'cer' => 'application/pkix-cert',
'3g2' => 'video/3gpp2',
'3gp' => 'video/3gp',
'mp4' => 'video/mp4',
'm4a' => 'audio/x-m4a',
'f4v' => 'video/mp4',
'webm' => 'video/webm',
'aac' => 'audio/x-acc',
'm4u' => 'application/vnd.mpegurl',
'm3u' => 'text/plain',
'xspf' => 'application/xspf+xml',
'vlc' => 'application/videolan',
'wmv' => 'video/x-ms-wmv',
'au' => 'audio/x-au',
'ac3' => 'audio/ac3',
'flac' => 'audio/x-flac',
'ogg' => 'audio/ogg',
'kmz' => 'application/vnd.google-earth.kmz',
'kml' => 'application/vnd.google-earth.kml+xml',
'ics' => 'text/calendar',
'zsh' => 'text/x-scriptzsh',
'7zip' => 'application/x-7z-compressed',
'cdr' => 'application/cdr',
'wma' => 'audio/x-ms-wma',
'jar' => 'application/java-archive',
'tex' => 'application/x-tex',
'latex' => 'application/x-latex',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'odg' => 'application/vnd.oasis.opendocument.graphics',
'odc' => 'application/vnd.oasis.opendocument.chart',
'odf' => 'application/vnd.oasis.opendocument.formula',
'odi' => 'application/vnd.oasis.opendocument.image',
'odm' => 'application/vnd.oasis.opendocument.text-master',
'odb' => 'application/vnd.oasis.opendocument.database',
'ott' => 'application/vnd.oasis.opendocument.text-template',
'webp' => 'image/webp',
];
/**
* Detects MIME Type based on given content.
*
* @param mixed $content
*
* @return string|null MIME Type or NULL if no mime type detected
*/
public static function detectByContent($content)
{
if ( ! class_exists('finfo') || ! is_string($content)) {
return null;
}
try {
$finfo = new finfo(FILEINFO_MIME_TYPE);
return $finfo->buffer($content) ?: null;
// @codeCoverageIgnoreStart
} catch (ErrorException $e) {
// This is caused by an array to string conversion error.
}
} // @codeCoverageIgnoreEnd
/**
* Detects MIME Type based on file extension.
*
* @param string $extension
*
* @return string|null MIME Type or NULL if no extension detected
*/
public static function detectByFileExtension($extension)
{
return isset(static::$extensionToMimeTypeMap[$extension])
? static::$extensionToMimeTypeMap[$extension]
: 'text/plain';
}
/**
* @param string $filename
*
* @return string|null MIME Type or NULL if no extension detected
*/
public static function detectByFilename($filename)
{
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
return empty($extension) ? 'text/plain' : static::detectByFileExtension($extension);
}
/**
* @return array Map of file extension to MIME Type
*/
public static function getExtensionToMimeTypeMap()
{
return static::$extensionToMimeTypeMap;
}
}
PK %qPQ Q src/Util/StreamHasher.phpnu W+A algo = $algo;
}
/**
* @param resource $resource
*
* @return string
*/
public function hash($resource)
{
rewind($resource);
$context = hash_init($this->algo);
hash_update_stream($context, $resource);
fclose($resource);
return hash_final($context);
}
}
PK %qP^_S S src/ConfigAwareTrait.phpnu W+A config = $config ? Util::ensureConfig($config) : new Config;
}
/**
* Get the Config.
*
* @return Config config object
*/
public function getConfig()
{
return $this->config;
}
/**
* Convert a config array to a Config object with the correct fallback.
*
* @param array $config
*
* @return Config
*/
protected function prepareConfig(array $config)
{
$config = new Config($config);
$config->setFallback($this->getConfig());
return $config;
}
}
PK %qPB5* src/ConnectionErrorException.phpnu W+A filesystem->delete($newpath);
} catch (FileNotFoundException $e) {
// The destination path does not exist. That's ok.
$deleted = true;
}
if ($deleted) {
return $this->filesystem->copy($path, $newpath);
}
return false;
}
}
PK %qP\J src/Plugin/ListPaths.phpnu W+A filesystem->listContents($directory, $recursive);
foreach ($contents as $object) {
$result[] = $object['path'];
}
return $result;
}
}
PK %qPh src/Plugin/GetWithMetadata.phpnu W+A filesystem->getMetadata($path);
if ( ! $object) {
return false;
}
$keys = array_diff($metadata, array_keys($object));
foreach ($keys as $key) {
if ( ! method_exists($this->filesystem, $method = 'get' . ucfirst($key))) {
throw new InvalidArgumentException('Could not fetch metadata: ' . $key);
}
$object[$key] = $this->filesystem->{$method}($path);
}
return $object;
}
}
PK %qP$ src/Plugin/AbstractPlugin.phpnu W+A filesystem = $filesystem;
}
}
PK %qPV" " src/Plugin/ForcedRename.phpnu W+A filesystem->delete($newpath);
} catch (FileNotFoundException $e) {
// The destination path does not exist. That's ok.
$deleted = true;
}
if ($deleted) {
return $this->filesystem->rename($path, $newpath);
}
return false;
}
}
PK %qPN src/Plugin/PluggableTrait.phpnu W+A plugins[$plugin->getMethod()] = $plugin;
return $this;
}
/**
* Find a specific plugin.
*
* @param string $method
*
* @throws PluginNotFoundException
*
* @return PluginInterface
*/
protected function findPlugin($method)
{
if ( ! isset($this->plugins[$method])) {
throw new PluginNotFoundException('Plugin not found for method: ' . $method);
}
return $this->plugins[$method];
}
/**
* Invoke a plugin by method name.
*
* @param string $method
* @param array $arguments
* @param FilesystemInterface $filesystem
*
* @throws PluginNotFoundException
*
* @return mixed
*/
protected function invokePlugin($method, array $arguments, FilesystemInterface $filesystem)
{
$plugin = $this->findPlugin($method);
$plugin->setFilesystem($filesystem);
$callback = [$plugin, 'handle'];
return call_user_func_array($callback, $arguments);
}
/**
* Plugins pass-through.
*
* @param string $method
* @param array $arguments
*
* @throws BadMethodCallException
*
* @return mixed
*/
public function __call($method, array $arguments)
{
try {
return $this->invokePlugin($method, $arguments, $this);
} catch (PluginNotFoundException $e) {
throw new BadMethodCallException(
'Call to undefined method '
. get_class($this)
. '::' . $method
);
}
}
}
PK %qP
&