Completed
Push — master ( f791d9...8c90d4 )
by
unknown
32:49
created
lib/private/Files/Type/Detection.php 2 patches
Indentation   +351 added lines, -351 removed lines patch added patch discarded remove patch
@@ -22,355 +22,355 @@
 block discarded – undo
22 22
  * @package OC\Files\Type
23 23
  */
24 24
 class Detection implements IMimeTypeDetector {
25
-	private const CUSTOM_MIMETYPEMAPPING = 'mimetypemapping.json';
26
-	private const CUSTOM_MIMETYPEALIASES = 'mimetypealiases.json';
27
-	private const CUSTOM_MIMETYPENAMES = 'mimetypenames.json';
28
-
29
-	/** @var array<list{string, string|null}> */
30
-	protected array $mimeTypes = [];
31
-	protected array $secureMimeTypes = [];
32
-
33
-	protected array $mimeTypeIcons = [];
34
-	/** @var array<string,string> */
35
-	protected array $mimeTypeAlias = [];
36
-	/** @var array<string,string> */
37
-	protected array $mimeTypesNames = [];
38
-
39
-	public function __construct(
40
-		private IURLGenerator $urlGenerator,
41
-		private LoggerInterface $logger,
42
-		private string $customConfigDir,
43
-		private string $defaultConfigDir,
44
-	) {
45
-	}
46
-
47
-	/**
48
-	 * Add an extension -> MIME type mapping
49
-	 *
50
-	 * $mimeType is the assumed correct mime type
51
-	 * The optional $secureMimeType is an alternative to send to send
52
-	 * to avoid potential XSS.
53
-	 *
54
-	 * @param string $extension
55
-	 * @param string $mimeType
56
-	 * @param string|null $secureMimeType
57
-	 */
58
-	public function registerType(
59
-		string $extension,
60
-		string $mimeType,
61
-		?string $secureMimeType = null): void {
62
-		// Make sure the extension is a string
63
-		// https://github.com/nextcloud/server/issues/42902
64
-		$this->mimeTypes[$extension] = [$mimeType, $secureMimeType];
65
-		$this->secureMimeTypes[$mimeType] = $secureMimeType ?? $mimeType;
66
-	}
67
-
68
-	/**
69
-	 * Add an array of extension -> MIME type mappings
70
-	 *
71
-	 * The mimeType value is in itself an array where the first index is
72
-	 * the assumed correct mimeType and the second is either a secure alternative
73
-	 * or null if the correct is considered secure.
74
-	 *
75
-	 * @param array $types
76
-	 */
77
-	public function registerTypeArray(array $types): void {
78
-		// Register the types,
79
-		foreach ($types as $extension => $mimeType) {
80
-			$this->registerType((string)$extension, $mimeType[0], $mimeType[1] ?? null);
81
-		}
82
-
83
-		// Update the alternative mimeTypes to avoid having to look them up each time.
84
-		foreach ($this->mimeTypes as $extension => $mimeType) {
85
-			if (str_starts_with((string)$extension, '_comment')) {
86
-				continue;
87
-			}
88
-
89
-			$this->secureMimeTypes[$mimeType[0]] = $mimeType[1] ?? $mimeType[0];
90
-			if (isset($mimeType[1])) {
91
-				$this->secureMimeTypes[$mimeType[1]] = $mimeType[1];
92
-			}
93
-		}
94
-	}
95
-
96
-	private function loadCustomDefinitions(string $fileName, array $definitions): array {
97
-		if (file_exists($this->customConfigDir . '/' . $fileName)) {
98
-			$custom = json_decode(file_get_contents($this->customConfigDir . '/' . $fileName), true);
99
-			if (json_last_error() === JSON_ERROR_NONE) {
100
-				$definitions = array_replace($definitions, $custom);
101
-			} else {
102
-				$this->logger->warning('Failed to parse ' . $fileName . ': ' . json_last_error_msg());
103
-			}
104
-		}
105
-		return $definitions;
106
-	}
107
-
108
-	/**
109
-	 * Add the MIME type aliases if they are not yet present
110
-	 */
111
-	private function loadAliases(): void {
112
-		if (!empty($this->mimeTypeAlias)) {
113
-			return;
114
-		}
115
-
116
-		$this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true);
117
-		$this->mimeTypeAlias = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPEALIASES, $this->mimeTypeAlias);
118
-	}
119
-
120
-	/**
121
-	 * @return array<string,string>
122
-	 */
123
-	public function getAllAliases(): array {
124
-		$this->loadAliases();
125
-		return $this->mimeTypeAlias;
126
-	}
127
-
128
-	public function getOnlyDefaultAliases(): array {
129
-		$this->loadMappings();
130
-		$this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true);
131
-		return $this->mimeTypeAlias;
132
-	}
133
-
134
-	/**
135
-	 * Add MIME type mappings if they are not yet present
136
-	 */
137
-	private function loadMappings(): void {
138
-		if (!empty($this->mimeTypes)) {
139
-			return;
140
-		}
141
-
142
-		$mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypemapping.dist.json'), true);
143
-		$mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPEMAPPING, $mimeTypeMapping);
144
-
145
-		$this->registerTypeArray($mimeTypeMapping);
146
-	}
147
-
148
-	/**
149
-	 * @return array<list{string, string|null}>
150
-	 */
151
-	public function getAllMappings(): array {
152
-		$this->loadMappings();
153
-		return $this->mimeTypes;
154
-	}
155
-
156
-	private function loadNamings(): void {
157
-		if (!empty($this->mimeTypesNames)) {
158
-			return;
159
-		}
160
-
161
-		$mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypenames.dist.json'), true);
162
-		$mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPENAMES, $mimeTypeMapping);
163
-
164
-		$this->mimeTypesNames = $mimeTypeMapping;
165
-	}
166
-
167
-	/**
168
-	 * @return array<string,string>
169
-	 */
170
-	public function getAllNamings(): array {
171
-		$this->loadNamings();
172
-		return $this->mimeTypesNames;
173
-	}
174
-
175
-	/**
176
-	 * detect MIME type only based on filename, content of file is not used
177
-	 *
178
-	 * @param string $path
179
-	 * @return string
180
-	 */
181
-	public function detectPath($path): string {
182
-		$this->loadMappings();
183
-
184
-		$fileName = basename($path);
185
-
186
-		// remove leading dot on hidden files with a file extension
187
-		$fileName = ltrim($fileName, '.');
188
-
189
-		// note: leading dot doesn't qualify as extension
190
-		if (strpos($fileName, '.') > 0) {
191
-			// remove versioning extension: name.v1508946057 and transfer extension: name.ocTransferId2057600214.part
192
-			$fileName = preg_replace('!((\.v\d+)|((\.ocTransferId\d+)?\.part))$!', '', $fileName);
193
-
194
-			//try to guess the type by the file extension
195
-			$extension = strrchr($fileName, '.');
196
-			if ($extension !== false) {
197
-				$extension = strtolower($extension);
198
-				$extension = substr($extension, 1); // remove leading .
199
-				return $this->mimeTypes[$extension][0] ?? 'application/octet-stream';
200
-			}
201
-		}
202
-
203
-		return 'application/octet-stream';
204
-	}
205
-
206
-	/**
207
-	 * Detect MIME type only based on the content of file.
208
-	 *
209
-	 * @param string $path
210
-	 * @return string
211
-	 * @since 18.0.0
212
-	 */
213
-	public function detectContent(string $path): string {
214
-		$this->loadMappings();
215
-
216
-		if (@is_dir($path)) {
217
-			// directories are easy
218
-			return 'httpd/unix-directory';
219
-		}
220
-
221
-		if (class_exists(finfo::class)) {
222
-			$finfo = new finfo(FILEINFO_MIME_TYPE);
223
-			$mimeType = @$finfo->file($path);
224
-			if ($mimeType) {
225
-				$mimeType = $this->getSecureMimeType($mimeType);
226
-				if ($mimeType !== 'application/octet-stream') {
227
-					return $mimeType;
228
-				}
229
-			}
230
-		}
231
-
232
-		if (str_starts_with($path, 'file://')) {
233
-			// Is the file wrapped in a stream?
234
-			return 'application/octet-stream';
235
-		}
236
-
237
-		if (function_exists('mime_content_type')) {
238
-			// use mime magic extension if available
239
-			$mimeType = mime_content_type($path);
240
-			if ($mimeType) {
241
-				$mimeType = $this->getSecureMimeType($mimeType);
242
-				if ($mimeType !== 'application/octet-stream') {
243
-					return $mimeType;
244
-				}
245
-			}
246
-		}
247
-
248
-		$binaryFinder = \OCP\Server::get(IBinaryFinder::class);
249
-		$program = $binaryFinder->findBinaryPath('file');
250
-		if ($program !== false) {
251
-			// it looks like we have a 'file' command,
252
-			// lets see if it does have mime support
253
-			$path = escapeshellarg($path);
254
-			$fp = popen("test -f $path && $program -b --mime-type $path", 'r');
255
-			if ($fp !== false) {
256
-				$mimeType = fgets($fp);
257
-				pclose($fp);
258
-				if ($mimeType) {
259
-					//trim the newline
260
-					$mimeType = trim($mimeType);
261
-					$mimeType = $this->getSecureMimeType($mimeType);
262
-					return $mimeType;
263
-				}
264
-			}
265
-		}
266
-
267
-		return 'application/octet-stream';
268
-	}
269
-
270
-	/**
271
-	 * Detect MIME type based on both filename and content
272
-	 *
273
-	 * @param string $path
274
-	 * @return string
275
-	 */
276
-	public function detect($path): string {
277
-		$mimeType = $this->detectPath($path);
278
-
279
-		if ($mimeType !== 'application/octet-stream') {
280
-			return $mimeType;
281
-		}
282
-
283
-		return $this->detectContent($path);
284
-	}
285
-
286
-	/**
287
-	 * Detect MIME type based on the content of a string
288
-	 *
289
-	 * @param string $data
290
-	 * @return string
291
-	 */
292
-	public function detectString($data): string {
293
-		if (class_exists(finfo::class)) {
294
-			$finfo = new finfo(FILEINFO_MIME_TYPE);
295
-			$mimeType = $finfo->buffer($data);
296
-			if ($mimeType) {
297
-				return $mimeType;
298
-			}
299
-		}
300
-
301
-		$tmpFile = \OCP\Server::get(ITempManager::class)->getTemporaryFile();
302
-		$fh = fopen($tmpFile, 'wb');
303
-		fwrite($fh, $data, 8024);
304
-		fclose($fh);
305
-		$mimeType = $this->detect($tmpFile);
306
-		unset($tmpFile);
307
-		return $mimeType;
308
-	}
309
-
310
-	/**
311
-	 * Get a secure MIME type that won't expose potential XSS.
312
-	 *
313
-	 * @param string $mimeType
314
-	 * @return string
315
-	 */
316
-	public function getSecureMimeType($mimeType): string {
317
-		$this->loadMappings();
318
-
319
-		return $this->secureMimeTypes[$mimeType] ?? 'application/octet-stream';
320
-	}
321
-
322
-	/**
323
-	 * Get path to the icon of a file type
324
-	 * @param string $mimeType the MIME type
325
-	 * @return string the url
326
-	 */
327
-	public function mimeTypeIcon($mimeType): string {
328
-		$this->loadAliases();
329
-
330
-		while (isset($this->mimeTypeAlias[$mimeType])) {
331
-			$mimeType = $this->mimeTypeAlias[$mimeType];
332
-		}
333
-		if (isset($this->mimeTypeIcons[$mimeType])) {
334
-			return $this->mimeTypeIcons[$mimeType];
335
-		}
336
-
337
-		// Replace slash and backslash with a minus
338
-		$icon = str_replace(['/', '\\'], '-', $mimeType);
339
-
340
-		// Is it a dir?
341
-		if ($mimeType === 'dir') {
342
-			$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/folder.svg');
343
-			return $this->mimeTypeIcons[$mimeType];
344
-		}
345
-		if ($mimeType === 'dir-shared') {
346
-			$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/folder-shared.svg');
347
-			return $this->mimeTypeIcons[$mimeType];
348
-		}
349
-		if ($mimeType === 'dir-external') {
350
-			$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/folder-external.svg');
351
-			return $this->mimeTypeIcons[$mimeType];
352
-		}
353
-
354
-		// Icon exists?
355
-		try {
356
-			$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/' . $icon . '.svg');
357
-			return $this->mimeTypeIcons[$mimeType];
358
-		} catch (\RuntimeException $e) {
359
-			// Specified image not found
360
-		}
361
-
362
-		// Try only the first part of the filetype
363
-		if (strpos($icon, '-')) {
364
-			$mimePart = substr($icon, 0, strpos($icon, '-'));
365
-			try {
366
-				$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/' . $mimePart . '.svg');
367
-				return $this->mimeTypeIcons[$mimeType];
368
-			} catch (\RuntimeException $e) {
369
-				// Image for the first part of the MIME type not found
370
-			}
371
-		}
372
-
373
-		$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/file.svg');
374
-		return $this->mimeTypeIcons[$mimeType];
375
-	}
25
+    private const CUSTOM_MIMETYPEMAPPING = 'mimetypemapping.json';
26
+    private const CUSTOM_MIMETYPEALIASES = 'mimetypealiases.json';
27
+    private const CUSTOM_MIMETYPENAMES = 'mimetypenames.json';
28
+
29
+    /** @var array<list{string, string|null}> */
30
+    protected array $mimeTypes = [];
31
+    protected array $secureMimeTypes = [];
32
+
33
+    protected array $mimeTypeIcons = [];
34
+    /** @var array<string,string> */
35
+    protected array $mimeTypeAlias = [];
36
+    /** @var array<string,string> */
37
+    protected array $mimeTypesNames = [];
38
+
39
+    public function __construct(
40
+        private IURLGenerator $urlGenerator,
41
+        private LoggerInterface $logger,
42
+        private string $customConfigDir,
43
+        private string $defaultConfigDir,
44
+    ) {
45
+    }
46
+
47
+    /**
48
+     * Add an extension -> MIME type mapping
49
+     *
50
+     * $mimeType is the assumed correct mime type
51
+     * The optional $secureMimeType is an alternative to send to send
52
+     * to avoid potential XSS.
53
+     *
54
+     * @param string $extension
55
+     * @param string $mimeType
56
+     * @param string|null $secureMimeType
57
+     */
58
+    public function registerType(
59
+        string $extension,
60
+        string $mimeType,
61
+        ?string $secureMimeType = null): void {
62
+        // Make sure the extension is a string
63
+        // https://github.com/nextcloud/server/issues/42902
64
+        $this->mimeTypes[$extension] = [$mimeType, $secureMimeType];
65
+        $this->secureMimeTypes[$mimeType] = $secureMimeType ?? $mimeType;
66
+    }
67
+
68
+    /**
69
+     * Add an array of extension -> MIME type mappings
70
+     *
71
+     * The mimeType value is in itself an array where the first index is
72
+     * the assumed correct mimeType and the second is either a secure alternative
73
+     * or null if the correct is considered secure.
74
+     *
75
+     * @param array $types
76
+     */
77
+    public function registerTypeArray(array $types): void {
78
+        // Register the types,
79
+        foreach ($types as $extension => $mimeType) {
80
+            $this->registerType((string)$extension, $mimeType[0], $mimeType[1] ?? null);
81
+        }
82
+
83
+        // Update the alternative mimeTypes to avoid having to look them up each time.
84
+        foreach ($this->mimeTypes as $extension => $mimeType) {
85
+            if (str_starts_with((string)$extension, '_comment')) {
86
+                continue;
87
+            }
88
+
89
+            $this->secureMimeTypes[$mimeType[0]] = $mimeType[1] ?? $mimeType[0];
90
+            if (isset($mimeType[1])) {
91
+                $this->secureMimeTypes[$mimeType[1]] = $mimeType[1];
92
+            }
93
+        }
94
+    }
95
+
96
+    private function loadCustomDefinitions(string $fileName, array $definitions): array {
97
+        if (file_exists($this->customConfigDir . '/' . $fileName)) {
98
+            $custom = json_decode(file_get_contents($this->customConfigDir . '/' . $fileName), true);
99
+            if (json_last_error() === JSON_ERROR_NONE) {
100
+                $definitions = array_replace($definitions, $custom);
101
+            } else {
102
+                $this->logger->warning('Failed to parse ' . $fileName . ': ' . json_last_error_msg());
103
+            }
104
+        }
105
+        return $definitions;
106
+    }
107
+
108
+    /**
109
+     * Add the MIME type aliases if they are not yet present
110
+     */
111
+    private function loadAliases(): void {
112
+        if (!empty($this->mimeTypeAlias)) {
113
+            return;
114
+        }
115
+
116
+        $this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true);
117
+        $this->mimeTypeAlias = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPEALIASES, $this->mimeTypeAlias);
118
+    }
119
+
120
+    /**
121
+     * @return array<string,string>
122
+     */
123
+    public function getAllAliases(): array {
124
+        $this->loadAliases();
125
+        return $this->mimeTypeAlias;
126
+    }
127
+
128
+    public function getOnlyDefaultAliases(): array {
129
+        $this->loadMappings();
130
+        $this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true);
131
+        return $this->mimeTypeAlias;
132
+    }
133
+
134
+    /**
135
+     * Add MIME type mappings if they are not yet present
136
+     */
137
+    private function loadMappings(): void {
138
+        if (!empty($this->mimeTypes)) {
139
+            return;
140
+        }
141
+
142
+        $mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypemapping.dist.json'), true);
143
+        $mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPEMAPPING, $mimeTypeMapping);
144
+
145
+        $this->registerTypeArray($mimeTypeMapping);
146
+    }
147
+
148
+    /**
149
+     * @return array<list{string, string|null}>
150
+     */
151
+    public function getAllMappings(): array {
152
+        $this->loadMappings();
153
+        return $this->mimeTypes;
154
+    }
155
+
156
+    private function loadNamings(): void {
157
+        if (!empty($this->mimeTypesNames)) {
158
+            return;
159
+        }
160
+
161
+        $mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypenames.dist.json'), true);
162
+        $mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPENAMES, $mimeTypeMapping);
163
+
164
+        $this->mimeTypesNames = $mimeTypeMapping;
165
+    }
166
+
167
+    /**
168
+     * @return array<string,string>
169
+     */
170
+    public function getAllNamings(): array {
171
+        $this->loadNamings();
172
+        return $this->mimeTypesNames;
173
+    }
174
+
175
+    /**
176
+     * detect MIME type only based on filename, content of file is not used
177
+     *
178
+     * @param string $path
179
+     * @return string
180
+     */
181
+    public function detectPath($path): string {
182
+        $this->loadMappings();
183
+
184
+        $fileName = basename($path);
185
+
186
+        // remove leading dot on hidden files with a file extension
187
+        $fileName = ltrim($fileName, '.');
188
+
189
+        // note: leading dot doesn't qualify as extension
190
+        if (strpos($fileName, '.') > 0) {
191
+            // remove versioning extension: name.v1508946057 and transfer extension: name.ocTransferId2057600214.part
192
+            $fileName = preg_replace('!((\.v\d+)|((\.ocTransferId\d+)?\.part))$!', '', $fileName);
193
+
194
+            //try to guess the type by the file extension
195
+            $extension = strrchr($fileName, '.');
196
+            if ($extension !== false) {
197
+                $extension = strtolower($extension);
198
+                $extension = substr($extension, 1); // remove leading .
199
+                return $this->mimeTypes[$extension][0] ?? 'application/octet-stream';
200
+            }
201
+        }
202
+
203
+        return 'application/octet-stream';
204
+    }
205
+
206
+    /**
207
+     * Detect MIME type only based on the content of file.
208
+     *
209
+     * @param string $path
210
+     * @return string
211
+     * @since 18.0.0
212
+     */
213
+    public function detectContent(string $path): string {
214
+        $this->loadMappings();
215
+
216
+        if (@is_dir($path)) {
217
+            // directories are easy
218
+            return 'httpd/unix-directory';
219
+        }
220
+
221
+        if (class_exists(finfo::class)) {
222
+            $finfo = new finfo(FILEINFO_MIME_TYPE);
223
+            $mimeType = @$finfo->file($path);
224
+            if ($mimeType) {
225
+                $mimeType = $this->getSecureMimeType($mimeType);
226
+                if ($mimeType !== 'application/octet-stream') {
227
+                    return $mimeType;
228
+                }
229
+            }
230
+        }
231
+
232
+        if (str_starts_with($path, 'file://')) {
233
+            // Is the file wrapped in a stream?
234
+            return 'application/octet-stream';
235
+        }
236
+
237
+        if (function_exists('mime_content_type')) {
238
+            // use mime magic extension if available
239
+            $mimeType = mime_content_type($path);
240
+            if ($mimeType) {
241
+                $mimeType = $this->getSecureMimeType($mimeType);
242
+                if ($mimeType !== 'application/octet-stream') {
243
+                    return $mimeType;
244
+                }
245
+            }
246
+        }
247
+
248
+        $binaryFinder = \OCP\Server::get(IBinaryFinder::class);
249
+        $program = $binaryFinder->findBinaryPath('file');
250
+        if ($program !== false) {
251
+            // it looks like we have a 'file' command,
252
+            // lets see if it does have mime support
253
+            $path = escapeshellarg($path);
254
+            $fp = popen("test -f $path && $program -b --mime-type $path", 'r');
255
+            if ($fp !== false) {
256
+                $mimeType = fgets($fp);
257
+                pclose($fp);
258
+                if ($mimeType) {
259
+                    //trim the newline
260
+                    $mimeType = trim($mimeType);
261
+                    $mimeType = $this->getSecureMimeType($mimeType);
262
+                    return $mimeType;
263
+                }
264
+            }
265
+        }
266
+
267
+        return 'application/octet-stream';
268
+    }
269
+
270
+    /**
271
+     * Detect MIME type based on both filename and content
272
+     *
273
+     * @param string $path
274
+     * @return string
275
+     */
276
+    public function detect($path): string {
277
+        $mimeType = $this->detectPath($path);
278
+
279
+        if ($mimeType !== 'application/octet-stream') {
280
+            return $mimeType;
281
+        }
282
+
283
+        return $this->detectContent($path);
284
+    }
285
+
286
+    /**
287
+     * Detect MIME type based on the content of a string
288
+     *
289
+     * @param string $data
290
+     * @return string
291
+     */
292
+    public function detectString($data): string {
293
+        if (class_exists(finfo::class)) {
294
+            $finfo = new finfo(FILEINFO_MIME_TYPE);
295
+            $mimeType = $finfo->buffer($data);
296
+            if ($mimeType) {
297
+                return $mimeType;
298
+            }
299
+        }
300
+
301
+        $tmpFile = \OCP\Server::get(ITempManager::class)->getTemporaryFile();
302
+        $fh = fopen($tmpFile, 'wb');
303
+        fwrite($fh, $data, 8024);
304
+        fclose($fh);
305
+        $mimeType = $this->detect($tmpFile);
306
+        unset($tmpFile);
307
+        return $mimeType;
308
+    }
309
+
310
+    /**
311
+     * Get a secure MIME type that won't expose potential XSS.
312
+     *
313
+     * @param string $mimeType
314
+     * @return string
315
+     */
316
+    public function getSecureMimeType($mimeType): string {
317
+        $this->loadMappings();
318
+
319
+        return $this->secureMimeTypes[$mimeType] ?? 'application/octet-stream';
320
+    }
321
+
322
+    /**
323
+     * Get path to the icon of a file type
324
+     * @param string $mimeType the MIME type
325
+     * @return string the url
326
+     */
327
+    public function mimeTypeIcon($mimeType): string {
328
+        $this->loadAliases();
329
+
330
+        while (isset($this->mimeTypeAlias[$mimeType])) {
331
+            $mimeType = $this->mimeTypeAlias[$mimeType];
332
+        }
333
+        if (isset($this->mimeTypeIcons[$mimeType])) {
334
+            return $this->mimeTypeIcons[$mimeType];
335
+        }
336
+
337
+        // Replace slash and backslash with a minus
338
+        $icon = str_replace(['/', '\\'], '-', $mimeType);
339
+
340
+        // Is it a dir?
341
+        if ($mimeType === 'dir') {
342
+            $this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/folder.svg');
343
+            return $this->mimeTypeIcons[$mimeType];
344
+        }
345
+        if ($mimeType === 'dir-shared') {
346
+            $this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/folder-shared.svg');
347
+            return $this->mimeTypeIcons[$mimeType];
348
+        }
349
+        if ($mimeType === 'dir-external') {
350
+            $this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/folder-external.svg');
351
+            return $this->mimeTypeIcons[$mimeType];
352
+        }
353
+
354
+        // Icon exists?
355
+        try {
356
+            $this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/' . $icon . '.svg');
357
+            return $this->mimeTypeIcons[$mimeType];
358
+        } catch (\RuntimeException $e) {
359
+            // Specified image not found
360
+        }
361
+
362
+        // Try only the first part of the filetype
363
+        if (strpos($icon, '-')) {
364
+            $mimePart = substr($icon, 0, strpos($icon, '-'));
365
+            try {
366
+                $this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/' . $mimePart . '.svg');
367
+                return $this->mimeTypeIcons[$mimeType];
368
+            } catch (\RuntimeException $e) {
369
+                // Image for the first part of the MIME type not found
370
+            }
371
+        }
372
+
373
+        $this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/file.svg');
374
+        return $this->mimeTypeIcons[$mimeType];
375
+    }
376 376
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -77,12 +77,12 @@  discard block
 block discarded – undo
