Passed
Push — master ( 7063d6...9e7aeb )
by Matias
04:08
created

FileService::bunzip2()   B

Complexity

Conditions 11
Paths 7

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 13.594

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 17
c 1
b 0
f 0
nc 7
nop 2
dl 0
loc 25
ccs 13
cts 18
cp 0.7221
crap 13.594
rs 7.3166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * @copyright Copyright (c) 2019-2023 Matias De lellis <[email protected]>
6
 *
7
 * @author Matias De lellis <[email protected]>
8
 *
9
 * @license GNU AGPL version 3 or any later version
10
 *
11
 * This program is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License as
13
 * published by the Free Software Foundation, either version 3 of the
14
 * License, or (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License
22
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
 *
24
 */
25
26
namespace OCA\FaceRecognition\Service;
27
28
use OCP\Files\IRootFolder;
29
use OCP\Files\File;
30
use OCP\Files\Folder;
31
use OCP\Files\Node;
32
use OCP\ITempManager;
33
34
use OCP\Files\IHomeStorage;
35
use OCP\Files\NotFoundException;
36
use OCP\Files\StorageNotAvailableException;
37
38
use OCA\Files_Sharing\External\Storage as SharingExternalStorage;
0 ignored issues
show
Bug introduced by
The type OCA\Files_Sharing\External\Storage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
39
40
use OCA\FaceRecognition\Service\SettingsService;
41
42
class FileService {
43
44
	const NOMEDIA_FILE = ".nomedia";
45
46
	const NOIMAGE_FILE = ".noimage";
47
48
	const FACERECOGNITION_SETTINGS_FILE = ".facerecognition.json";
49
50
	/**  @var string|null */
51
	private $userId;
52
53
	/** @var IRootFolder */
54
	private $rootFolder;
55
56
	/** @var ITempManager */
57
	private $tempManager;
58
59
	/** @var SettingsService */
60
	private $settingsService;
61
62 1
	public function __construct($userId,
63
	                            IRootFolder     $rootFolder,
64
	                            ITempManager    $tempManager,
65
	                            SettingsService $settingsService)
66
	{
67 1
		$this->userId          = $userId;
68 1
		$this->rootFolder      = $rootFolder;
69 1
		$this->tempManager     = $tempManager;
70 1
		$this->settingsService = $settingsService;
71
	}
72
73
	/**
74
	 * TODO: Describe exactly when necessary.
75
	 *
76
	 * @return void
77
	 */
78 11
	public function setupFS(string $userId): void {
79 11
		\OC_Util::tearDownFS();
0 ignored issues
show
Bug introduced by
The type OC_Util was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
80 11
		\OC_Util::setupFS($userId);
81
82 11
		$this->userId = $userId;
83
	}
84
85
	/**
86
	 * Get root user folder
87
	 * @param string $userId
88
	 * @return Folder
89
	 */
90 10
	public function getUserFolder($userId = null): Folder {
91 10
		return $this->rootFolder->getUserFolder($this->userId ?? $userId);
92
	}
93
94
	/**
95
	 * Get a Node from userFolder
96
	 * @param int $id the id of the Node
97
	 * @param string $userId
98
	 * @return Node | null
99
	 */
100 6
	public function getFileById(int $fileId, $userId = null): ?Node {
101 6
		$files = $this->rootFolder->getUserFolder($this->userId ?? $userId)->getById($fileId);
102 6
		if (count($files) === 0) {
103 1
			return null;
104
		}
105
106 5
		return $files[0];
107
	}
108
109
	/**
110
	 * Get a Node from userFolder
111
	 * @param string $fullpath the fullpath of the Node
112
	 * @param string $userId
113
	 * @return Node | null
114
	 */
115
	public function getFileByPath($fullpath, $userId = null): ?Node {
116
		$file = $this->rootFolder->getUserFolder($this->userId ?? $userId)->get($fullpath);
117
		return $file;
118
	}
119
120
	/**
121
	 * Checks if this file is located somewhere under .nomedia file and should be therefore ignored.
122
	 * Or with an .facerecognition.json setting file that disable tha analysis
123
	 *
124
	 * @param File $file File to search for
125
	 * @return bool True if file is located under .nomedia or .facerecognition.json that disabled
126
	 * analysis, false otherwise
127
	 */
128 1
	public function isUnderNoDetection(Node $node): bool {
129
		// If we detect .nomedia file anywhere on the path to root folder (id===null), bail out
130 1
		$parentNode = $node->getParent();
131 1
		while (($parentNode instanceof Folder) && ($parentNode->getId() !== null)) {
132 1
			$allowDetection = $this->getDescendantDetection($parentNode);
133 1
			if (!$allowDetection)
134 1
				return true;
135
136
			try {
137 1
				$parentNode = $parentNode->getParent();
138 1
			} catch (NotFoundException $e) {
139 1
				return false;
140
			}
141
		}
142
		return false;
143
	}
144
145
	/**
146
	 * Checks if this folder has .nomedia file an .facerecognition.json setting file that
147
	 * disable that analysis.
148
	 *
149
	 * @param Folder $folder Folder to search for
150
	 * @return bool true if folder dont have an .nomedia file or .facerecognition.json that disabled
151
	 * analysis, false otherwise
152
	 */
153 3
	public function getDescendantDetection(Folder $folder): bool {
154
		try {
155 3
			if ($folder->nodeExists(FileService::NOMEDIA_FILE) ||
156 3
			    $folder->nodeExists(FileService::NOIMAGE_FILE)) {
157 2
				return false;
158
			}
159
160 3
			if (!$folder->nodeExists(FileService::FACERECOGNITION_SETTINGS_FILE)) {
161 3
				return true;
162
			}
163
164
			$file = $folder->get(FileService::FACERECOGNITION_SETTINGS_FILE);
165
			if (!($file instanceof File)) // Maybe the node exists but it can be a folder.
166
				return true;
167
168
			$settings = json_decode($file->getContent(), true);
169
			if ($settings === null || !array_key_exists('detection', $settings)) {
170
				return true;
171
			}
172
173
			if ($settings['detection'] === 'off') {
174
				return false;
175
			}
176
		} catch (StorageNotAvailableException $e) {
177
			return false;
178
		}
179
180
		return true;
181
	}
182
183
	/**
184
	 * Set this folder to enable or disable the analysis using the .facerecognition.json file.
185
	 *
186
	 * @param Folder $folder Folder to enable/disable for
187
	 * @return bool true if the change is done. False if failed.
188
	 */
189
	public function setDescendantDetection(Folder $folder, bool $detection): bool {
190
		if ($folder->nodeExists(FileService::FACERECOGNITION_SETTINGS_FILE)) {
191
			$file = $folder->get(FileService::FACERECOGNITION_SETTINGS_FILE);
192
			if (!($file instanceof File)) // Maybe the node exists but it can be a folder.
193
				return false;
194
195
			$settings = json_decode($file->getContent(), true);
196
			if ($settings === null) // Invalid json
197
				return false;
198
		}
199
		else {
200
			$file = $folder->newFile(FileService::FACERECOGNITION_SETTINGS_FILE);
201
			$settings = array();
202
		}
203
204
		$settings['detection'] = $detection ? "on" : "off";
205
		$file->putContent(json_encode($settings));
206
207
		return true;
208
	}
209
210
	/**
211
	 * Returns if the file is inside a native home storage.
212
	 */
213 8
	public function isUserFile(Node $node): bool {
214 8
		return (($node->getMountPoint()->getMountType() === '') &&
215 8
		        ($node->getStorage()->instanceOfStorage(IHomeStorage::class)));
216
	}
217
218
	/**
219
	 * Returns if the file is inside a shared mount storage.
220
	 */
221
	public function isSharedFile(Node $node): bool {
222
		return ($node->getMountPoint()->getMountType() === 'shared');
223
	}
224
225
	/**
226
	 * Returns if the file is inside a external mount storage.
227
	 */
228
	public function isExternalFile(Node $node): bool {
229
		return ($node->getMountPoint()->getMountType() === 'external');
230
	}
231
232
	/**
233
	 * Returns if the file is inside a group folder storage.
234
	 */
235
	public function isGroupFile(Node $node): bool {
236
		return ($node->getMountPoint()->getMountType() === 'group');
237
	}
238
239
	/**
240
	 * Returns if the Node is allowed based on preferences.
241
	 */
242 8
	public function isAllowedNode(Node $node): bool {
243 8
		if ($this->isUserFile($node)) {
244 8
			return true;
245
		} else if ($this->isSharedFile($node)) {
246
			return $this->settingsService->getHandleSharedFiles();
247
		} else if ($this->isExternalFile($node)) {
248
			return $this->settingsService->getHandleExternalFiles();
249
		} else if ($this->isGroupFile($node)) {
250
			return $this->settingsService->getHandleGroupFiles();
251
		}
252
		return false;
253
	}
254
255
	/**
256
	 * Get a path to either the local file or temporary file
257
	 *
258
	 * @param File $file
259
	 * @param int $maxSize maximum size for temporary files
260
	 * @return string|null
261
	 */
262 4
	public function getLocalFile(File $file, int $maxSize = null): ?string {
263 4
		$useTempFile = $file->isEncrypted() || !$file->getStorage()->isLocal();
264 4
		if ($useTempFile) {
265
			$absPath = $this->tempManager->getTemporaryFile();
266
267
			$content = $file->fopen('r');
268
			if ($maxSize !== null) {
269
				$content = stream_get_contents($content, $maxSize);
270
			}
271
			file_put_contents($absPath, $content);
272
273
			return $absPath;
274
		} else {
275 4
			$localPath = $file->getStorage()->getLocalFile($file->getInternalPath());
276 4
			return ($localPath !== false) ? $localPath : null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $localPath !== false ? $localPath : null could return the type true which is incompatible with the type-hinted return null|string. Consider adding an additional type-check to rule them out.
Loading history...
277
		}
278
	}
279
280
	/**
281
	 * Return all images from a given folder.
282
	 *
283
	 * TODO: It is inefficient since it copies the array recursively.
284
	 *
285
	 * @param Folder $folder Folder to get images from
286
	 * @return array List of all images and folders to continue recursive crawling
287
	 */
288 10
	public function getPicturesFromFolder(Folder $folder, $results = array()) {
289 10
		$nodes = $folder->getDirectoryListing();
290 10
		foreach ($nodes as $node) {
291 8
			if (!$this->isAllowedNode($node)) {
292
				continue;
293
			}
294 8
			if ($node instanceof Folder && $this->getDescendantDetection($node)) {
295 3
				$results = $this->getPicturesFromFolder($node, $results);
296
			}
297 8
			else if ($node instanceof File) {
298 8
				if ($this->settingsService->isAllowedMimetype($node->getMimeType())) {
299 7
					$results[] = $node;
300
				}
301
			}
302
		}
303 10
		return $results;
304
	}
305
306
	/**
307
	 * Create a temporary file and return the path
308
	 */
309
	public function getTemporaryFile(string $postFix = ''): string {
310
		return $this->tempManager->getTemporaryFile($postFix);
311
	}
312
313
	/**
314
	 * Remove any temporary file from the service.
315
	 *
316
	 * @return void
317
	 */
318 4
	public function clean(): void {
319 4
		$this->tempManager->clean();
320
	}
321
322
}
323