Passed
Push — master ( d23e96...1bc100 )
by Roeland
11:25 queued 10s
created

FileMimeType::cacheAndReturnMimeType()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 3
c 1
b 0
f 0
nc 2
nop 3
dl 0
loc 6
rs 10
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Joas Schilling <[email protected]>
4
 *
5
 * @license GNU AGPL version 3 or any later version
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as
9
 * published by the Free Software Foundation, either version 3 of the
10
 * License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 */
21
22
namespace OCA\WorkflowEngine\Check;
23
24
25
use OCA\WorkflowEngine\Entity\File;
26
use OCP\Files\IMimeTypeDetector;
27
use OCP\Files\Storage\IStorage;
28
use OCP\IL10N;
29
use OCP\IRequest;
30
use OCP\WorkflowEngine\IFileCheck;
31
32
class FileMimeType extends AbstractStringCheck implements IFileCheck {
33
	use TFileCheck {
34
		setFileInfo as _setFileInfo;
35
	}
36
37
	/** @var array */
38
	protected $mimeType;
39
40
	/** @var IRequest */
41
	protected $request;
42
43
	/** @var IMimeTypeDetector */
44
	protected $mimeTypeDetector;
45
46
	/**
47
	 * @param IL10N $l
48
	 * @param IRequest $request
49
	 * @param IMimeTypeDetector $mimeTypeDetector
50
	 */
51
	public function __construct(IL10N $l, IRequest $request, IMimeTypeDetector $mimeTypeDetector) {
52
		parent::__construct($l);
53
		$this->request = $request;
54
		$this->mimeTypeDetector = $mimeTypeDetector;
55
	}
56
57
	/**
58
	 * @param IStorage $storage
59
	 * @param string $path
60
	 */
61
	public function setFileInfo(IStorage $storage, string $path) {
62
		$this->_setFileInfo($storage, $path);
63
		if (!isset($this->mimeType[$this->storage->getId()][$this->path])
64
			|| $this->mimeType[$this->storage->getId()][$this->path] === '') {
65
			$this->mimeType[$this->storage->getId()][$this->path] = null;
66
		}
67
	}
68
69
	/**
70
	 * The mimetype is only cached if the file exists. Otherwise files access
71
	 * control will cache "application/octet-stream" for all the target node on:
72
	 * rename, move, copy and all other methods which create a new item
73
	 *
74
	 * To check this:
75
	 * 1. Add an automated tagging rule which tags on mimetype NOT "httpd/unix-directory"
76
	 * 2. Add an access control rule which checks for any mimetype
77
	 * 3. Create a folder and rename it, the folder should not be tagged, but it is
78
	 *
79
	 * @param string $storageId
80
	 * @param string|null $path
81
	 * @param string $mimeType
82
	 * @return string
83
	 */
84
	protected function cacheAndReturnMimeType(string $storageId, ?string $path, string $mimeType): string {
85
		if ($path !== null && $this->storage->file_exists($path)) {
86
			$this->mimeType[$storageId][$path] = $mimeType;
87
		}
88
89
		return $mimeType;
90
	}
91
92
	/**
93
	 * @return string
94
	 */
95
	protected function getActualValue() {
96
		if ($this->mimeType[$this->storage->getId()][$this->path] !== null) {
97
			return $this->mimeType[$this->storage->getId()][$this->path];
98
		}
99
100
		if ($this->storage->is_dir($this->path)) {
101
			return $this->cacheAndReturnMimeType($this->storage->getId(), $this->path, 'httpd/unix-directory');
102
		}
103
104
		if ($this->isWebDAVRequest()) {
105
			// Creating a folder
106
			if ($this->request->getMethod() === 'MKCOL') {
107
				return $this->cacheAndReturnMimeType($this->storage->getId(), $this->path, 'httpd/unix-directory');
108
			}
109
110
			if ($this->request->getMethod() === 'PUT' || $this->request->getMethod() === 'MOVE') {
111
				if ($this->request->getMethod() === 'MOVE') {
112
					$mimeType = $this->mimeTypeDetector->detectPath($this->path);
113
				} else {
114
					$path = $this->request->getPathInfo();
115
					$mimeType = $this->mimeTypeDetector->detectPath($path);
116
				}
117
				return $this->cacheAndReturnMimeType($this->storage->getId(), $this->path, $mimeType);
118
			}
119
		} else if ($this->isPublicWebDAVRequest()) {
120
			if ($this->request->getMethod() === 'PUT') {
121
				$path = $this->request->getPathInfo();
122
				if (strpos($path, '/webdav/') === 0) {
123
					$path = substr($path, strlen('/webdav'));
124
				}
125
				$path = $this->path . $path;
0 ignored issues
show
Bug introduced by
Are you sure $path of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

125
				$path = $this->path . /** @scrutinizer ignore-type */ $path;
Loading history...
126
				$mimeType = $this->mimeTypeDetector->detectPath($path);
127
				return $this->cacheAndReturnMimeType($this->storage->getId(), $path, $mimeType);
128
			}
129
		}
130
131
		if (in_array($this->request->getMethod(), ['POST', 'PUT'])) {
132
			$files = $this->request->getUploadedFile('files');
133
			if (isset($files['type'][0])) {
134
				$mimeType = $files['type'][0];
135
				if ($mimeType === 'application/octet-stream') {
136
					// Maybe not...
137
					$mimeTypeTest = $this->mimeTypeDetector->detectPath($files['name'][0]);
138
					if ($mimeTypeTest !== 'application/octet-stream' && $mimeTypeTest !== false) {
139
						$mimeType = $mimeTypeTest;
140
					} else {
141
						$mimeTypeTest = $this->mimeTypeDetector->detect($files['tmp_name'][0]);
142
						if ($mimeTypeTest !== 'application/octet-stream' && $mimeTypeTest !== false) {
143
							$mimeType = $mimeTypeTest;
144
						}
145
					}
146
				}
147
				return $this->cacheAndReturnMimeType($this->storage->getId(), $this->path, $mimeType);
148
			}
149
		}
150
151
		$mimeType = $this->storage->getMimeType($this->path);
152
		if ($mimeType === 'application/octet-stream') {
153
			$mimeType = $this->detectMimetypeFromPath();
154
		}
155
156
		return $this->cacheAndReturnMimeType($this->storage->getId(), $this->path, $mimeType);
157
	}
158
159
	/**
160
	 * @return string
161
	 */
162
	protected function detectMimetypeFromPath() {
163
		$mimeType = $this->mimeTypeDetector->detectPath($this->path);
164
		if ($mimeType !== 'application/octet-stream' && $mimeType !== false) {
165
			return $mimeType;
166
		}
167
168
		if ($this->storage->instanceOfStorage('\OC\Files\Storage\Local')
169
			|| $this->storage->instanceOfStorage('\OC\Files\Storage\Home')
170
			|| $this->storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')) {
171
			$localFile = $this->storage->getLocalFile($this->path);
172
			if ($localFile !== false) {
173
				$mimeType = $this->mimeTypeDetector->detect($localFile);
174
				if ($mimeType !== false) {
0 ignored issues
show
introduced by
The condition $mimeType !== false is always true.
Loading history...
175
					return $mimeType;
176
				}
177
			}
178
179
			return 'application/octet-stream';
180
		} else {
181
			$handle = $this->storage->fopen($this->path, 'r');
182
			$data = fread($handle, 8024);
183
			fclose($handle);
184
			$mimeType = $this->mimeTypeDetector->detectString($data);
185
			if ($mimeType !== false) {
0 ignored issues
show
introduced by
The condition $mimeType !== false is always true.
Loading history...
186
				return $mimeType;
187
			}
188
189
			return 'application/octet-stream';
190
		}
191
	}
192
193
	/**
194
	 * @return bool
195
	 */
196
	protected function isWebDAVRequest() {
197
		return substr($this->request->getScriptName(), 0 - strlen('/remote.php')) === '/remote.php' && (
198
			$this->request->getPathInfo() === '/webdav' ||
199
			strpos($this->request->getPathInfo(), '/webdav/') === 0 ||
200
			$this->request->getPathInfo() === '/dav/files' ||
201
			strpos($this->request->getPathInfo(), '/dav/files/') === 0 ||
202
			$this->request->getPathInfo() === '/dav/uploads' ||
203
			strpos($this->request->getPathInfo(), '/dav/uploads/') === 0
204
		);
205
	}
206
207
	/**
208
	 * @return bool
209
	 */
210
	protected function isPublicWebDAVRequest() {
211
		return substr($this->request->getScriptName(), 0 - strlen('/public.php')) === '/public.php' && (
212
			$this->request->getPathInfo() === '/webdav' ||
213
			strpos($this->request->getPathInfo(), '/webdav/') === 0
214
		);
215
	}
216
217
	public function supportedEntities(): array {
218
		return [ File::class ];
219
	}
220
}
221