From 8ffb14b3051edc065ff06a4b3b5deb9dce1ed386 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Mon, 7 Aug 2017 11:58:58 +0200
Subject: [PATCH 01/18] [ticket/15305] Add streams

PHPBB3-15305
---
 phpBB/language/en/common.php                  |  2 ++
 .../storage/adapter/adapter_interface.php     | 22 ++++++++++++
 phpBB/phpbb/storage/adapter/local.php         | 35 +++++++++++++++++++
 phpBB/phpbb/storage/storage.php               | 28 +++++++++++++++
 4 files changed, 87 insertions(+)

diff --git a/phpBB/language/en/common.php b/phpBB/language/en/common.php
index 006582772a..19a9a720cb 100644
--- a/phpBB/language/en/common.php
+++ b/phpBB/language/en/common.php
@@ -736,6 +736,8 @@ $lang = array_merge($lang, array(
 	'STORAGE_CANNOT_RENAME'			=> 'Can not rename file or folder.',
 	'STORAGE_CANNOT_COPY'			=> 'Can not copy file or folder.',
 	'STORAGE_CANNOT_CREATE_DIR'		=> 'Can not create directory.',
+	'STORAGE_CANNOT_OPEN_FILE'		=> 'Can not open file.',
+	'STORAGE_CANNOT_CREATE_FILE'	=> 'Can not create file.',
 
 	'TB'				=> 'TB',
 	'TERMS_USE'			=> 'Terms of use',
diff --git a/phpBB/phpbb/storage/adapter/adapter_interface.php b/phpBB/phpbb/storage/adapter/adapter_interface.php
index 207f001fad..1a4fe5c046 100644
--- a/phpBB/phpbb/storage/adapter/adapter_interface.php
+++ b/phpBB/phpbb/storage/adapter/adapter_interface.php
@@ -84,4 +84,26 @@ interface adapter_interface
 	 * 												When the file cannot be copied
 	 */
 	public function copy($path_orig, $path_dest);
+
+	/**
+	 * Reads a file as a stream.
+	 *
+	 * @param string	$path	File to read
+	 *
+	 * @throws \phpbb\storage\exception\exception	When cannot open file
+	 *
+	 * @return resource	Returns a file pointer
+	 */
+	public function read_stream($path);
+
+	/**
+	 * Writes a new file using a stream.
+	 *
+	 * @param string	$path		The target file
+	 * @param resource	$resource	The resource
+	 *
+	 * @throws \phpbb\storage\exception\exception	When target file exists
+	 * 												When target file cannot be created
+	 */
+	public function write_stream($path, $resource);
 }
diff --git a/phpBB/phpbb/storage/adapter/local.php b/phpBB/phpbb/storage/adapter/local.php
index a8498d6d8c..4c822d3a5e 100644
--- a/phpBB/phpbb/storage/adapter/local.php
+++ b/phpBB/phpbb/storage/adapter/local.php
@@ -195,4 +195,39 @@ class local implements adapter_interface
 			$this->create_dir($path);
 		}
 	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function read_stream($path)
+	{
+		$stream = @fopen($path, 'rb');
+
+		if (!$stream)
+		{
+			throw new exception('STORAGE_CANNOT_OPEN_FILE', $path);
+		}
+
+		return $stream;
+	}
+
+	/**
+	 * {@inheritdoc}
+	 */
+	public function write_stream($path, $resource)
+	{
+		if ($this->exists($path))
+		{
+			throw new exception('STORAGE_FILE_EXISTS', $path);
+		}
+
+		$stream = @fopen($path, 'w+b');
+
+		if (!$stream)
+		{
+			throw new exception('STORAGE_CANNOT_CREATE_FILE', $path);
+		}
+
+		stream_copy_to_stream($resource, $stream);
+	}
 }
diff --git a/phpBB/phpbb/storage/storage.php b/phpBB/phpbb/storage/storage.php
index 65a4f6eb72..ff8f49c2e4 100644
--- a/phpBB/phpbb/storage/storage.php
+++ b/phpBB/phpbb/storage/storage.php
@@ -140,4 +140,32 @@ class storage
 	{
 		$this->get_adapter()->copy($path_orig, $path_dest);
 	}
+
+	/**
+	 * Reads a file as a stream.
+	 *
+	 * @param string	$path	File to read
+	 *
+	 * @throws \phpbb\storage\exception\exception	When cannot open file
+	 *
+	 * @return resource	Returns a file pointer
+	 */
+	public function read_stream($path)
+	{
+		$this->get_adapter()->read_stream($path);
+	}
+
+	/**
+	 * Writes a new file using a stream.
+	 *
+	 * @param string	$path		The target file
+	 * @param resource	$resource	The resource
+	 *
+	 * @throws \phpbb\storage\exception\exception	When target file exists
+	 * 												When target file cannot be created
+	 */
+	public function write_stream($path, $resource)
+	{
+		$this->get_adapter()->write_stream($path, $resource);
+	}
 }

