Passed
Push — master ( 3a8cb3...bd2da0 )
by Pauli
13:04
created

FileStreamResponse::streamDataToOutput()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 8
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 10
rs 10
1
<?php declare(strict_types=1);
2
3
/**
4
 * ownCloud - Music app
5
 *
6
 * This file is licensed under the Affero General Public License version 3 or
7
 * later. See the COPYING file.
8
 *
9
 * @author Pauli Järvinen <[email protected]>
10
 * @copyright Pauli Järvinen 2021
11
 */
12
13
namespace OCA\Music\Http;
14
15
use OCP\AppFramework\Http\ICallbackResponse;
16
use OCP\AppFramework\Http\IOutput;
17
use OCP\AppFramework\Http\Response;
18
use OCP\AppFramework\Http;
19
20
/**
21
 * A renderer for files
22
 */
23
class FileStreamResponse extends Response implements ICallbackResponse {
24
	private $file;
25
	private $start;
26
	private $end;
27
28
	/**
29
	 * @param \OCP\Files\File $file file
30
	 */
31
	public function __construct($file) {
32
33
		$this->file = $file;
34
		$mime = $file->getMimetype();
35
		$size = $file->getSize();
36
		
37
		$this->addHeader('Content-type', "$mime; charset=utf-8");
38
39
		if (isset($_SERVER['HTTP_RANGE'])) {
40
			// Note that we do not support Range Header of the type
41
			// bytes=x-y,z-w
42
			if (!\preg_match('/^bytes=\d*-\d*$/', $_SERVER['HTTP_RANGE'])) {
43
				$this->addHeader('Content-Range', 'bytes */' . $size);
44
				$this->setStatus(Http::STATUS_REQUEST_RANGE_NOT_SATISFIABLE);
45
			} else {
46
				$parts = \explode('-', \substr($_SERVER['HTTP_RANGE'], 6));
47
				$this->start = $parts[0] != '' ? (int)$parts[0] : 0;
48
				$this->end = $parts[1] != '' ? (int)$parts[1] : $size - 1;
49
				$this->end = \min($this->end, $size - 1);
50
51
				if ($this->start > $this->end) {
52
					$this->addHeader('Content-Range', 'bytes */' . $size);
53
					$this->setStatus(Http::STATUS_REQUEST_RANGE_NOT_SATISFIABLE);
54
				} else {
55
					$this->addHeader('Accept-Ranges', 'bytes');
56
					$this->addHeader(
57
						'Content-Range', 'bytes ' .
58
						$this->start . '-' .
59
						$this->end . '/' . $size
60
					);
61
					$this->setStatus(Http::STATUS_PARTIAL_CONTENT);
62
				}
63
			}
64
		} else {
65
			$this->start = 0;
66
			$this->end = $size - 1;
67
			$this->setStatus(Http::STATUS_OK);
68
		}
69
	}
70
71
	public function callback(IOutput $output) {
72
		$status = $this->getStatus();
73
74
		if ($status === Http::STATUS_OK || $status === Http::STATUS_PARTIAL_CONTENT) {
75
			$fp = $this->file->fopen('r') ?? null;
76
77
			if (!is_resource($fp)) {
78
				$status = Http::STATUS_NOT_FOUND;
79
			} else {
80
				if ($this->streamDataToOutput($fp) === false) {
81
					$status = Http::STATUS_BAD_REQUEST;
82
				}
83
				\fclose($fp);
84
			}
85
		}
86
87
		$output->setHttpResponseCode($status);
88
	}
89
90
	private function streamDataToOutput($fp) {
91
		// Request Range Not Satisfiable
92
		if (!isset($this->start) || !isset($this->end) || $this->start > $this->end) {
93
			return false;
94
		} else {
95
			$outputStream = \fopen('php://output', 'w');
96
			$length = $this->end - $this->start + 1;
97
			$bytesCopied = stream_copy_to_stream($fp, $outputStream, $length, $this->start);
98
			\fclose($outputStream);
99
			return ($bytesCopied > 0);
100
		}
101
	}
102
103
}
104