diff options
Diffstat (limited to 'apps/files_external/3rdparty/icewind/streams/src')
12 files changed, 673 insertions, 41 deletions
diff --git a/apps/files_external/3rdparty/icewind/streams/src/CallbackWrapper.php b/apps/files_external/3rdparty/icewind/streams/src/CallbackWrapper.php index fd99aa6ebe8..c5847b95fdb 100644 --- a/apps/files_external/3rdparty/icewind/streams/src/CallbackWrapper.php +++ b/apps/files_external/3rdparty/icewind/streams/src/CallbackWrapper.php @@ -13,10 +13,11 @@ namespace Icewind\Streams; * The following options should be passed in the context when opening the stream * [ * 'callback' => [ - * 'source' => resource - * 'read' => function($count){} (optional) - * 'write' => function($data){} (optional) - * 'close' => function(){} (optional) + * 'source' => resource + * 'read' => function($count){} (optional) + * 'write' => function($data){} (optional) + * 'close' => function(){} (optional) + * 'readdir' => function(){} (optional) * ] * ] * @@ -39,54 +40,56 @@ class CallbackWrapper extends Wrapper { protected $closeCallback; /** + * @var callable + */ + protected $readDirCallBack; + + /** * Wraps a stream with the provided callbacks * * @param resource $source * @param callable $read (optional) * @param callable $write (optional) * @param callable $close (optional) + * @param callable $readDir (optional) * @return resource * * @throws \BadMethodCallException */ - public static function wrap($source, $read = null, $write = null, $close = null) { + public static function wrap($source, $read = null, $write = null, $close = null, $readDir = null) { $context = stream_context_create(array( 'callback' => array( 'source' => $source, 'read' => $read, 'write' => $write, - 'close' => $close + 'close' => $close, + 'readDir' => $readDir ) )); - stream_wrapper_register('callback', '\Icewind\Streams\CallbackWrapper'); - try { - $wrapped = fopen('callback://', 'r+', false, $context); - } catch (\BadMethodCallException $e) { - stream_wrapper_unregister('callback'); - throw $e; - } - stream_wrapper_unregister('callback'); - return $wrapped; + return Wrapper::wrapSource($source, $context, 'callback', '\Icewind\Streams\CallbackWrapper'); } - public function stream_open($path, $mode, $options, &$opened_path) { + protected function open() { $context = $this->loadContext('callback'); - if (isset($context['read']) and is_callable($context['read'])) { - $this->readCallback = $context['read']; - } - if (isset($context['write']) and is_callable($context['write'])) { - $this->writeCallback = $context['write']; - } - if (isset($context['close']) and is_callable($context['close'])) { - $this->closeCallback = $context['close']; - } + $this->readCallback = $context['read']; + $this->writeCallback = $context['write']; + $this->closeCallback = $context['close']; + $this->readDirCallBack = $context['readDir']; return true; } + public function dir_opendir($path, $options) { + return $this->open(); + } + + public function stream_open($path, $mode, $options, &$opened_path) { + return $this->open(); + } + public function stream_read($count) { $result = parent::stream_read($count); - if ($this->readCallback) { + if (is_callable($this->readCallback)) { call_user_func($this->readCallback, $count); } return $result; @@ -94,7 +97,7 @@ class CallbackWrapper extends Wrapper { public function stream_write($data) { $result = parent::stream_write($data); - if ($this->writeCallback) { + if (is_callable($this->writeCallback)) { call_user_func($this->writeCallback, $data); } return $result; @@ -102,9 +105,17 @@ class CallbackWrapper extends Wrapper { public function stream_close() { $result = parent::stream_close(); - if ($this->closeCallback) { + if (is_callable($this->closeCallback)) { call_user_func($this->closeCallback); } return $result; } + + public function dir_readdir() { + $result = parent::dir_readdir(); + if (is_callable($this->readDirCallBack)) { + call_user_func($this->readDirCallBack); + } + return $result; + } } diff --git a/apps/files_external/3rdparty/icewind/streams/src/DirectoryFilter.php b/apps/files_external/3rdparty/icewind/streams/src/DirectoryFilter.php new file mode 100644 index 00000000000..4b869699000 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/DirectoryFilter.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright (c) 2015 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Wrapper allows filtering of directories + * + * The filter callback will be called for each entry in the folder + * when the callback return false the entry will be filtered out + */ +class DirectoryFilter extends DirectoryWrapper { + /** + * @var callable + */ + private $filter; + + /** + * @param string $path + * @param array $options + * @return bool + */ + public function dir_opendir($path, $options) { + $context = $this->loadContext('filter'); + $this->filter = $context['filter']; + return true; + } + + /** + * @return string + */ + public function dir_readdir() { + $file = readdir($this->source); + $filter = $this->filter; + // keep reading untill we have an accepted entry or we're at the end of the folder + while ($file !== false && $filter($file) === false) { + $file = readdir($this->source); + } + return $file; + } + + /** + * @param resource $source + * @param callable $filter + * @return resource + */ + public static function wrap($source, callable $filter) { + $options = array( + 'filter' => array( + 'source' => $source, + 'filter' => $filter + ) + ); + return self::wrapWithOptions($options, '\Icewind\Streams\DirectoryFilter'); + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/DirectoryWrapper.php b/apps/files_external/3rdparty/icewind/streams/src/DirectoryWrapper.php new file mode 100644 index 00000000000..63e4805a807 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/DirectoryWrapper.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright (c) 2015 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +class DirectoryWrapper implements Directory { + /** + * @var resource + */ + public $context; + + /** + * @var resource + */ + protected $source; + + /** + * Load the source from the stream context and return the context options + * + * @param string $name + * @return array + * @throws \Exception + */ + protected function loadContext($name) { + $context = stream_context_get_options($this->context); + if (isset($context[$name])) { + $context = $context[$name]; + } else { + throw new \BadMethodCallException('Invalid context, "' . $name . '" options not set'); + } + if (isset($context['source']) and is_resource($context['source'])) { + $this->source = $context['source']; + } else { + throw new \BadMethodCallException('Invalid context, source not set'); + } + return $context; + } + + /** + * @param string $path + * @param array $options + * @return bool + */ + public function dir_opendir($path, $options) { + $this->loadContext('dir'); + return true; + } + + /** + * @return string + */ + public function dir_readdir() { + return readdir($this->source); + } + + /** + * @return bool + */ + public function dir_closedir() { + closedir($this->source); + return true; + } + + /** + * @return bool + */ + public function dir_rewinddir() { + rewinddir($this->source); + return true; + } + + /** + * @param array $options the options for the context to wrap the stream with + * @param string $class + * @return resource + */ + protected static function wrapWithOptions($options, $class) { + $context = stream_context_create($options); + stream_wrapper_register('dirwrapper', $class); + $wrapped = opendir('dirwrapper://', $context); + stream_wrapper_unregister('dirwrapper'); + return $wrapped; + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/File.php b/apps/files_external/3rdparty/icewind/streams/src/File.php index 6202ef4a4b4..252b7b8971f 100644 --- a/apps/files_external/3rdparty/icewind/streams/src/File.php +++ b/apps/files_external/3rdparty/icewind/streams/src/File.php @@ -21,7 +21,7 @@ interface File { public function stream_open($path, $mode, $options, &$opened_path); /** - * @param string $offset + * @param int $offset * @param int $whence * @return bool */ diff --git a/apps/files_external/3rdparty/icewind/streams/src/IteratorDirectory.php b/apps/files_external/3rdparty/icewind/streams/src/IteratorDirectory.php index c4eac5d4ed3..6dfa42a8b68 100644 --- a/apps/files_external/3rdparty/icewind/streams/src/IteratorDirectory.php +++ b/apps/files_external/3rdparty/icewind/streams/src/IteratorDirectory.php @@ -45,9 +45,9 @@ class IteratorDirectory implements Directory { } else { throw new \BadMethodCallException('Invalid context, "' . $name . '" options not set'); } - if (isset($context['iterator']) and $context['iterator'] instanceof \Iterator) { + if (isset($context['iterator'])) { $this->iterator = $context['iterator']; - } else if (isset($context['array']) and is_array($context['array'])) { + } else if (isset($context['array'])) { $this->iterator = new \ArrayIterator($context['array']); } else { throw new \BadMethodCallException('Invalid context, iterator or array not set'); diff --git a/apps/files_external/3rdparty/icewind/streams/src/NullWrapper.php b/apps/files_external/3rdparty/icewind/streams/src/NullWrapper.php index 8cbaaa756d3..b6c71d98fc4 100644 --- a/apps/files_external/3rdparty/icewind/streams/src/NullWrapper.php +++ b/apps/files_external/3rdparty/icewind/streams/src/NullWrapper.php @@ -24,19 +24,16 @@ class NullWrapper extends Wrapper { 'null' => array( 'source' => $source) )); - stream_wrapper_register('null', '\Icewind\Streams\NullWrapper'); - try { - $wrapped = fopen('null://', 'r+', false, $context); - } catch (\BadMethodCallException $e) { - stream_wrapper_unregister('null'); - throw $e; - } - stream_wrapper_unregister('null'); - return $wrapped; + return Wrapper::wrapSource($source, $context, 'null', '\Icewind\Streams\NullWrapper'); } public function stream_open($path, $mode, $options, &$opened_path) { $this->loadContext('null'); return true; } + + public function dir_opendir($path, $options) { + $this->loadContext('null'); + return true; + } } diff --git a/apps/files_external/3rdparty/icewind/streams/src/Path.php b/apps/files_external/3rdparty/icewind/streams/src/Path.php new file mode 100644 index 00000000000..46d2156b69a --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/Path.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * A string-like object that automatically registers a stream wrapper when used and removes the stream wrapper when no longer used + * + * Can optionally pass context options to the stream wrapper + */ +class Path { + + /** + * @var bool + */ + protected $registered = false; + + /** + * @var string + */ + protected $protocol; + + /** + * @var string + */ + protected $class; + + /** + * @var array + */ + protected $contextOptions; + + /** + * @param string $class + * @param array $contextOptions + */ + public function __construct($class, $contextOptions = array()) { + $this->class = $class; + $this->contextOptions = $contextOptions; + } + + public function getProtocol() { + if (!$this->protocol) { + $this->protocol = 'auto' . uniqid(); + } + return $this->protocol; + } + + public function wrapPath($path) { + return $this->getProtocol() . '://' . $path; + } + + protected function register() { + if (!$this->registered) { + $this->appendDefaultContent($this->getProtocol(), $this->contextOptions); + stream_wrapper_register($this->getProtocol(), $this->class); + $this->registered = true; + } + } + + protected function unregister() { + stream_wrapper_unregister($this->getProtocol()); + $this->unsetDefaultContent($this->getProtocol()); + $this->registered = false; + } + + /** + * Add values to the default stream context + * + * @param string $key + * @param array $values + */ + protected function appendDefaultContent($key, $values) { + $context = stream_context_get_default(); + $defaults = stream_context_get_options($context); + $defaults[$key] = $values; + stream_context_set_default($defaults); + } + + /** + * Remove values from the default stream context + * + * @param string $key + */ + protected function unsetDefaultContent($key) { + $context = stream_context_get_default(); + $defaults = stream_context_get_options($context); + unset($defaults[$key]); + stream_context_set_default($defaults); + } + + public function __toString() { + $this->register(); + return $this->protocol . '://'; + } + + public function __destruct() { + $this->unregister(); + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/RetryWrapper.php b/apps/files_external/3rdparty/icewind/streams/src/RetryWrapper.php new file mode 100644 index 00000000000..84b43f6bd02 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/RetryWrapper.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright (c) 2016 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Wrapper that retries reads/writes to remote streams that dont deliver/recieve all requested data at once + */ +class RetryWrapper extends Wrapper { + + /** + * Wraps a stream with the provided callbacks + * + * @param resource $source + * @return resource + */ + public static function wrap($source) { + $context = stream_context_create(array( + 'retry' => array( + 'source' => $source + ) + )); + return Wrapper::wrapSource($source, $context, 'retry', '\Icewind\Streams\RetryWrapper'); + } + + protected function open() { + $this->loadContext('retry'); + return true; + } + + public function dir_opendir($path, $options) { + return false; + } + + public function stream_open($path, $mode, $options, &$opened_path) { + return $this->open(); + } + + public function stream_read($count) { + $result = parent::stream_read($count); + + $bytesReceived = strlen($result); + while ($bytesReceived < $count && !$this->stream_eof()) { + $result .= parent::stream_read($count - $bytesReceived); + $bytesReceived = strlen($result); + } + + return $result; + } + + public function stream_write($data) { + $bytesToSend = strlen($data); + $result = parent::stream_write($data); + + while ($result < $bytesToSend && !$this->stream_eof()) { + $dataLeft = substr($data, $result); + $result += parent::stream_write($dataLeft); + } + + return $result; + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/SeekableWrapper.php b/apps/files_external/3rdparty/icewind/streams/src/SeekableWrapper.php new file mode 100644 index 00000000000..d41fd73ec9c --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/SeekableWrapper.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Wrapper that provides callbacks for write, read and close + * + * The following options should be passed in the context when opening the stream + * [ + * 'callback' => [ + * 'source' => resource + * ] + * ] + * + * All callbacks are called after the operation is executed on the source stream + */ +class SeekableWrapper extends Wrapper { + /** + * @var resource + */ + protected $cache; + + /** + * Wraps a stream to make it seekable + * + * @param resource $source + * @return resource + * + * @throws \BadMethodCallException + */ + public static function wrap($source) { + $context = stream_context_create(array( + 'callback' => array( + 'source' => $source + ) + )); + return Wrapper::wrapSource($source, $context, 'callback', '\Icewind\Streams\SeekableWrapper'); + } + + public function dir_opendir($path, $options) { + return false; + } + + public function stream_open($path, $mode, $options, &$opened_path) { + $this->loadContext('callback'); + $this->cache = fopen('php://temp', 'w+'); + return true; + } + + protected function readTill($position) { + $current = ftell($this->source); + if ($position > $current) { + $data = parent::stream_read($position - $current); + $cachePosition = ftell($this->cache); + fseek($this->cache, $current); + fwrite($this->cache, $data); + fseek($this->cache, $cachePosition); + } + } + + public function stream_read($count) { + $current = ftell($this->cache); + $this->readTill($current + $count); + return fread($this->cache, $count); + } + + public function stream_seek($offset, $whence = SEEK_SET) { + if ($whence === SEEK_SET) { + $target = $offset; + } else if ($whence === SEEK_CUR) { + $current = ftell($this->cache); + $target = $current + $offset; + } else { + return false; + } + $this->readTill($target); + return fseek($this->cache, $target) === 0; + } + + public function stream_tell() { + return ftell($this->cache); + } + + public function stream_eof() { + return parent::stream_eof() and (ftell($this->source) === ftell($this->cache)); + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/Url.php b/apps/files_external/3rdparty/icewind/streams/src/Url.php new file mode 100644 index 00000000000..d6822608a33 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/Url.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Interface for stream wrappers that implement url functions such as unlink, stat + */ +interface Url { + /** + * @param string $path + * @param array $options + * @return bool + */ + public function dir_opendir($path, $options); + + /** + * @param string $path + * @param string $mode + * @param int $options + * @param string &$opened_path + * @return bool + */ + public function stream_open($path, $mode, $options, &$opened_path); + + /** + * @param string $path + * @param int $mode + * @param int $options + * @return bool + */ + public function mkdir($path, $mode, $options); + + /** + * @param string $source + * @param string $target + * @return bool + */ + public function rename($source, $target); + + /** + * @param string $path + * @param int $options + * @return bool + */ + public function rmdir($path, $options); + + /** + * @param string + * @return bool + */ + public function unlink($path); + + /** + * @param string $path + * @param int $flags + * @return array + */ + public function url_stat($path, $flags); +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/UrlCallBack.php b/apps/files_external/3rdparty/icewind/streams/src/UrlCallBack.php new file mode 100644 index 00000000000..580bfc6ba22 --- /dev/null +++ b/apps/files_external/3rdparty/icewind/streams/src/UrlCallBack.php @@ -0,0 +1,121 @@ +<?php +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Licensed under the MIT license: + * http://opensource.org/licenses/MIT + */ + +namespace Icewind\Streams; + +/** + * Wrapper that provides callbacks for url actions such as fopen, unlink, rename + * + * Usage: + * + * $path = UrlCallBack('/path/so/source', function(){ + * echo 'fopen'; + * }, function(){ + * echo 'opendir'; + * }, function(){ + * echo 'mkdir'; + * }, function(){ + * echo 'rename'; + * }, function(){ + * echo 'rmdir'; + * }, function(){ + * echo 'unlink'; + * }, function(){ + * echo 'stat'; + * }); + * + * mkdir($path); + * ... + * + * All callbacks are called after the operation is executed on the source stream + */ +class UrlCallback extends Wrapper implements Url { + + /** + * @param string $source + * @param callable $fopen + * @param callable $opendir + * @param callable $mkdir + * @param callable $rename + * @param callable $rmdir + * @param callable $unlink + * @param callable $stat + * @return \Icewind\Streams\Path + * + * @throws \BadMethodCallException + * @throws \Exception + */ + public static function wrap($source, $fopen = null, $opendir = null, $mkdir = null, $rename = null, $rmdir = null, + $unlink = null, $stat = null) { + $options = array( + 'source' => $source, + 'fopen' => $fopen, + 'opendir' => $opendir, + 'mkdir' => $mkdir, + 'rename' => $rename, + 'rmdir' => $rmdir, + 'unlink' => $unlink, + 'stat' => $stat + ); + return new Path('\Icewind\Streams\UrlCallBack', $options); + } + + protected function loadContext($url) { + list($protocol) = explode('://', $url); + $options = stream_context_get_options($this->context); + return $options[$protocol]; + } + + protected function callCallBack($context, $callback) { + if (is_callable($context[$callback])) { + call_user_func($context[$callback]); + } + } + + public function stream_open($path, $mode, $options, &$opened_path) { + $context = $this->loadContext($path); + $this->callCallBack($context, 'fopen'); + $this->setSourceStream(fopen($context['source'], $mode)); + return true; + } + + public function dir_opendir($path, $options) { + $context = $this->loadContext($path); + $this->callCallBack($context, 'opendir'); + $this->setSourceStream(opendir($context['source'])); + return true; + } + + public function mkdir($path, $mode, $options) { + $context = $this->loadContext($path); + $this->callCallBack($context, 'mkdir'); + return mkdir($context['source'], $mode, $options & STREAM_MKDIR_RECURSIVE); + } + + public function rmdir($path, $options) { + $context = $this->loadContext($path); + $this->callCallBack($context, 'rmdir'); + return rmdir($context['source']); + } + + public function rename($source, $target) { + $context = $this->loadContext($source); + $this->callCallBack($context, 'rename'); + list(, $target) = explode('://', $target); + return rename($context['source'], $target); + } + + public function unlink($path) { + $context = $this->loadContext($path); + $this->callCallBack($context, 'unlink'); + return unlink($context['source']); + } + + public function url_stat($path, $flags) { + throw new \Exception('stat is not supported due to php bug 50526'); + } +} diff --git a/apps/files_external/3rdparty/icewind/streams/src/Wrapper.php b/apps/files_external/3rdparty/icewind/streams/src/Wrapper.php index 2e3a6e6cd88..53de2942ca9 100644 --- a/apps/files_external/3rdparty/icewind/streams/src/Wrapper.php +++ b/apps/files_external/3rdparty/icewind/streams/src/Wrapper.php @@ -12,7 +12,7 @@ namespace Icewind\Streams; * * This wrapper itself doesn't implement any functionality but is just a base class for other wrappers to extend */ -abstract class Wrapper implements File { +abstract class Wrapper implements File, Directory { /** * @var resource */ @@ -25,6 +25,22 @@ abstract class Wrapper implements File { */ protected $source; + protected static function wrapSource($source, $context, $protocol, $class) { + try { + stream_wrapper_register($protocol, $class); + if (@rewinddir($source) === false) { + $wrapped = fopen($protocol . '://', 'r+', false, $context); + } else { + $wrapped = opendir($protocol . '://', $context); + } + } catch (\BadMethodCallException $e) { + stream_wrapper_unregister($protocol); + throw $e; + } + stream_wrapper_unregister($protocol); + return $wrapped; + } + /** * Load the source from the stream context and return the context options * @@ -107,4 +123,17 @@ abstract class Wrapper implements File { public function stream_close() { return fclose($this->source); } + + public function dir_readdir() { + return readdir($this->source); + } + + public function dir_closedir() { + closedir($this->source); + return true; + } + + public function dir_rewinddir() { + return rewind($this->source); + } } |