From dedd7648cd2f6b9811efcfea2059876646862cef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Mon, 7 Aug 2017 12:05:58 +0200
Subject: [PATCH 02/18] [ticket/15305] Update PHPDoc

PHPBB3-15305
---
 .../storage/adapter/adapter_interface.php     | 34 ++++++++++++-------
 phpBB/phpbb/storage/storage.php               | 34 ++++++++++++-------
 2 files changed, 44 insertions(+), 24 deletions(-)

diff --git a/phpBB/phpbb/storage/adapter/adapter_interface.php b/phpBB/phpbb/storage/adapter/adapter_interface.php
index 1a4fe5c046..8cf7bd3a88 100644
--- a/phpBB/phpbb/storage/adapter/adapter_interface.php
+++ b/phpBB/phpbb/storage/adapter/adapter_interface.php
@@ -28,8 +28,9 @@ interface adapter_interface
 	 * @param string	path		The file to be written to.
 	 * @param string	content		The data to write into the file.
 	 *
-	 * @throws \phpbb\storage\exception\exception	When the file already exists
-	 * 												When the file cannot be written
+	 * @throws \phpbb\storage\exception\exception		When the file already exists
+	 * 													When the file cannot be written
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function put_contents($path, $content);
 
@@ -38,8 +39,10 @@ interface adapter_interface
 	 *
 	 * @param string	$path	The file to read
 	 *
-	 * @throws \phpbb\storage\exception\exception	When the file dont exists
-	 * 												When cannot read file contents
+	 * @throws \phpbb\storage\exception\exception		When the file dont exists
+	 * 													When cannot read file contents
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
+	 *
 	 * @return string	Returns file contents
 	 *
 	 */
@@ -50,6 +53,8 @@ interface adapter_interface
 	 *
 	 * @param string	$path	file/directory to check
 	 *
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
+	 *
 	 * @return bool	Returns true if all files/directories exist, false otherwise
 	 */
 	public function exists($path);
@@ -59,7 +64,8 @@ interface adapter_interface
 	 *
 	 * @param string	$path	file/directory to remove
 	 *
-	 * @throws \phpbb\storage\exception\exception	When removal fails.
+	 * @throws \phpbb\storage\exception\exception		When removal fails.
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function delete($path);
 
@@ -69,8 +75,9 @@ interface adapter_interface
 	 * @param string	$path_orig	The original file/direcotry
 	 * @param string	$path_dest	The target file/directory
 	 *
-	 * @throws \phpbb\storage\exception\exception	When target exists
-	 * 												When file/directory cannot be renamed
+	 * @throws \phpbb\storage\exception\exception		When target exists
+	 * 													When file/directory cannot be renamed
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function rename($path_orig, $path_dest);
 
@@ -80,8 +87,9 @@ interface adapter_interface
 	 * @param string	$path_orig	The original filename
 	 * @param string	$path_dest	The target filename
 	 *
-	 * @throws \phpbb\storage\exception\exception	When target exists
-	 * 												When the file cannot be copied
+	 * @throws \phpbb\storage\exception\exception		When target exists
+	 * 													When the file cannot be copied
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function copy($path_orig, $path_dest);
 
@@ -90,7 +98,8 @@ interface adapter_interface
 	 *
 	 * @param string	$path	File to read
 	 *
-	 * @throws \phpbb\storage\exception\exception	When cannot open file
+	 * @throws \phpbb\storage\exception\exception		When cannot open file
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 *
 	 * @return resource	Returns a file pointer
 	 */
@@ -102,8 +111,9 @@ interface adapter_interface
 	 * @param string	$path		The target file
 	 * @param resource	$resource	The resource
 	 *
-	 * @throws \phpbb\storage\exception\exception	When target file exists
-	 * 												When target file cannot be created
+	 * @throws \phpbb\storage\exception\exception		When target file exists
+	 * 													When target file cannot be created
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function write_stream($path, $resource);
 }
diff --git a/phpBB/phpbb/storage/storage.php b/phpBB/phpbb/storage/storage.php
index ff8f49c2e4..c0c70fac44 100644
--- a/phpBB/phpbb/storage/storage.php
+++ b/phpBB/phpbb/storage/storage.php
@@ -66,8 +66,9 @@ class storage
 	 * @param string	path		The file to be written to.
 	 * @param string	content		The data to write into the file.
 	 *