77 77
 	public function registerTypeArray(array $types): void {
78 78
 		// Register the types,
79 79
 		foreach ($types as $extension => $mimeType) {
80
-			$this->registerType((string)$extension, $mimeType[0], $mimeType[1] ?? null);
80
+			$this->registerType((string) $extension, $mimeType[0], $mimeType[1] ?? null);
81 81
 		}
82 82
 
83 83
 		// Update the alternative mimeTypes to avoid having to look them up each time.
84 84
 		foreach ($this->mimeTypes as $extension => $mimeType) {
85
-			if (str_starts_with((string)$extension, '_comment')) {
85
+			if (str_starts_with((string) $extension, '_comment')) {
86 86
 				continue;
87 87
 			}
88 88
 
@@ -94,12 +94,12 @@  discard block
 block discarded – undo
94 94
 	}
95 95
 
96 96
 	private function loadCustomDefinitions(string $fileName, array $definitions): array {
97
-		if (file_exists($this->customConfigDir . '/' . $fileName)) {
98
-			$custom = json_decode(file_get_contents($this->customConfigDir . '/' . $fileName), true);
97
+		if (file_exists($this->customConfigDir.'/'.$fileName)) {
98
+			$custom = json_decode(file_get_contents($this->customConfigDir.'/'.$fileName), true);
99 99
 			if (json_last_error() === JSON_ERROR_NONE) {
100 100
 				$definitions = array_replace($definitions, $custom);
101 101
 			} else {
102
-				$this->logger->warning('Failed to parse ' . $fileName . ': ' . json_last_error_msg());
102
+				$this->logger->warning('Failed to parse '.$fileName.': '.json_last_error_msg());
103 103
 			}
104 104
 		}
105 105
 		return $definitions;
@@ -113,7 +113,7 @@  discard block
 block discarded – undo
113 113
 			return;
114 114
 		}
115 115
 
116
-		$this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true);
116
+		$this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir.'/mimetypealiases.dist.json'), true);
117 117
 		$this->mimeTypeAlias = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPEALIASES, $this->mimeTypeAlias);
118 118
 	}
119 119
 
@@ -127,7 +127,7 @@  discard block
 block discarded – undo
127 127
 
128 128
 	public function getOnlyDefaultAliases(): array {
129 129
 		$this->loadMappings();
130
-		$this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypealiases.dist.json'), true);
130
+		$this->mimeTypeAlias = json_decode(file_get_contents($this->defaultConfigDir.'/mimetypealiases.dist.json'), true);
131 131
 		return $this->mimeTypeAlias;
132 132
 	}
133 133
 
@@ -139,7 +139,7 @@  discard block
 block discarded – undo
139 139
 			return;
140 140
 		}
141 141
 
142
-		$mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypemapping.dist.json'), true);
142
+		$mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir.'/mimetypemapping.dist.json'), true);
143 143
 		$mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPEMAPPING, $mimeTypeMapping);
144 144
 
145 145
 		$this->registerTypeArray($mimeTypeMapping);
@@ -158,7 +158,7 @@  discard block
 block discarded – undo
158 158
 			return;
159 159
 		}
160 160
 
161
-		$mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypenames.dist.json'), true);
161
+		$mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir.'/mimetypenames.dist.json'), true);
162 162
 		$mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPENAMES, $mimeTypeMapping);
163 163
 
164 164
 		$this->mimeTypesNames = $mimeTypeMapping;