-	 * @throws \phpbb\storage\exception\exception	When the file already exists
-	 * 												When the file cannot be written
+	 * @throws \phpbb\storage\exception\exception		When the file already exists
+	 * 													When the file cannot be written
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function put_contents($path, $content)
 	{
@@ -79,8 +80,10 @@ class storage
 	 *
 	 * @param string	$path	The file to read
 	 *
-	 * @throws \phpbb\storage\exception\exception	When the file dont exists
-	 * 												When cannot read file contents
+	 * @throws \phpbb\storage\exception\exception		When the file dont exists
+	 * 													When cannot read file contents
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
+	 *
 	 * @return string	Returns file contents
 	 *
 	 */
@@ -94,6 +97,8 @@ class storage
 	 *
 	 * @param string	$path	file/directory to check
 	 *
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
+	 *
 	 * @return bool	Returns true if all files/directories exist, false otherwise
 	 */
 	public function exists($path)
@@ -106,7 +111,8 @@ class storage
 	 *
 	 * @param string	$path	file/directory to remove
 	 *
-	 * @throws \phpbb\storage\exception\exception	When removal fails.
+	 * @throws \phpbb\storage\exception\exception		When removal fails.
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function delete($path)
 	{
@@ -119,8 +125,9 @@ class storage
 	 * @param string	$path_orig	The original file/direcotry
 	 * @param string	$path_dest	The target file/directory
 	 *
-	 * @throws \phpbb\storage\exception\exception	When target exists
-	 * 												When file/directory cannot be renamed
+	 * @throws \phpbb\storage\exception\exception		When target exists
+	 * 													When file/directory cannot be renamed
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function rename($path_orig, $path_dest)
 	{
@@ -133,8 +140,9 @@ class storage
 	 * @param string	$path_orig	The original filename
 	 * @param string	$path_dest	The target filename
 	 *
-	 * @throws \phpbb\storage\exception\exception	When target exists
-	 * 												When the file cannot be copied
+	 * @throws \phpbb\storage\exception\exception		When target exists
+	 * 													When the file cannot be copied
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function copy($path_orig, $path_dest)
 	{
@@ -146,7 +154,8 @@ class storage
 	 *
 	 * @param string	$path	File to read
 	 *
-	 * @throws \phpbb\storage\exception\exception	When cannot open file
+	 * @throws \phpbb\storage\exception\exception		When cannot open file
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 *
 	 * @return resource	Returns a file pointer
 	 */
@@ -161,8 +170,9 @@ class storage
 	 * @param string	$path		The target file
 	 * @param resource	$resource	The resource
 	 *
-	 * @throws \phpbb\storage\exception\exception	When target file exists
-	 * 												When target file cannot be created
+	 * @throws \phpbb\storage\exception\exception		When target file exists
+	 * 													When target file cannot be created
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function write_stream($path, $resource)
 	{

From 9b7a5fc2a5f72989505278b95c4dba4b3d45d027 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Mon, 7 Aug 2017 12:39:42 +0200
Subject: [PATCH 03/18] [ticket/15305] Add tests

PHPBB3-15305
---
 tests/storage/adapter/local_test.php | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/tests/storage/adapter/local_test.php b/tests/storage/adapter/local_test.php
index a2725cd387..0eeb3f2b31 100644
--- a/tests/storage/adapter/local_test.php
+++ b/tests/storage/adapter/local_test.php
@@ -103,4 +103,23 @@
 		unlink($this->path . 'file2.txt');
 	}
 
+	public function test_read_stream()
+	{
+		file_put_contents($this->path . 'file.txt', '');
+		$stream = $this->adapter->read_stream($this->path . 'file.txt');
+		$this->assertTrue(is_resource($stream));
+		fclose($stream);
+		unlink($this->path . 'file.txt');
+	}
+
+	public function test_write_stream()
+	{
+		file_put_contents($this->path . 'file.txt', 'abc');
+		$stream = fopen($this->path . 'file.txt', 'r');
+		$this->adapter->write_stream($this->path . 'file2.txt', $stream);
+		fclose($stream);
+		$this->assertEquals(file_get_contents($this->path . 'file2.txt'), 'abc');
+		unlink($this->path . 'file.txt');
+		unlink($this->path . 'file2.txt');
+	}
  }

From cd2bae63cbc730df8a5d08a1839257b80732c779 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Mon, 7 Aug 2017 13:19:05 +0200
Subject: [PATCH 04/18] [ticket/15305] Fix streams

PHPBB3-15305
---
 phpBB/phpbb/storage/adapter/local.php | 4 ++--
 tests/storage/adapter/local_test.php  | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/phpBB/phpbb/storage/adapter/local.php b/phpBB/phpbb/storage/adapter/local.php
index 4c822d3a5e..479f8d990e 100644
--- a/phpBB/phpbb/storage/adapter/local.php
+++ b/phpBB/phpbb/storage/adapter/local.php
@@ -201,7 +201,7 @@ class local implements adapter_interface
 	 */
 	public function read_stream($path)
 	{
-		$stream = @fopen($path, 'rb');
+		$stream = @fopen($this->root_path . $path, 'rb');
 
 		if (!$stream)
 		{
@@ -221,7 +221,7 @@ class local implements adapter_interface
 			throw new exception('STORAGE_FILE_EXISTS', $path);
 		}
 
-		$stream = @fopen($path, 'w+b');
+		$stream = @fopen($this->root_path . $path, 'w+b');
 
 		if (!$stream)
 		{
diff --git a/tests/storage/adapter/local_test.php b/tests/storage/adapter/local_test.php
index 0eeb3f2b31..e31f507c4f 100644
--- a/tests/storage/adapter/local_test.php
+++ b/tests/storage/adapter/local_test.php
@@ -106,7 +106,7 @@
 	public function test_read_stream()
 	{
 		file_put_contents($this->path . 'file.txt', '');
-		$stream = $this->adapter->read_stream($this->path . 'file.txt');
+		$stream = $this->adapter->read_stream('file.txt');
 		$this->assertTrue(is_resource($stream));
 		fclose($stream);
 		unlink($this->path . 'file.txt');
@@ -116,7 +116,7 @@
 	{
 		file_put_contents($this->path . 'file.txt', 'abc');
 		$stream = fopen($this->path . 'file.txt', 'r');
-		$this->adapter->write_stream($this->path . 'file2.txt', $stream);
+		$this->adapter->write_stream('file2.txt', $stream);
 		fclose($stream);
 		$this->assertEquals(file_get_contents($this->path . 'file2.txt'), 'abc');
 		unlink($this->path . 'file.txt');

From c93c2d400727d3090b1753daa76fdf0240ab995a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Mon, 7 Aug 2017 17:15:49 +0200
Subject: [PATCH 05/18] [ticket/15305] Use an aditional interface for streams

PHPBB3-15305
---
 .../storage/adapter/adapter_interface.php     | 24 -----------
 phpBB/phpbb/storage/adapter/local.php         |  3 +-
 phpBB/phpbb/storage/storage.php               |  7 +---
 phpBB/phpbb/storage/stream_interface.php      | 41 +++++++++++++++++++
 4 files changed, 45 insertions(+), 30 deletions(-)
 create mode 100644 phpBB/phpbb/storage/stream_interface.php

diff --git a/phpBB/phpbb/storage/adapter/adapter_interface.php b/phpBB/phpbb/storage/adapter/adapter_interface.php
index 8cf7bd3a88..7ad90b9aa5 100644
--- a/phpBB/phpbb/storage/adapter/adapter_interface.php
+++ b/phpBB/phpbb/storage/adapter/adapter_interface.php
@@ -92,28 +92,4 @@ interface adapter_interface
 	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function copy($path_orig, $path_dest);
-
-	/**
-	 * Reads a file as a stream.
-	 *
-	 * @param string	$path	File to read
-	 *
-	 * @throws \phpbb\storage\exception\exception		When cannot open file
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
-	 *
-	 * @return resource	Returns a file pointer
-	 */
-	public function read_stream($path);
-
-	/**
-	 * Writes a new file using a stream.
-	 *
-	 * @param string	$path		The target file
-	 * @param resource	$resource	The resource
-	 *
-	 * @throws \phpbb\storage\exception\exception		When target file exists
-	 * 													When target file cannot be created
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
-	 */
-	public function write_stream($path, $resource);
 }
diff --git a/phpBB/phpbb/storage/adapter/local.php b/phpBB/phpbb/storage/adapter/local.php
index 479f8d990e..225f15c432 100644
--- a/phpBB/phpbb/storage/adapter/local.php
+++ b/phpBB/phpbb/storage/adapter/local.php
@@ -13,6 +13,7 @@
 
 namespace phpbb\storage\adapter;
 
+use phpbb\storage\stream_interface;
 use phpbb\storage\exception\exception;
 use phpbb\filesystem\exception\filesystem_exception;
 use phpbb\filesystem\filesystem;
@@ -21,7 +22,7 @@ use phpbb\filesystem\helper as filesystem_helper;
 /**
  * @internal Experimental
  */
-class local implements adapter_interface
+class local implements adapter_interface, stream_interface
 {
 	/**
 	 * Filesystem component
diff --git a/phpBB/phpbb/storage/storage.php b/phpBB/phpbb/storage/storage.php
index c0c70fac44..3d7052bc84 100644
--- a/phpBB/phpbb/storage/storage.php
+++ b/phpBB/phpbb/storage/storage.php
@@ -155,8 +155,7 @@ class storage
 	 * @param string	$path	File to read
 	 *
 	 * @throws \phpbb\storage\exception\exception		When cannot open file
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
-	 *
+
 	 * @return resource	Returns a file pointer
 	 */
 	public function read_stream($path)
@@ -169,9 +168,7 @@ class storage
 	 *
 	 * @param string	$path		The target file
 	 * @param resource	$resource	The resource
-	 *
-	 * @throws \phpbb\storage\exception\exception		When target file exists
-	 * 													When target file cannot be created
+	 *										When target file cannot be created
 	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function write_stream($path, $resource)
diff --git a/phpBB/phpbb/storage/stream_interface.php b/phpBB/phpbb/storage/stream_interface.php
new file mode 100644
index 0000000000..01328677b5
--- /dev/null
+++ b/phpBB/phpbb/storage/stream_interface.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\storage;
+
+interface stream_interface
+{
+	/**
+	 * Reads a file as a stream.
+	 *
+	 * @param string	$path	File to read
+	 *
+	 * @throws \phpbb\storage\exception\exception		When cannot open file
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
+	 *
+	 * @return resource	Returns a file pointer
+	 */
+	public function read_stream($path);
+
+	/**
+	 * Writes a new file using a stream.
+	 *
+	 * @param string	$path		The target file
+	 * @param resource	$resource	The resource
+	 *
+	 * @throws \phpbb\storage\exception\exception		When target file exists
+	 * 													When target file cannot be created
+	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
+	 */
+	public function write_stream($path, $resource);
+}

From b9e22b4da43295b89f9deac1c6d3c43afb9ca282 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Mon, 7 Aug 2017 17:26:54 +0200
Subject: [PATCH 06/18] [ticket/15305] Simulate streams if they are not
 implemented

PHPBB3-15305
---
 phpBB/phpbb/storage/storage.php | 29 +++++++++++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)

diff --git a/phpBB/phpbb/storage/storage.php b/phpBB/phpbb/storage/storage.php
index 3d7052bc84..93d9208cc7 100644
--- a/phpBB/phpbb/storage/storage.php
+++ b/phpBB/phpbb/storage/storage.php
@@ -160,7 +160,22 @@ class storage
 	 */
 	public function read_stream($path)
 	{
-		$this->get_adapter()->read_stream($path);
+		$stream = null;
+		$adapter = $this->get_adapter();
+
+		if ($adapter instanceof stream_interface)
+		{
+			$stream = $adapter->read_stream($path);
+		}
+		else
+		{
+			// Simulate the stream
+			$stream = tmpfile();
+			fwrite($stream, $adapter->get_contents($path));
+			rewind($stream);
+		}
+
+		return $stream;
 	}
 
 	/**
@@ -173,6 +188,16 @@ class storage
 	 */
 	public function write_stream($path, $resource)
 	{
-		$this->get_adapter()->write_stream($path, $resource);
+		$adapter = $this->get_adapter();
+
+		if ($adapter instanceof stream_interface)
+		{
+			$adapter>write_stream($path, $resource);
+		}
+		else
+		{
+			// Simulate the stream
+			$adapter->put_contents($path, stream_get_contents($resource));
+		}
 	}
 }

From c28100be1c36538146266f499d43dad17e15b408 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Tue, 8 Aug 2017 00:11:34 +0200
Subject: [PATCH 07/18] [ticket/15305] Open file in binary mode

PHPBB3-15305
---
 tests/storage/adapter/local_test.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/storage/adapter/local_test.php b/tests/storage/adapter/local_test.php
index e31f507c4f..6d3ba10bca 100644
--- a/tests/storage/adapter/local_test.php
+++ b/tests/storage/adapter/local_test.php
@@ -115,7 +115,7 @@
 	public function test_write_stream()
 	{
 		file_put_contents($this->path . 'file.txt', 'abc');
-		$stream = fopen($this->path . 'file.txt', 'r');
+		$stream = fopen($this->path . 'file.txt', 'rb');
 		$this->adapter->write_stream('file2.txt', $stream);
 		fclose($stream);
 		$this->assertEquals(file_get_contents($this->path . 'file2.txt'), 'abc');

From 9d8e0080b97de3627674b9077aa298403d925df8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Tue, 8 Aug 2017 00:14:56 +0200
Subject: [PATCH 08/18] [ticket/15305] Open file in w+b mode

PHPBB3-15305
---
 tests/storage/adapter/local_test.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/storage/adapter/local_test.php b/tests/storage/adapter/local_test.php
index 6d3ba10bca..1e1f33292e 100644
--- a/tests/storage/adapter/local_test.php
+++ b/tests/storage/adapter/local_test.php
@@ -115,7 +115,7 @@
 	public function test_write_stream()
 	{
 		file_put_contents($this->path . 'file.txt', 'abc');
-		$stream = fopen($this->path . 'file.txt', 'rb');
+		$stream = fopen($this->path . 'file.txt', 'w+b');
 		$this->adapter->write_stream('file2.txt', $stream);
 		fclose($stream);
 		$this->assertEquals(file_get_contents($this->path . 'file2.txt'), 'abc');

From e87daeb95282bf22ecbc8863948b4f10953a14fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Tue, 8 Aug 2017 00:21:14 +0200
Subject: [PATCH 09/18] [ticket/15305] Fix typo

PHPBB3-15305
---
 phpBB/phpbb/storage/storage.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/phpBB/phpbb/storage/storage.php b/phpBB/phpbb/storage/storage.php
index 93d9208cc7..46e75daadf 100644
--- a/phpBB/phpbb/storage/storage.php
+++ b/phpBB/phpbb/storage/storage.php
@@ -192,7 +192,7 @@ class storage
 
 		if ($adapter instanceof stream_interface)
 		{
-			$adapter>write_stream($path, $resource);
+			$adapter->write_stream($path, $resource);
 		}
 		else
 		{

From 4a4e5d13b1f5b5485cf64d63b7c7c2de1f15fe13 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Tue, 8 Aug 2017 00:28:29 +0200
Subject: [PATCH 10/18] [ticket/15305] Revert open in w+b mode

PHPBB3-15305
---
 tests/storage/adapter/local_test.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/storage/adapter/local_test.php b/tests/storage/adapter/local_test.php
index 1e1f33292e..6d3ba10bca 100644
--- a/tests/storage/adapter/local_test.php
+++ b/tests/storage/adapter/local_test.php
@@ -115,7 +115,7 @@
 	public function test_write_stream()
 	{
 		file_put_contents($this->path . 'file.txt', 'abc');
-		$stream = fopen($this->path . 'file.txt', 'w+b');
+		$stream = fopen($this->path . 'file.txt', 'rb');
 		$this->adapter->write_stream('file2.txt', $stream);
 		fclose($stream);
 		$this->assertEquals(file_get_contents($this->path . 'file2.txt'), 'abc');

From 98ad1e4971fb67d3ba092a614a2c949ab37fce16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Tue, 8 Aug 2017 11:24:20 +0200
Subject: [PATCH 11/18] [ticket/15305] Use php://temp stream instead of
 tmpfile()

The tmpfile() return a file resource, but is not in binary mode.

PHPBB3-15305
---
 phpBB/phpbb/storage/storage.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/phpBB/phpbb/storage/storage.php b/phpBB/phpbb/storage/storage.php
index 46e75daadf..7798b28ad4 100644
--- a/phpBB/phpbb/storage/storage.php
+++ b/phpBB/phpbb/storage/storage.php
@@ -170,7 +170,7 @@ class storage
 		else
 		{
 			// Simulate the stream
-			$stream = tmpfile();
+			$stream = fopen('php://temp', 'w+b');
 			fwrite($stream, $adapter->get_contents($path));
 			rewind($stream);
 		}

From d89df359596987ff3f7f0fbdf2b5225cf0b8170d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Tue, 8 Aug 2017 11:40:16 +0200
Subject: [PATCH 12/18] [ticket/15305] Throw exception if
 stream_copy_to_stream() fail

PHPBB3-15305
---
 phpBB/language/en/common.php          | 1 +
 phpBB/phpbb/storage/adapter/local.php | 5 ++++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/phpBB/language/en/common.php b/phpBB/language/en/common.php
index 19a9a720cb..05de3c05bc 100644
--- a/phpBB/language/en/common.php
+++ b/phpBB/language/en/common.php
@@ -735,6 +735,7 @@ $lang = array_merge($lang, array(
 	'STORAGE_CANNOT_DELETE'			=> 'Can not delete file or folder.',
 	'STORAGE_CANNOT_RENAME'			=> 'Can not rename file or folder.',
 	'STORAGE_CANNOT_COPY'			=> 'Can not copy file or folder.',
+	'STORAGE_CANNOT_COPY_RESOURCE'	=> 'Can not copy resource.',
 	'STORAGE_CANNOT_CREATE_DIR'		=> 'Can not create directory.',
 	'STORAGE_CANNOT_OPEN_FILE'		=> 'Can not open file.',
 	'STORAGE_CANNOT_CREATE_FILE'	=> 'Can not create file.',
diff --git a/phpBB/phpbb/storage/adapter/local.php b/phpBB/phpbb/storage/adapter/local.php
index 225f15c432..159c85b522 100644
--- a/phpBB/phpbb/storage/adapter/local.php
+++ b/phpBB/phpbb/storage/adapter/local.php
@@ -229,6 +229,9 @@ class local implements adapter_interface, stream_interface
 			throw new exception('STORAGE_CANNOT_CREATE_FILE', $path);
 		}
 
-		stream_copy_to_stream($resource, $stream);
+		if (stream_copy_to_stream($resource, $stream) === false)
+		{
+			throw new exception('STORAGE_CANNOT_COPY_RESOURCE');
+		}
 	}
 }

From 55f3452e2dafbe2709cbb4a579c9f7686ffb9206 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Wed, 9 Aug 2017 12:29:22 +0200
Subject: [PATCH 13/18] [ticket/15305] Use streams in filespec

PHPBB3-15305
---
 phpBB/phpbb/files/filespec_storage.php | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/phpBB/phpbb/files/filespec_storage.php b/phpBB/phpbb/files/filespec_storage.php
index b4042ebf10..c39c47dc38 100644
--- a/phpBB/phpbb/files/filespec_storage.php
+++ b/phpBB/phpbb/files/filespec_storage.php
@@ -447,7 +447,9 @@ class filespec_storage
 
 		try
 		{
-			$storage->put_contents($this->destination_file, file_get_contents($this->filename));
+			$fp = fopen($this->filename, 'rb');
+			$storage->write_stream($this->destination_file, $fp);
+			fclose($fp);
 		}
 		catch (\phpbb\storage\exception\exception $e)
 		{

From dbd80a950af46859ce3c903b130db722d0931001 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Wed, 23 Aug 2017 16:32:19 +0200
Subject: [PATCH 14/18] [ticket/15305] Remove not_implemented exception in
 streams

PHPBB3-15305
---
 phpBB/phpbb/storage/stream_interface.php | 2 --
 1 file changed, 2 deletions(-)

diff --git a/phpBB/phpbb/storage/stream_interface.php b/phpBB/phpbb/storage/stream_interface.php
index 01328677b5..29c84d775b 100644
--- a/phpBB/phpbb/storage/stream_interface.php
+++ b/phpBB/phpbb/storage/stream_interface.php
@@ -21,7 +21,6 @@ interface stream_interface
 	 * @param string	$path	File to read
 	 *
 	 * @throws \phpbb\storage\exception\exception		When cannot open file
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 *
 	 * @return resource	Returns a file pointer
 	 */
@@ -35,7 +34,6 @@ interface stream_interface
 	 *
 	 * @throws \phpbb\storage\exception\exception		When target file exists
 	 * 													When target file cannot be created
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function write_stream($path, $resource);
 }

From b311bb64111a7d6d1d546d4bf54d72dde9804165 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Wed, 23 Aug 2017 16:40:46 +0200
Subject: [PATCH 15/18] [ticket/15305] Update annotations

PHPBB3-15305
---
 phpBB/phpbb/storage/adapter/adapter_interface.php | 2 +-
 phpBB/phpbb/storage/storage.php                   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/phpBB/phpbb/storage/adapter/adapter_interface.php b/phpBB/phpbb/storage/adapter/adapter_interface.php
index 7ad90b9aa5..fb214f5b07 100644
--- a/phpBB/phpbb/storage/adapter/adapter_interface.php
+++ b/phpBB/phpbb/storage/adapter/adapter_interface.php
@@ -55,7 +55,7 @@ interface adapter_interface
 	 *
 	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 *
-	 * @return bool	Returns true if all files/directories exist, false otherwise
+	 * @return bool	Returns true if the file/directory exist, false otherwise.
 	 */
 	public function exists($path);
 
diff --git a/phpBB/phpbb/storage/storage.php b/phpBB/phpbb/storage/storage.php
index 7798b28ad4..ee581290d3 100644
--- a/phpBB/phpbb/storage/storage.php
+++ b/phpBB/phpbb/storage/storage.php
@@ -99,7 +99,7 @@ class storage
 	 *
 	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 *
-	 * @return bool	Returns true if all files/directories exist, false otherwise
+	 * @return bool	Returns true if the file/directory exist, false otherwise.
 	 */
 	public function exists($path)
 	{

From 0ec50e9055d620738ddf05aec253e2a72fd5f1a7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Wed, 23 Aug 2017 16:42:06 +0200
Subject: [PATCH 16/18] [ticket/15305] Close stream before throwing an
 exception

PHPBB3-15305
---
 phpBB/phpbb/storage/adapter/local.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/phpBB/phpbb/storage/adapter/local.php b/phpBB/phpbb/storage/adapter/local.php
index 159c85b522..de63d0f6a5 100644
--- a/phpBB/phpbb/storage/adapter/local.php
+++ b/phpBB/phpbb/storage/adapter/local.php
@@ -231,6 +231,7 @@ class local implements adapter_interface, stream_interface
 
 		if (stream_copy_to_stream($resource, $stream) === false)
 		{
+			fclose($stream);
 			throw new exception('STORAGE_CANNOT_COPY_RESOURCE');
 		}
 	}

From 6c3756a1b300d39e443026d8ebcf94fc0c6027f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Thu, 24 Aug 2017 17:42:31 +0200
Subject: [PATCH 17/18] [ticket/15305] Remove not_implemented exception

PHPBB3-15305
---
 .../storage/adapter/adapter_interface.php     |  7 -------
 .../storage/exception/not_implemented.php     | 19 -------------------
 phpBB/phpbb/storage/storage.php               |  8 --------
 3 files changed, 34 deletions(-)
 delete mode 100644 phpBB/phpbb/storage/exception/not_implemented.php

diff --git a/phpBB/phpbb/storage/adapter/adapter_interface.php b/phpBB/phpbb/storage/adapter/adapter_interface.php
index fb214f5b07..b2f6043741 100644
--- a/phpBB/phpbb/storage/adapter/adapter_interface.php
+++ b/phpBB/phpbb/storage/adapter/adapter_interface.php
@@ -30,7 +30,6 @@ interface adapter_interface
 	 *
 	 * @throws \phpbb\storage\exception\exception		When the file already exists
 	 * 													When the file cannot be written
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function put_contents($path, $content);
 
@@ -41,7 +40,6 @@ interface adapter_interface
 	 *
 	 * @throws \phpbb\storage\exception\exception		When the file dont exists
 	 * 													When cannot read file contents
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 *
 	 * @return string	Returns file contents
 	 *
@@ -53,8 +51,6 @@ interface adapter_interface
 	 *
 	 * @param string	$path	file/directory to check
 	 *
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
-	 *
 	 * @return bool	Returns true if the file/directory exist, false otherwise.
 	 */
 	public function exists($path);
@@ -65,7 +61,6 @@ interface adapter_interface
 	 * @param string	$path	file/directory to remove
 	 *
 	 * @throws \phpbb\storage\exception\exception		When removal fails.
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function delete($path);
 
@@ -77,7 +72,6 @@ interface adapter_interface
 	 *
 	 * @throws \phpbb\storage\exception\exception		When target exists
 	 * 													When file/directory cannot be renamed
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function rename($path_orig, $path_dest);
 
@@ -89,7 +83,6 @@ interface adapter_interface
 	 *
 	 * @throws \phpbb\storage\exception\exception		When target exists
 	 * 													When the file cannot be copied
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function copy($path_orig, $path_dest);
 }
diff --git a/phpBB/phpbb/storage/exception/not_implemented.php b/phpBB/phpbb/storage/exception/not_implemented.php
deleted file mode 100644
index df535048e5..0000000000
--- a/phpBB/phpbb/storage/exception/not_implemented.php
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-/**
- *
- * This file is part of the phpBB Forum Software package.
- *
- * @copyright (c) phpBB Limited <https://www.phpbb.com>
- * @license GNU General Public License, version 2 (GPL-2.0)
- *
- * For full copyright and license information, please see
- * the docs/CREDITS.txt file.
- *
- */
-
-namespace phpbb\storage\exception;
-
-class not_implemented extends exception
-{
-
-}
diff --git a/phpBB/phpbb/storage/storage.php b/phpBB/phpbb/storage/storage.php
index ee581290d3..609a05cbd1 100644
--- a/phpBB/phpbb/storage/storage.php
+++ b/phpBB/phpbb/storage/storage.php
@@ -68,7 +68,6 @@ class storage
 	 *
 	 * @throws \phpbb\storage\exception\exception		When the file already exists
 	 * 													When the file cannot be written
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function put_contents($path, $content)
 	{
@@ -82,7 +81,6 @@ class storage
 	 *
 	 * @throws \phpbb\storage\exception\exception		When the file dont exists
 	 * 													When cannot read file contents
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 *
 	 * @return string	Returns file contents
 	 *
@@ -97,8 +95,6 @@ class storage
 	 *
 	 * @param string	$path	file/directory to check
 	 *
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
-	 *
 	 * @return bool	Returns true if the file/directory exist, false otherwise.
 	 */
 	public function exists($path)
@@ -112,7 +108,6 @@ class storage
 	 * @param string	$path	file/directory to remove
 	 *
 	 * @throws \phpbb\storage\exception\exception		When removal fails.
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function delete($path)
 	{
@@ -127,7 +122,6 @@ class storage
 	 *
 	 * @throws \phpbb\storage\exception\exception		When target exists
 	 * 													When file/directory cannot be renamed
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function rename($path_orig, $path_dest)
 	{
@@ -142,7 +136,6 @@ class storage
 	 *
 	 * @throws \phpbb\storage\exception\exception		When target exists
 	 * 													When the file cannot be copied
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function copy($path_orig, $path_dest)
 	{
@@ -184,7 +177,6 @@ class storage
 	 * @param string	$path		The target file
 	 * @param resource	$resource	The resource
 	 *										When target file cannot be created
-	 * @throws \phpbb\storage\exception\not_implemented	When the adapter doesnt implement the method
 	 */
 	public function write_stream($path, $resource)
 	{

From 89bb3593613877867d6d80068c75d3d095bd8a31 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n=20Calvo?= <rubencm@gmail.com>
Date: Fri, 25 Aug 2017 15:11:21 +0200
Subject: [PATCH 18/18] [ticket/15305] Update phpdoc

PHPBB3-15305
---
 phpBB/phpbb/storage/storage.php          | 2 +-
 phpBB/phpbb/storage/stream_interface.php | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/phpBB/phpbb/storage/storage.php b/phpBB/phpbb/storage/storage.php
index 609a05cbd1..a2eb89ecb3 100644
--- a/phpBB/phpbb/storage/storage.php
+++ b/phpBB/phpbb/storage/storage.php
@@ -147,7 +147,7 @@ class storage
 	 *
 	 * @param string	$path	File to read
 	 *
-	 * @throws \phpbb\storage\exception\exception		When cannot open file
+	 * @throws \phpbb\storage\exception\exception		When unable to open file
 
 	 * @return resource	Returns a file pointer
 	 */
diff --git a/phpBB/phpbb/storage/stream_interface.php b/phpBB/phpbb/storage/stream_interface.php
index 29c84d775b..0ba866777a 100644
--- a/phpBB/phpbb/storage/stream_interface.php
+++ b/phpBB/phpbb/storage/stream_interface.php
@@ -20,7 +20,7 @@ interface stream_interface
 	 *
 	 * @param string	$path	File to read
 	 *
-	 * @throws \phpbb\storage\exception\exception		When cannot open file
+	 * @throws \phpbb\storage\exception\exception		When unable to open file
 	 *
 	 * @return resource	Returns a file pointer
 	 */