@@ -353,7 +353,7 @@  discard block
 block discarded – undo
353 353
 
354 354
 		// Icon exists?
355 355
 		try {
356
-			$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/' . $icon . '.svg');
356
+			$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/'.$icon.'.svg');
357 357
 			return $this->mimeTypeIcons[$mimeType];
358 358
 		} catch (\RuntimeException $e) {
359 359
 			// Specified image not found
@@ -363,7 +363,7 @@  discard block
 block discarded – undo
363 363
 		if (strpos($icon, '-')) {
364 364
 			$mimePart = substr($icon, 0, strpos($icon, '-'));
365 365
 			try {
366
-				$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/' . $mimePart . '.svg');
366
+				$this->mimeTypeIcons[$mimeType] = $this->urlGenerator->imagePath('core', 'filetypes/'.$mimePart.'.svg');
367 367
 				return $this->mimeTypeIcons[$mimeType];
368 368
 			} catch (\RuntimeException $e) {
369 369
 				// Image for the first part of the MIME type not found
Please login to merge, or discard this patch.
tests/lib/Files/Type/DetectionTest.php 2 patches
Indentation   +342 added lines, -342 removed lines patch added patch discarded remove patch
@@ -14,394 +14,394 @@
 block discarded – undo
14 14
 use Psr\Log\LoggerInterface;
15 15
 
16 16
 class DetectionTest extends \Test\TestCase {
17
-	/** @var Detection */
18
-	private $detection;
19
-
20
-	protected function setUp(): void {
21
-		parent::setUp();
22
-		$this->detection = new Detection(
23
-			Server::get(IURLGenerator::class),
24
-			Server::get(LoggerInterface::class),
25
-			\OC::$SERVERROOT . '/config/',
26
-			\OC::$SERVERROOT . '/resources/config/'
27
-		);
28
-	}
29
-
30
-	public static function dataDetectPath(): array {
31
-		return [
32
-			['foo.txt', 'text/plain'],
33
-			['foo.png', 'image/png'],
34
-			['foo.bar.png', 'image/png'],
35
-			['.hidden.png', 'image/png'],
36
-			['.hidden.foo.png', 'image/png'],
37
-			['.hidden/foo.png', 'image/png'],
38
-			['.hidden/.hidden.png', 'image/png'],
39
-			['test.jpg/foo.png', 'image/png'],
40
-			['.png', 'application/octet-stream'],
41
-			['..hidden', 'application/octet-stream'],
42
-			['foo', 'application/octet-stream'],
43
-			['', 'application/octet-stream'],
44
-			['foo.png.ocTransferId123456789.part', 'image/png'],
45
-			['foo.png.v1234567890', 'image/png'],
46
-		];
47
-	}
48
-
49
-	/**
50
-	 *
51
-	 * @param string $path
52
-	 * @param string $expected
53
-	 */
54
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataDetectPath')]
55
-	public function testDetectPath(string $path, string $expected): void {
56
-		$this->assertEquals($expected, $this->detection->detectPath($path));
57
-	}
58
-
59
-	public static function dataDetectContent(): array {
60
-		return [
61
-			['/', 'httpd/unix-directory'],
62
-			['/data.tar.gz', 'application/gzip'],
63
-			['/data.zip', 'application/zip'],
64
-			['/testimage.mp3', 'audio/mpeg'],
65
-			['/testimage.png', 'image/png'],
66
-		];
67
-	}
68
-
69
-	/**
70
-	 *
71
-	 * @param string $path
72
-	 * @param string $expected
73
-	 */
74
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataDetectContent')]
75
-	public function testDetectContent(string $path, string $expected): void {
76
-		$this->assertEquals($expected, $this->detection->detectContent(\OC::$SERVERROOT . '/tests/data' . $path));
77
-	}
78
-
79
-	public static function dataDetect(): array {
80
-		return [
81
-			['/', 'httpd/unix-directory'],
82
-			['/data.tar.gz', 'application/gzip'],
83
-			['/data.zip', 'application/zip'],
84
-			['/testimagelarge.svg', 'image/svg+xml'],
85
-			['/testimage.png', 'image/png'],
86
-		];
87
-	}
88
-
89
-	/**
90
-	 *
91
-	 * @param string $path
92
-	 * @param string $expected
93
-	 */
94
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataDetect')]
95
-	public function testDetect(string $path, string $expected): void {
96
-		$this->assertEquals($expected, $this->detection->detect(\OC::$SERVERROOT . '/tests/data' . $path));
97
-	}
98
-
99
-	public function testDetectString(): void {
100
-		$result = $this->detection->detectString('/data/data.tar.gz');
101
-		$expected = 'text/plain';
102
-		$this->assertEquals($expected, $result);
103
-	}
104
-
105
-	public static function dataMimeTypeCustom(): array {
106
-		return [
107
-			['123', 'foobar/123'],
108
-			['a123', 'foobar/123'],
109
-			['bar', 'foobar/bar'],
110
-		];
111
-	}
112
-
113
-	/**
114
-	 *
115
-	 * @param string $ext
116
-	 * @param string $mime
117
-	 */
118
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataMimeTypeCustom')]
119
-	public function testDetectMimeTypeCustom(string $ext, string $mime): void {
120
-		$tmpDir = sys_get_temp_dir() . '/nc-detect-' . uniqid('', true);
121
-		mkdir($tmpDir, 0700);
122
-
123
-		try {
124
-			// Create a default / shipped mapping file (dist)
125
-			file_put_contents($tmpDir . '/mimetypemapping.dist.json', json_encode([]));
126
-
127
-			// Create new custom mapping file that should be picked up by Detection
128
-			file_put_contents($tmpDir . '/mimetypemapping.json', json_encode([$ext => [$mime]]));
129
-
130
-			/** @var IURLGenerator $urlGenerator */
131
-			$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
132
-				->disableOriginalConstructor()
133
-				->getMock();
134
-
135
-			/** @var LoggerInterface $logger */
136
-			$logger = $this->createMock(LoggerInterface::class);
137
-
138
-			$detection = new Detection($urlGenerator, $logger, $tmpDir, $tmpDir);
139
-			$mappings = $detection->getAllMappings();
140
-
141
-			$this->assertArrayHasKey($ext, $mappings);
142
-			$this->assertEquals($mime, $detection->detectPath('foo.' . $ext));
143
-		} finally {
144
-			// cleanup
145
-			@unlink($tmpDir . '/mimetypemapping.json');
146
-			@unlink($tmpDir . '/mimetypemapping.dist.json');
147
-			@rmdir($tmpDir);
148
-		}
149
-	}
150
-
151
-	public function testDetectMimeTypePreservesLeadingZeroKeys(): void {
152
-		$tmpDir = sys_get_temp_dir() . '/nc-detect-' . uniqid();
153
-		mkdir($tmpDir, 0700);
154
-		try {
155
-			// Create a default / shipped mapping file (dist)
156
-			file_put_contents($tmpDir . '/mimetypemapping.dist.json', json_encode([]));
157
-
158
-			// Create new custom mapping file with potentially problematic keys
159
-			$mappings = [
160
-				'001' => ['application/x-zeroone', null],
161
-				'1' => ['application/x-one', null],
162
-			];
163
-			file_put_contents($tmpDir . '/mimetypemapping.json', json_encode($mappings));
164
-
165
-			/** @var IURLGenerator $urlGenerator */
166
-			$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
167
-				->disableOriginalConstructor()
168
-				->getMock();
169
-
170
-			/** @var LoggerInterface $logger */
171
-			$logger = $this->createMock(LoggerInterface::class);
172
-
173
-			$detection = new Detection($urlGenerator, $logger, $tmpDir, $tmpDir);
174
-			$mappings = $detection->getAllMappings();
175
-
176
-			$this->assertArrayHasKey('001', $mappings, 'Expected mapping to contain key "001"');
177
-			$this->assertArrayHasKey('1', $mappings, 'Expected mapping to contain key "1"');
178
-
179
-			$this->assertEquals('application/x-zeroone', $detection->detectPath('foo.001'));
180
-			$this->assertEquals('application/x-one', $detection->detectPath('foo.1'));
181
-		} finally {
182
-			@unlink($tmpDir . '/mimetypemapping.json');
183
-			@unlink($tmpDir . '/mimetypemapping.dist.json');
184
-			@rmdir($tmpDir);
185
-		}
186
-	}
187
-
188
-	public static function dataGetSecureMimeType(): array {
189
-		return [
190
-			['image/svg+xml', 'text/plain'],
191
-			['image/png', 'image/png'],
192
-		];
193
-	}
194
-
195
-	/**
196
-	 *
197
-	 * @param string $mimeType
198
-	 * @param string $expected
199
-	 */
200
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataGetSecureMimeType')]
201
-	public function testGetSecureMimeType(string $mimeType, string $expected): void {
202
-		$this->assertEquals($expected, $this->detection->getSecureMimeType($mimeType));
203
-	}
204
-
205
-	public function testMimeTypeIcon(): void {
206
-		if (!class_exists('org\\bovigo\\vfs\\vfsStream')) {
207
-			$this->markTestSkipped('Package vfsStream not installed');
208
-		}
209
-		$confDir = \org\bovigo\vfs\vfsStream::setup();
210
-		$mimetypealiases_dist = \org\bovigo\vfs\vfsStream::newFile('mimetypealiases.dist.json')->at($confDir);
211
-
212
-		//Empty alias file
213
-		$mimetypealiases_dist->setContent(json_encode([], JSON_FORCE_OBJECT));
214
-
215
-
216
-		/*
17
+    /** @var Detection */
18
+    private $detection;
19
+
20
+    protected function setUp(): void {
21
+        parent::setUp();
22
+        $this->detection = new Detection(
23
+            Server::get(IURLGenerator::class),
24
+            Server::get(LoggerInterface::class),
25
+            \OC::$SERVERROOT . '/config/',
26
+            \OC::$SERVERROOT . '/resources/config/'
27
+        );
28
+    }
29
+
30
+    public static function dataDetectPath(): array {
31
+        return [
32
+            ['foo.txt', 'text/plain'],
33
+            ['foo.png', 'image/png'],
34
+            ['foo.bar.png', 'image/png'],
35
+            ['.hidden.png', 'image/png'],
36
+            ['.hidden.foo.png', 'image/png'],
37
+            ['.hidden/foo.png', 'image/png'],
38
+            ['.hidden/.hidden.png', 'image/png'],
39
+            ['test.jpg/foo.png', 'image/png'],
40
+            ['.png', 'application/octet-stream'],
41
+            ['..hidden', 'application/octet-stream'],
42
+            ['foo', 'application/octet-stream'],
43
+            ['', 'application/octet-stream'],
44
+            ['foo.png.ocTransferId123456789.part', 'image/png'],
45
+            ['foo.png.v1234567890', 'image/png'],
46
+        ];
47
+    }
48
+
49
+    /**
50
+     *
51
+     * @param string $path
52
+     * @param string $expected
53
+     */
54
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataDetectPath')]
55
+    public function testDetectPath(string $path, string $expected): void {
56
+        $this->assertEquals($expected, $this->detection->detectPath($path));
57
+    }
58
+
59
+    public static function dataDetectContent(): array {
60
+        return [
61
+            ['/', 'httpd/unix-directory'],
62
+            ['/data.tar.gz', 'application/gzip'],
63
+            ['/data.zip', 'application/zip'],
64
+            ['/testimage.mp3', 'audio/mpeg'],
65
+            ['/testimage.png', 'image/png'],
66
+        ];
67
+    }
68
+
69
+    /**
70
+     *
71
+     * @param string $path
72
+     * @param string $expected
73
+     */
74
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataDetectContent')]
75
+    public function testDetectContent(string $path, string $expected): void {
76
+        $this->assertEquals($expected, $this->detection->detectContent(\OC::$SERVERROOT . '/tests/data' . $path));
77
+    }
78
+
79
+    public static function dataDetect(): array {
80
+        return [
81
+            ['/', 'httpd/unix-directory'],
82
+            ['/data.tar.gz', 'application/gzip'],
83
+            ['/data.zip', 'application/zip'],
84
+            ['/testimagelarge.svg', 'image/svg+xml'],
85
+            ['/testimage.png', 'image/png'],
86
+        ];
87
+    }
88
+
89
+    /**
90
+     *
91
+     * @param string $path
92
+     * @param string $expected
93
+     */
94
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataDetect')]
95
+    public function testDetect(string $path, string $expected): void {
96
+        $this->assertEquals($expected, $this->detection->detect(\OC::$SERVERROOT . '/tests/data' . $path));
97
+    }
98
+
99
+    public function testDetectString(): void {
100
+        $result = $this->detection->detectString('/data/data.tar.gz');
101
+        $expected = 'text/plain';
102
+        $this->assertEquals($expected, $result);
103
+    }
104
+
105
+    public static function dataMimeTypeCustom(): array {
106
+        return [
107
+            ['123', 'foobar/123'],
108
+            ['a123', 'foobar/123'],
109
+            ['bar', 'foobar/bar'],
110
+        ];
111
+    }
112
+
113
+    /**
114
+     *
115
+     * @param string $ext
116
+     * @param string $mime
117
+     */
118
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataMimeTypeCustom')]
119
+    public function testDetectMimeTypeCustom(string $ext, string $mime): void {
120
+        $tmpDir = sys_get_temp_dir() . '/nc-detect-' . uniqid('', true);
121
+        mkdir($tmpDir, 0700);
122
+
123
+        try {
124
+            // Create a default / shipped mapping file (dist)
125
+            file_put_contents($tmpDir . '/mimetypemapping.dist.json', json_encode([]));
126
+
127
+            // Create new custom mapping file that should be picked up by Detection
128
+            file_put_contents($tmpDir . '/mimetypemapping.json', json_encode([$ext => [$mime]]));
129
+
130
+            /** @var IURLGenerator $urlGenerator */
131
+            $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
132
+                ->disableOriginalConstructor()
133
+                ->getMock();
134
+
135
+            /** @var LoggerInterface $logger */
136
+            $logger = $this->createMock(LoggerInterface::class);
137
+
138
+            $detection = new Detection($urlGenerator, $logger, $tmpDir, $tmpDir);
139
+            $mappings = $detection->getAllMappings();
140
+
141
+            $this->assertArrayHasKey($ext, $mappings);
142
+            $this->assertEquals($mime, $detection->detectPath('foo.' . $ext));
143
+        } finally {
144
+            // cleanup
145
+            @unlink($tmpDir . '/mimetypemapping.json');
146
+            @unlink($tmpDir . '/mimetypemapping.dist.json');
147
+            @rmdir($tmpDir);
148
+        }
149
+    }
150
+
151
+    public function testDetectMimeTypePreservesLeadingZeroKeys(): void {
152
+        $tmpDir = sys_get_temp_dir() . '/nc-detect-' . uniqid();
153
+        mkdir($tmpDir, 0700);
154
+        try {
155
+            // Create a default / shipped mapping file (dist)
156
+            file_put_contents($tmpDir . '/mimetypemapping.dist.json', json_encode([]));
157
+
158
+            // Create new custom mapping file with potentially problematic keys
159
+            $mappings = [
160
+                '001' => ['application/x-zeroone', null],
161
+                '1' => ['application/x-one', null],
162
+            ];
163
+            file_put_contents($tmpDir . '/mimetypemapping.json', json_encode($mappings));
164
+
165
+            /** @var IURLGenerator $urlGenerator */
166
+            $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
167
+                ->disableOriginalConstructor()
168
+                ->getMock();
169
+
170
+            /** @var LoggerInterface $logger */
171
+            $logger = $this->createMock(LoggerInterface::class);
172
+
173
+            $detection = new Detection($urlGenerator, $logger, $tmpDir, $tmpDir);
174
+            $mappings = $detection->getAllMappings();
175
+
176
+            $this->assertArrayHasKey('001', $mappings, 'Expected mapping to contain key "001"');
177
+            $this->assertArrayHasKey('1', $mappings, 'Expected mapping to contain key "1"');
178
+
179
+            $this->assertEquals('application/x-zeroone', $detection->detectPath('foo.001'));
180
+            $this->assertEquals('application/x-one', $detection->detectPath('foo.1'));
181
+        } finally {
182
+            @unlink($tmpDir . '/mimetypemapping.json');
183
+            @unlink($tmpDir . '/mimetypemapping.dist.json');
184
+            @rmdir($tmpDir);
185
+        }
186
+    }
187
+
188
+    public static function dataGetSecureMimeType(): array {
189
+        return [
190
+            ['image/svg+xml', 'text/plain'],
191
+            ['image/png', 'image/png'],
192
+        ];
193
+    }
194
+
195
+    /**
196
+     *
197
+     * @param string $mimeType
198
+     * @param string $expected
199
+     */
200
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataGetSecureMimeType')]
201
+    public function testGetSecureMimeType(string $mimeType, string $expected): void {
202
+        $this->assertEquals($expected, $this->detection->getSecureMimeType($mimeType));
203
+    }
204
+
205
+    public function testMimeTypeIcon(): void {
206
+        if (!class_exists('org\\bovigo\\vfs\\vfsStream')) {
207
+            $this->markTestSkipped('Package vfsStream not installed');
208
+        }
209
+        $confDir = \org\bovigo\vfs\vfsStream::setup();
210
+        $mimetypealiases_dist = \org\bovigo\vfs\vfsStream::newFile('mimetypealiases.dist.json')->at($confDir);
211
+
212
+        //Empty alias file
213
+        $mimetypealiases_dist->setContent(json_encode([], JSON_FORCE_OBJECT));
214
+
215
+
216
+        /*
217 217
 		 * Test dir mimetype
218 218
 		 */
219 219
 
220
-		//Mock UrlGenerator
221
-		$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
222
-			->disableOriginalConstructor()
223
-			->getMock();
220
+        //Mock UrlGenerator
221
+        $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
222
+            ->disableOriginalConstructor()
223
+            ->getMock();
224 224
 
225
-		/** @var LoggerInterface $logger */
226
-		$logger = $this->createMock(LoggerInterface::class);
225
+        /** @var LoggerInterface $logger */
226
+        $logger = $this->createMock(LoggerInterface::class);
227 227
 
228
-		//Only call the url generator once
229
-		$urlGenerator->expects($this->once())
230
-			->method('imagePath')
231
-			->with($this->equalTo('core'), $this->equalTo('filetypes/folder.png'))
232
-			->willReturn('folder.svg');
228
+        //Only call the url generator once
229
+        $urlGenerator->expects($this->once())
230
+            ->method('imagePath')
231
+            ->with($this->equalTo('core'), $this->equalTo('filetypes/folder.png'))
232
+            ->willReturn('folder.svg');
233 233
 
234
-		$detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
235
-		$mimeType = $detection->mimeTypeIcon('dir');
236
-		$this->assertEquals('folder.svg', $mimeType);
234
+        $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
235
+        $mimeType = $detection->mimeTypeIcon('dir');
236
+        $this->assertEquals('folder.svg', $mimeType);
237 237
 
238 238
 
239
-		/*
239
+        /*
240 240
 		 * Test dir-shareed mimetype
241 241
 		 */
242
-		//Mock UrlGenerator
243
-		$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
244
-			->disableOriginalConstructor()
245
-			->getMock();
242
+        //Mock UrlGenerator
243
+        $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
244
+            ->disableOriginalConstructor()
245
+            ->getMock();
246 246
 
247
-		//Only call the url generator once
248
-		$urlGenerator->expects($this->once())
249
-			->method('imagePath')
250
-			->with($this->equalTo('core'), $this->equalTo('filetypes/folder-shared.png'))
251
-			->willReturn('folder-shared.svg');
247
+        //Only call the url generator once
248
+        $urlGenerator->expects($this->once())
249
+            ->method('imagePath')
250
+            ->with($this->equalTo('core'), $this->equalTo('filetypes/folder-shared.png'))
251
+            ->willReturn('folder-shared.svg');
252 252
 
253
-		$detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
254
-		$mimeType = $detection->mimeTypeIcon('dir-shared');
255
-		$this->assertEquals('folder-shared.svg', $mimeType);
253
+        $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
254
+        $mimeType = $detection->mimeTypeIcon('dir-shared');
255
+        $this->assertEquals('folder-shared.svg', $mimeType);
256 256
 
257 257
 
258
-		/*
258
+        /*
259 259
 		 * Test dir external
260 260
 		 */
261 261
 
262
-		//Mock UrlGenerator
263
-		$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
264
-			->disableOriginalConstructor()
265
-			->getMock();
262
+        //Mock UrlGenerator
263
+        $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
264
+            ->disableOriginalConstructor()
265
+            ->getMock();
266 266
 
267
-		//Only call the url generator once
268
-		$urlGenerator->expects($this->once())
269
-			->method('imagePath')
270
-			->with($this->equalTo('core'), $this->equalTo('filetypes/folder-external.png'))
271
-			->willReturn('folder-external.svg');
267
+        //Only call the url generator once
268
+        $urlGenerator->expects($this->once())
269
+            ->method('imagePath')
270
+            ->with($this->equalTo('core'), $this->equalTo('filetypes/folder-external.png'))
271
+            ->willReturn('folder-external.svg');
272 272
 
273
-		$detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
274
-		$mimeType = $detection->mimeTypeIcon('dir-external');
275
-		$this->assertEquals('folder-external.svg', $mimeType);
273
+        $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
274
+        $mimeType = $detection->mimeTypeIcon('dir-external');
275
+        $this->assertEquals('folder-external.svg', $mimeType);
276 276
 
277 277
 
278
-		/*
278
+        /*
279 279
 		 * Test complete mimetype
280 280
 		 */
281 281
 
282
-		//Mock UrlGenerator
283
-		$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
284
-			->disableOriginalConstructor()
285
-			->getMock();
282
+        //Mock UrlGenerator
283
+        $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
284
+            ->disableOriginalConstructor()
285
+            ->getMock();
286 286
 
287
-		//Only call the url generator once
288
-		$urlGenerator->expects($this->once())
289
-			->method('imagePath')
290
-			->with($this->equalTo('core'), $this->equalTo('filetypes/my-type.png'))
291
-			->willReturn('my-type.svg');
287
+        //Only call the url generator once
288
+        $urlGenerator->expects($this->once())
289
+            ->method('imagePath')
290
+            ->with($this->equalTo('core'), $this->equalTo('filetypes/my-type.png'))
291
+            ->willReturn('my-type.svg');
292 292
 
293
-		$detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
294
-		$mimeType = $detection->mimeTypeIcon('my-type');
295
-		$this->assertEquals('my-type.svg', $mimeType);
293
+        $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
294
+        $mimeType = $detection->mimeTypeIcon('my-type');
295
+        $this->assertEquals('my-type.svg', $mimeType);
296 296
 
297 297
 
298
-		/*
298
+        /*
299 299
 		 * Test subtype
300 300
 		 */
301 301
 
302
-		//Mock UrlGenerator
303
-		$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
304
-			->disableOriginalConstructor()
305
-			->getMock();
306
-
307
-		//Only call the url generator once
308
-		$calls = [
309
-			['core', 'filetypes/my-type.png'],
310
-			['core', 'filetypes/my.png'],
311
-		];
312
-		$urlGenerator->expects($this->exactly(2))
313
-			->method('imagePath')
314
-			->willReturnCallback(
315
-				function ($appName, $file) use (&$calls) {
316
-					$expected = array_shift($calls);
317
-					$this->assertEquals($expected, [$appName, $file]);
318
-					if ($file === 'filetypes/my.png') {
319
-						return 'my.svg';
320
-					}
321
-					throw new \RuntimeException();
322
-				}
323
-			);
324
-
325
-		$detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
326
-		$mimeType = $detection->mimeTypeIcon('my-type');
327
-		$this->assertEquals('my.svg', $mimeType);
328
-
329
-
330
-		/*
302
+        //Mock UrlGenerator
303
+        $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
304
+            ->disableOriginalConstructor()
305
+            ->getMock();
306
+
307
+        //Only call the url generator once
308
+        $calls = [
309
+            ['core', 'filetypes/my-type.png'],
310
+            ['core', 'filetypes/my.png'],
311
+        ];
312
+        $urlGenerator->expects($this->exactly(2))
313
+            ->method('imagePath')
314
+            ->willReturnCallback(
315
+                function ($appName, $file) use (&$calls) {
316
+                    $expected = array_shift($calls);
317
+                    $this->assertEquals($expected, [$appName, $file]);
318
+                    if ($file === 'filetypes/my.png') {
319
+                        return 'my.svg';
320
+                    }
321
+                    throw new \RuntimeException();
322
+                }
323
+            );
324
+
325
+        $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
326
+        $mimeType = $detection->mimeTypeIcon('my-type');
327
+        $this->assertEquals('my.svg', $mimeType);
328
+
329
+
330
+        /*
331 331
 		 * Test default mimetype
332 332
 		 */
333 333
 
334
-		//Mock UrlGenerator
335
-		$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
336
-			->disableOriginalConstructor()
337
-			->getMock();
338
-
339
-		//Only call the url generator once
340
-		$calls = [
341
-			['core', 'filetypes/foo-bar.png'],
342
-			['core', 'filetypes/foo.png'],
343
-			['core', 'filetypes/file.png'],
344
-		];
345
-		$urlGenerator->expects($this->exactly(3))
346
-			->method('imagePath')
347
-			->willReturnCallback(
348
-				function ($appName, $file) use (&$calls) {
349
-					$expected = array_shift($calls);
350
-					$this->assertEquals($expected, [$appName, $file]);
351
-					if ($file === 'filetypes/file.png') {
352
-						return 'file.svg';
353
-					}
354
-					throw new \RuntimeException();
355
-				}
356
-			);
357
-
358
-		$detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
359
-		$mimeType = $detection->mimeTypeIcon('foo-bar');
360
-		$this->assertEquals('file.svg', $mimeType);
361
-
362
-		/*
334
+        //Mock UrlGenerator
335
+        $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
336
+            ->disableOriginalConstructor()
337
+            ->getMock();
338
+
339
+        //Only call the url generator once
340
+        $calls = [
341
+            ['core', 'filetypes/foo-bar.png'],
342
+            ['core', 'filetypes/foo.png'],
343
+            ['core', 'filetypes/file.png'],
344
+        ];
345
+        $urlGenerator->expects($this->exactly(3))
346
+            ->method('imagePath')
347
+            ->willReturnCallback(
348
+                function ($appName, $file) use (&$calls) {
349
+                    $expected = array_shift($calls);
350
+                    $this->assertEquals($expected, [$appName, $file]);
351
+                    if ($file === 'filetypes/file.png') {
352
+                        return 'file.svg';
353
+                    }
354
+                    throw new \RuntimeException();
355
+                }
356
+            );
357
+
358
+        $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
359
+        $mimeType = $detection->mimeTypeIcon('foo-bar');
360
+        $this->assertEquals('file.svg', $mimeType);
361
+
362
+        /*
363 363
 		 * Test chaching
364 364
 		 */
365 365
 
366
-		//Mock UrlGenerator
367
-		$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
368
-			->disableOriginalConstructor()
369
-			->getMock();
366
+        //Mock UrlGenerator
367
+        $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
368
+            ->disableOriginalConstructor()
369
+            ->getMock();
370 370
 
371
-		//Only call the url generator once
372
-		$urlGenerator->expects($this->once())
373
-			->method('imagePath')
374
-			->with($this->equalTo('core'), $this->equalTo('filetypes/foo-bar.png'))
375
-			->willReturn('foo-bar.svg');
371
+        //Only call the url generator once
372
+        $urlGenerator->expects($this->once())
373
+            ->method('imagePath')
374
+            ->with($this->equalTo('core'), $this->equalTo('filetypes/foo-bar.png'))
375
+            ->willReturn('foo-bar.svg');
376 376
 
377
-		$detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
378
-		$mimeType = $detection->mimeTypeIcon('foo-bar');
379
-		$this->assertEquals('foo-bar.svg', $mimeType);
380
-		$mimeType = $detection->mimeTypeIcon('foo-bar');
381
-		$this->assertEquals('foo-bar.svg', $mimeType);
377
+        $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
378
+        $mimeType = $detection->mimeTypeIcon('foo-bar');
379
+        $this->assertEquals('foo-bar.svg', $mimeType);
380
+        $mimeType = $detection->mimeTypeIcon('foo-bar');
381
+        $this->assertEquals('foo-bar.svg', $mimeType);
382 382
 
383 383
 
384 384
 
385
-		/*
385
+        /*
386 386
 		 * Test aliases
387 387
 		 */
388 388
 
389
-		//Put alias
390
-		$mimetypealiases_dist->setContent(json_encode(['foo' => 'foobar/baz'], JSON_FORCE_OBJECT));
389
+        //Put alias
390
+        $mimetypealiases_dist->setContent(json_encode(['foo' => 'foobar/baz'], JSON_FORCE_OBJECT));
391 391
 
392
-		//Mock UrlGenerator
393
-		$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
394
-			->disableOriginalConstructor()
395
-			->getMock();
392
+        //Mock UrlGenerator
393
+        $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
394
+            ->disableOriginalConstructor()
395
+            ->getMock();
396 396
 
397
-		//Only call the url generator once
398
-		$urlGenerator->expects($this->once())
399
-			->method('imagePath')
400
-			->with($this->equalTo('core'), $this->equalTo('filetypes/foobar-baz.png'))
401
-			->willReturn('foobar-baz.svg');
397
+        //Only call the url generator once
398
+        $urlGenerator->expects($this->once())
399
+            ->method('imagePath')
400
+            ->with($this->equalTo('core'), $this->equalTo('filetypes/foobar-baz.png'))
401
+            ->willReturn('foobar-baz.svg');
402 402
 
403
-		$detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
404
-		$mimeType = $detection->mimeTypeIcon('foo');
405
-		$this->assertEquals('foobar-baz.svg', $mimeType);
406
-	}
403
+        $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
404
+        $mimeType = $detection->mimeTypeIcon('foo');
405
+        $this->assertEquals('foobar-baz.svg', $mimeType);
406
+    }
407 407
 }
Please login to merge, or discard this patch.
Spacing   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -22,8 +22,8 @@  discard block
 block discarded – undo
22 22
 		$this->detection = new Detection(
23 23
 			Server::get(IURLGenerator::class),
24 24
 			Server::get(LoggerInterface::class),
25
-			\OC::$SERVERROOT . '/config/',
26
-			\OC::$SERVERROOT . '/resources/config/'
25
+			\OC::$SERVERROOT.'/config/',
26
+			\OC::$SERVERROOT.'/resources/config/'
27 27
 		);
28 28
 	}
29 29
 
@@ -73,7 +73,7 @@  discard block
 block discarded – undo
73 73
 	 */
74 74
 	#[\PHPUnit\Framework\Attributes\DataProvider('dataDetectContent')]
75 75
 	public function testDetectContent(string $path, string $expected): void {
76
-		$this->assertEquals($expected, $this->detection->detectContent(\OC::$SERVERROOT . '/tests/data' . $path));
76
+		$this->assertEquals($expected, $this->detection->detectContent(\OC::$SERVERROOT.'/tests/data'.$path));
77 77
 	}
78 78
 
79 79
 	public static function dataDetect(): array {
@@ -93,7 +93,7 @@  discard block
 block discarded – undo
93 93
 	 */
94 94
 	#[\PHPUnit\Framework\Attributes\DataProvider('dataDetect')]
95 95
 	public function testDetect(string $path, string $expected): void {
96
-		$this->assertEquals($expected, $this->detection->detect(\OC::$SERVERROOT . '/tests/data' . $path));
96
+		$this->assertEquals($expected, $this->detection->detect(\OC::$SERVERROOT.'/tests/data'.$path));
97 97
 	}
98 98
 
99 99
 	public function testDetectString(): void {
@@ -117,15 +117,15 @@  discard block
 block discarded – undo
117 117
 	 */
118 118
 	#[\PHPUnit\Framework\Attributes\DataProvider('dataMimeTypeCustom')]
119 119
 	public function testDetectMimeTypeCustom(string $ext, string $mime): void {
120
-		$tmpDir = sys_get_temp_dir() . '/nc-detect-' . uniqid('', true);
120
+		$tmpDir = sys_get_temp_dir().'/nc-detect-'.uniqid('', true);
121 121
 		mkdir($tmpDir, 0700);
122 122
 
123 123
 		try {
124 124
 			// Create a default / shipped mapping file (dist)
125
-			file_put_contents($tmpDir . '/mimetypemapping.dist.json', json_encode([]));
125
+			file_put_contents($tmpDir.'/mimetypemapping.dist.json', json_encode([]));
126 126
 
127 127
 			// Create new custom mapping file that should be picked up by Detection
128
-			file_put_contents($tmpDir . '/mimetypemapping.json', json_encode([$ext => [$mime]]));
128
+			file_put_contents($tmpDir.'/mimetypemapping.json', json_encode([$ext => [$mime]]));
129 129
 
130 130
 			/** @var IURLGenerator $urlGenerator */
131 131
 			$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
@@ -139,28 +139,28 @@  discard block
 block discarded – undo
139 139
 			$mappings = $detection->getAllMappings();
140 140
 
141 141
 			$this->assertArrayHasKey($ext, $mappings);
142
-			$this->assertEquals($mime, $detection->detectPath('foo.' . $ext));
142
+			$this->assertEquals($mime, $detection->detectPath('foo.'.$ext));
143 143
 		} finally {
144 144
 			// cleanup
145
-			@unlink($tmpDir . '/mimetypemapping.json');
146
-			@unlink($tmpDir . '/mimetypemapping.dist.json');
145
+			@unlink($tmpDir.'/mimetypemapping.json');
146
+			@unlink($tmpDir.'/mimetypemapping.dist.json');
147 147
 			@rmdir($tmpDir);
148 148
 		}
149 149
 	}
150 150
 
151 151
 	public function testDetectMimeTypePreservesLeadingZeroKeys(): void {
152
-		$tmpDir = sys_get_temp_dir() . '/nc-detect-' . uniqid();
152
+		$tmpDir = sys_get_temp_dir().'/nc-detect-'.uniqid();
153 153
 		mkdir($tmpDir, 0700);
154 154
 		try {
155 155
 			// Create a default / shipped mapping file (dist)
156
-			file_put_contents($tmpDir . '/mimetypemapping.dist.json', json_encode([]));
156
+			file_put_contents($tmpDir.'/mimetypemapping.dist.json', json_encode([]));
157 157
 
158 158
 			// Create new custom mapping file with potentially problematic keys
159 159
 			$mappings = [
160 160
 				'001' => ['application/x-zeroone', null],
161 161
 				'1' => ['application/x-one', null],
162 162
 			];
163
-			file_put_contents($tmpDir . '/mimetypemapping.json', json_encode($mappings));
163
+			file_put_contents($tmpDir.'/mimetypemapping.json', json_encode($mappings));
164 164
 
165 165
 			/** @var IURLGenerator $urlGenerator */
166 166
 			$urlGenerator = $this->getMockBuilder(IURLGenerator::class)
@@ -179,8 +179,8 @@  discard block
 block discarded – undo
179 179
 			$this->assertEquals('application/x-zeroone', $detection->detectPath('foo.001'));
180 180
 			$this->assertEquals('application/x-one', $detection->detectPath('foo.1'));
181 181
 		} finally {
182
-			@unlink($tmpDir . '/mimetypemapping.json');
183
-			@unlink($tmpDir . '/mimetypemapping.dist.json');
182
+			@unlink($tmpDir.'/mimetypemapping.json');
183
+			@unlink($tmpDir.'/mimetypemapping.dist.json');
184 184
 			@rmdir($tmpDir);
185 185
 		}
186 186
 	}
@@ -312,7 +312,7 @@  discard block
 block discarded – undo
312 312
 		$urlGenerator->expects($this->exactly(2))
313 313
 			->method('imagePath')
314 314
 			->willReturnCallback(
315
-				function ($appName, $file) use (&$calls) {
315
+				function($appName, $file) use (&$calls) {
316 316
 					$expected = array_shift($calls);
317 317
 					$this->assertEquals($expected, [$appName, $file]);
318 318
 					if ($file === 'filetypes/my.png') {
@@ -345,7 +345,7 @@  discard block
 block discarded – undo
345 345
 		$urlGenerator->expects($this->exactly(3))
346 346
 			->method('imagePath')
347 347
 			->willReturnCallback(
348
-				function ($appName, $file) use (&$calls) {
348
+				function($appName, $file) use (&$calls) {
349 349
 					$expected = array_shift($calls);
350 350
 					$this->assertEquals($expected, [$appName, $file]);
351 351
 					if ($file === 'filetypes/file.png') {
Please login to merge, or discard this patch.