Completed
Push — master ( b8ecd8...271187 )
by Maxence
05:41 queued 03:40
created

FilesService::getChunksFromDirectory()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.2728
c 0
b 0
f 0
cc 5
nc 6
nop 3
1
<?php
2
declare(strict_types=1);
3
4
5
/**
6
 * Files_FullTextSearch - Index the content of your files
7
 *
8
 * This file is licensed under the Affero General Public License version 3 or
9
 * later. See the COPYING file.
10
 *
11
 * @author Maxence Lange <[email protected]>
12
 * @copyright 2018
13
 * @license GNU AGPL version 3 or any later version
14
 *
15
 * This program is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License as
17
 * published by the Free Software Foundation, either version 3 of the
18
 * License, or (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
27
 *
28
 */
29
30
31
namespace OCA\Files_FullTextSearch\Service;
32
33
34
use daita\MySmallPhpTools\Traits\TPathTools;
35
use Exception;
36
use OCA\Files_FullTextSearch\Exceptions\EmptyUserException;
37
use OCA\Files_FullTextSearch\Exceptions\FileIsNotIndexableException;
38
use OCA\Files_FullTextSearch\Exceptions\FilesNotFoundException;
39
use OCA\Files_FullTextSearch\Exceptions\KnownFileMimeTypeException;
40
use OCA\Files_FullTextSearch\Exceptions\KnownFileSourceException;
41
use OCA\Files_FullTextSearch\Model\FilesDocument;
42
use OCA\Files_FullTextSearch\Provider\FilesProvider;
43
use OCP\App\IAppManager;
44
use OCP\AppFramework\IAppContainer;
45
use OCP\Files\File;
46
use OCP\Files\FileInfo;
47
use OCP\Files\Folder;
48
use OCP\Files\InvalidPathException;
49
use OCP\Files\IRootFolder;
50
use OCP\Files\Node;
51
use OCP\Files\NotFoundException;
52
use OCP\Files\NotPermittedException;
53
use OCP\Files\StorageNotAvailableException;
54
use OCP\FullTextSearch\IFullTextSearchManager;
55
use OCP\FullTextSearch\Model\DocumentAccess;
56
use OCP\FullTextSearch\Model\IIndex;
57
use OCP\FullTextSearch\Model\IIndexOptions;
58
use OCP\FullTextSearch\Model\IndexDocument;
59
use OCP\FullTextSearch\Model\IRunner;
60
use OCP\IUserManager;
61
use OCP\Share\IManager;
62
use Throwable;
63
64
65
/**
66
 * Class FilesService
67
 *
68
 * @package OCA\Files_FullTextSearch\Service
69
 */
70
class FilesService {
71
72
73
	use TPathTools;
74
75
76
	const MIMETYPE_TEXT = 'files_text';
77
	const MIMETYPE_PDF = 'files_pdf';
78
	const MIMETYPE_OFFICE = 'files_office';
79
	const MIMETYPE_ZIP = 'files_zip';
80
	const MIMETYPE_IMAGE = 'files_image';
81
	const MIMETYPE_AUDIO = 'files_audio';
82
83
	const CHUNK_TREE_SIZE = 2;
84
85
86
	/** @var IAppContainer */
87
	private $container;
88
89
	/** @var IRootFolder */
90
	private $rootFolder;
91
92
	/** @var IUserManager */
93
	private $userManager;
94
95
	/** @var IAppManager */
96
	private $appManager;
97
98
	/** @var IManager */
99
	private $shareManager;
100
101
	/** @var ConfigService */
102
	private $configService;
103
104
	/** @var LocalFilesService */
105
	private $localFilesService;
106
107
	/** @var ExternalFilesService */
108
	private $externalFilesService;
109
110
	/** @var GroupFoldersService */
111
	private $groupFoldersService;
112
113
	/** @var ExtensionService */
114
	private $extensionService;
115
116
	/** @var IFullTextSearchManager */
117
	private $fullTextSearchManager;
118
119
	/** @var MiscService */
120
	private $miscService;
121
122
123
	/** @var IRunner */
124
	private $runner;
125
126
	/** @var int */
127
	private $sumDocuments;
128
129
130
	/**
131
	 * FilesService constructor.
132
	 *
133
	 * @param IAppContainer $container
134
	 * @param IRootFolder $rootFolder
135
	 * @param IAppManager $appManager
136
	 * @param IUserManager $userManager
137
	 * @param IManager $shareManager
138
	 * @param ConfigService $configService
139
	 * @param LocalFilesService $localFilesService
140
	 * @param ExternalFilesService $externalFilesService
141
	 * @param GroupFoldersService $groupFoldersService
142
	 * @param ExtensionService $extensionService
143
	 * @param IFullTextSearchManager $fullTextSearchManager
144
	 * @param MiscService $miscService
145
	 *
146
	 * @internal param IProviderFactory $factory
147
	 */
148
	public function __construct(
149
		IAppContainer $container, IRootFolder $rootFolder, IAppManager $appManager,
150
		IUserManager $userManager, IManager $shareManager, ConfigService $configService,
151
		LocalFilesService $localFilesService, ExternalFilesService $externalFilesService,
152
		GroupFoldersService $groupFoldersService, ExtensionService $extensionService,
153
		IFullTextSearchManager $fullTextSearchManager, MiscService $miscService
154
	) {
155
		$this->container = $container;
156
		$this->rootFolder = $rootFolder;
157
		$this->appManager = $appManager;
158
		$this->userManager = $userManager;
159
		$this->shareManager = $shareManager;
160
161
		$this->configService = $configService;
162
		$this->localFilesService = $localFilesService;
163
		$this->externalFilesService = $externalFilesService;
164
		$this->groupFoldersService = $groupFoldersService;
165
		$this->extensionService = $extensionService;
166
		$this->fullTextSearchManager = $fullTextSearchManager;
167
168
		$this->miscService = $miscService;
169
	}
170
171
172
	/**
173
	 * @param IRunner $runner
174
	 */
175
	public function setRunner(IRunner $runner) {
176
		$this->runner = $runner;
177
	}
178
179
180
	/**
181
	 * @param string $userId
182
	 * @param IIndexOptions $indexOptions
183
	 *
184
	 * @return FilesDocument[]
185
	 * @throws NotFoundException
186
	 * @throws InvalidPathException
187
	 */
188
	public function getChunksFromUser(string $userId, IIndexOptions $indexOptions): array {
189
		$this->initFileSystems($userId);
190
191
		/** @var Folder $files */
192
		$files = $this->rootFolder->getUserFolder($userId)
193
								  ->get($indexOptions->getOption('path', '/'));
194
		if ($files instanceof Folder) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\Folder does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
195
			return $this->getChunksFromDirectory($userId, $files);
196
		} else {
197
			return [$files];
198
		}
199
	}
200
201
202
	/**
203
	 * @param string $userId
204
	 * @param Folder $node
205
	 * @param int $level
206
	 *
207
	 * @return FilesDocument[]
208
	 * @throws InvalidPathException
209
	 * @throws NotFoundException
210
	 */
211
	public function getChunksFromDirectory(string $userId, Folder $node, $level = 0): array {
212
		$entries = [];
213
		$level++;
214
215
		$files = $node->getDirectoryListing();
216
		if (empty($files)) {
217
			$entries[] = $this->getPathFromRoot($node->getPath(), $userId, true);
218
		}
219
220
		foreach ($files as $file) {
221
			if ($file->getType() === FileInfo::TYPE_FOLDER && $level < self::CHUNK_TREE_SIZE) {
222
				/** @var $file Folder */
223
				$entries =
224
					array_merge($entries, $this->getChunksFromDirectory($userId, $file, $level));
225
			} else {
226
				$entries[] = $this->getPathFromRoot($file->getPath(), $userId, true);
227
			}
228
		}
229
230
		return $entries;
231
	}
232
233
234
	/**
235
	 * @param string $userId
236
	 * @param string $chunk
237
	 *
238
	 * @return FilesDocument[]
239
	 * @throws InvalidPathException
240
	 * @throws NotFoundException
241
	 */
242
	public function getFilesFromUser(string $userId, string $chunk): array {
243
244
		$this->initFileSystems($userId);
245
		$this->sumDocuments = 0;
246
247
		/** @var Folder $files */
248
		$files = $this->rootFolder->getUserFolder($userId)
249
								  ->get($chunk);
250
		if ($files instanceof Folder) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\Folder does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
251
			$result = $this->getFilesFromDirectory($userId, $files);
252
		} else {
253
			$result = [];
254
			try {
255
				$result[] = $this->generateFilesDocumentFromFile($userId, $files);
256
			} catch (FileIsNotIndexableException $e) {
257
				/** we do nothin' */
258
			}
259
		}
260
261
		return $result;
262
	}
263
264
265
	/**
266
	 * @param string $userId
267
	 * @param Folder $node
268
	 *
269
	 * @return FilesDocument[]
270
	 * @throws InvalidPathException
271
	 * @throws NotFoundException
272
	 * @throws Exception
273
	 */
274
	public function getFilesFromDirectory(string $userId, Folder $node): array {
275
		$documents = [];
276
277
		$this->updateRunnerAction('generateIndexFiles', true);
278
		$this->updateRunnerInfo(
279
			[
280
				'info'          => $node->getPath(),
281
				'documentTotal' => $this->sumDocuments
282
			]
283
		);
284
285
		try {
286
			if ($node->nodeExists('.noindex')) {
287
				return $documents;
288
			}
289
		} catch (StorageNotAvailableException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\StorageNotAvailableException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
290
			return $documents;
291
		}
292
293
		$files = $node->getDirectoryListing();
294
		foreach ($files as $file) {
295
296
			try {
297
				$documents[] = $this->generateFilesDocumentFromFile($userId, $file);
298
				$this->sumDocuments++;
299
			} catch (FileIsNotIndexableException $e) {
300
				continue;
301
			}
302
303
			if ($file->getType() === FileInfo::TYPE_FOLDER) {
304
				/** @var $file Folder */
305
				$documents =
306
					array_merge($documents, $this->getFilesFromDirectory($userId, $file));
307
			}
308
		}
309
310
		return $documents;
311
	}
312
313
314
	/**
315
	 * @param string $userId
316
	 */
317
	private function initFileSystems(string $userId) {
318
		if ($userId === '') {
319
			return;
320
		}
321
322
		$this->externalFilesService->initExternalFilesForUser($userId);
323
		$this->groupFoldersService->initGroupSharesForUser($userId);
324
	}
325
326
327
	/**
328
	 * @param string $viewerId
329
	 * @param Node $file
330
	 *
331
	 * @return FilesDocument
332
	 * @throws FileIsNotIndexableException
333
	 * @throws InvalidPathException
334
	 * @throws NotFoundException
335
	 * @throws Exception
336
	 */
337
	private function generateFilesDocumentFromFile(string $viewerId, Node $file): FilesDocument {
338
339
		$source = $this->getFileSource($file);
340
		$document = new FilesDocument(FilesProvider::FILES_PROVIDER_ID, (string)$file->getId());
341
		$document->setAccess(new DocumentAccess());
342
343
		if ($file->getId() === -1) {
344
			throw new FileIsNotIndexableException();
345
		}
346
347
		$ownerId = '';
348
		if ($file->getOwner() !== null) {
349
			$ownerId = $file->getOwner()
350
							->getUID();
351
		}
352
353
		if (!is_string($ownerId)) {
354
			$ownerId = '';
355
		}
356
357
		$document->setType($file->getType())
358
				 ->setOwnerId($ownerId)
359
				 ->setPath($this->getPathFromViewerId($file->getId(), $viewerId))
360
				 ->setViewerId($viewerId)
361
				 ->setMimetype($file->getMimetype());
362
		$document->setModifiedTime($file->getMTime())
363
				 ->setSource($source);
364
365
		return $document;
366
	}
367
368
369
	/**
370
	 * @param Node $file
371
	 *
372
	 * @return string
373
	 * @throws FileIsNotIndexableException
374
	 */
375
	private function getFileSource(Node $file): string {
376
		$source = '';
377
378
		try {
379
			$this->localFilesService->getFileSource($file, $source);
380
			$this->externalFilesService->getFileSource($file, $source);
381
			$this->groupFoldersService->getFileSource($file, $source);
382
		} catch (KnownFileSourceException $e) {
383
			/** we know the source, just leave. */
384
		}
385
386
		return $source;
387
	}
388
389
390
	/**
391
	 * @param string $userId
392
	 * @param string $path
393
	 *
394
	 * @return Node
395
	 * @throws NotFoundException
396
	 */
397
	public function getFileFromPath(string $userId, string $path): Node {
398
		return $this->rootFolder->getUserFolder($userId)
399
								->get($path);
400
	}
401
402
403
	/**
404
	 * @param string $userId
405
	 * @param int $fileId
406
	 *
407
	 * @return Node
408
	 * @throws FilesNotFoundException
409
	 * @throws EmptyUserException
410
	 */
411
	public function getFileFromId(string $userId, int $fileId): Node {
412
413
		if ($userId === '') {
414
			throw new EmptyUserException();
415
		}
416
417
		$files = $this->rootFolder->getUserFolder($userId)
418
								  ->getById($fileId);
419
		if (sizeof($files) === 0) {
420
			throw new FilesNotFoundException();
421
		}
422
423
		$file = array_shift($files);
424
425
		return $file;
426
	}
427
428
429
	/**
430
	 * @param IIndex $index
431
	 *
432
	 * @return Node
433
	 * @throws EmptyUserException
434
	 * @throws FilesNotFoundException
435
	 */
436
	public function getFileFromIndex(IIndex $index): Node {
437
		// it seems the method is already call slightly earlier in the process
438
//		$this->impersonateOwner($index);
439
440
		return $this->getFileFromId($index->getOwnerId(), (int)$index->getDocumentId());
441
	}
442
443
444
	/**
445
	 * @param int $fileId
446
	 * @param string $viewerId
447
	 *
448
	 * @throws Exception
449
	 * @return string
450
	 */
451
	private function getPathFromViewerId(int $fileId, string $viewerId): string {
452
453
		$viewerFiles = $this->rootFolder->getUserFolder($viewerId)
454
										->getById($fileId);
455
456
		if (sizeof($viewerFiles) === 0) {
457
			return '';
458
		}
459
460
		$file = array_shift($viewerFiles);
461
462
		// TODO: better way to do this : we remove the '/userid/files/'
463
		$path = $this->getPathFromRoot($file->getPath(), $viewerId);
464
		if (!is_string($path)) {
465
			throw new FileIsNotIndexableException();
466
		}
467
468
		$path = $this->withoutEndSlash($path);
469
470
		return $path;
471
	}
472
473
474
	/**
475
	 * @param FilesDocument $document
476
	 */
477
	public function generateDocument(FilesDocument $document) {
478
479
		try {
480
			$this->updateFilesDocument($document);
481
		} catch (Exception $e) {
482
			// TODO - update $document with a error status instead of just ignore !
483
			$document->getIndex()
484
					 ->setStatus(IIndex::INDEX_IGNORE);
485
			$this->miscService->log(
486
				'Exception while generateDocument: ' . $e->getMessage() . ' - trace: '
487
				. json_encode($e->getTrace())
488
			);
489
		}
490
	}
491
492
493
	/**
494
	 * @param IIndex $index
495
	 *
496
	 * @return FilesDocument
497
	 * @throws FileIsNotIndexableException
498
	 * @throws InvalidPathException
499
	 * @throws NotFoundException
500
	 */
501
	private function generateDocumentFromIndex(IIndex $index): FilesDocument {
502
503
		try {
504
			$file = $this->getFileFromIndex($index);
505
		} catch (Exception $e) {
506
			$index->setStatus(IIndex::INDEX_REMOVE);
507
			$document = new FilesDocument($index->getProviderId(), $index->getDocumentId());
508
			$document->setIndex($index);
509
510
			return $document;
511
		}
512
513
		$document = $this->generateFilesDocumentFromFile($index->getOwnerId(), $file);
514
		$document->setIndex($index);
515
516
		$this->updateFilesDocumentFromFile($document, $file);
517
518
		return $document;
519
	}
520
521
522
	/**
523
	 * @param IndexDocument $document
524
	 *
525
	 * @return bool
526
	 */
527
	public function isDocumentUpToDate(IndexDocument $document): bool {
528
		$index = $document->getIndex();
529
530
		if (!$this->configService->compareIndexOptions($index)) {
531
			$index->setStatus(IIndex::INDEX_CONTENT);
532
			$document->setIndex($index);
533
534
			return false;
535
		}
536
537
		if ($index->getStatus() !== IIndex::INDEX_OK) {
538
			return false;
539
		}
540
541
		if ($index->getLastIndex() >= $document->getModifiedTime()) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return $index->getLastIn...ent->getModifiedTime();.
Loading history...
542
			return true;
543
		}
544
545
		return false;
546
	}
547
548
549
	/**
550
	 * @param IIndex $index
551
	 *
552
	 * @return FilesDocument
553
	 * @throws InvalidPathException
554
	 * @throws NotFoundException
555
	 * @throws FileIsNotIndexableException
556
	 */
557
	public function updateDocument(IIndex $index): FilesDocument {
558
		$this->impersonateOwner($index);
559
		$this->initFileSystems($index->getOwnerId());
560
561
		$document = $this->generateDocumentFromIndex($index);
562
		$this->updateDirectoryContentIndex($index);
563
564
		return $document;
565
	}
566
567
568
	/**
569
	 * @param FilesDocument $document
570
	 *
571
	 * @throws NotFoundException
572
	 */
573
	private function updateFilesDocument(FilesDocument $document) {
574
		$userFolder = $this->rootFolder->getUserFolder($document->getViewerId());
575
		$file = $userFolder->get($document->getPath());
576
577
		try {
578
			$this->updateFilesDocumentFromFile($document, $file);
579
		} catch (FileIsNotIndexableException $e) {
580
			$document->getIndex()
581
					 ->setStatus(IIndex::INDEX_IGNORE);
582
		}
583
	}
584
585
586
	/**
587
	 * @param FilesDocument $document
588
	 * @param Node $file
589
	 *
590
	 * @throws FileIsNotIndexableException
591
	 */
592
	private function updateFilesDocumentFromFile(FilesDocument $document, Node $file) {
593
594
		$document->getIndex()
595
				 ->setSource($document->getSource());
596
597
		$this->updateDocumentAccess($document, $file);
598
		$this->updateContentFromFile($document, $file);
599
600
		$document->addMetaTag($document->getSource());
601
	}
602
603
604
	/**
605
	 * @param FilesDocument $document
606
	 * @param Node $file
607
	 *
608
	 * @throws FileIsNotIndexableException
609
	 */
610
	private function updateDocumentAccess(FilesDocument $document, Node $file) {
611
612
		$index = $document->getIndex();
0 ignored issues
show
Unused Code introduced by
$index is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
613
		// This should not be needed, let's assume we _need_ to update document access
614
//		if (!$index->isStatus(IIndex::INDEX_FULL)
615
//			&& !$index->isStatus(IIndex::INDEX_META)) {
616
//			return;
617
//		}
618
619
		$this->localFilesService->updateDocumentAccess($document, $file);
620
		$this->externalFilesService->updateDocumentAccess($document, $file);
621
		$this->groupFoldersService->updateDocumentAccess($document, $file);
622
623
		$this->updateShareNames($document, $file);
624
	}
625
626
627
	/**
628
	 * @param FilesDocument $document
629
	 * @param Node $file
630
	 */
631
	private function updateContentFromFile(FilesDocument $document, Node $file) {
632
633
		$document->setTitle($document->getPath());
634
635
		if (!$document->getIndex()
636
					  ->isStatus(IIndex::INDEX_CONTENT)
637
			|| $file->getType() !== FileInfo::TYPE_FILE) {
638
			return;
639
		}
640
641
		try {
642
			/** @var File $file */
643
			if ($file->getSize() <
644
				($this->configService->getAppValue(ConfigService::FILES_SIZE) * 1024 * 1024)) {
645
				$this->extractContentFromFileText($document, $file);
646
				$this->extractContentFromFileOffice($document, $file);
647
				$this->extractContentFromFilePDF($document, $file);
648
				$this->extractContentFromFileZip($document, $file);
649
650
				$this->extensionService->fileIndexing($document, $file);
651
			}
652
		} catch (Throwable $t) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
653
			$this->manageContentErrorException($document, $t);
654
		}
655
656
		if ($document->getContent() === null) {
657
			$document->getIndex()
658
					 ->unsetStatus(IIndex::INDEX_CONTENT);
659
		}
660
	}
661
662
663
	/**
664
	 * @param FilesDocument $document
665
	 * @param Node $file
666
	 *
667
	 * @return array
668
	 */
669
	private function updateShareNames(FilesDocument $document, Node $file): array {
670
671
		$users = [];
672
673
		$this->localFilesService->getShareUsersFromFile($file, $users);
674
		$this->externalFilesService->getShareUsers($document, $users);
675
		$this->groupFoldersService->getShareUsers($document, $users);
676
677
		$shareNames = [];
678
		foreach ($users as $username) {
679
			try {
680
				$user = $this->userManager->get($username);
681
				if ($user === null || $user->getLastLogin() === 0) {
682
					continue;
683
				}
684
685
				$path = $this->getPathFromViewerId($file->getId(), $username);
686
				$shareNames[$this->miscService->secureUsername($username)] =
687
					(!is_string($path)) ? $path = '' : $path;
688
689
			} catch (Exception $e) {
690
				$this->miscService->log(
691
					'Issue while getting information on documentId:' . $document->getId(), 0
692
				);
693
			}
694
		}
695
696
		$document->setInfoArray('share_names', $shareNames);
697
698
		return $shareNames;
699
	}
700
701
702
	/**
703
	 * @param string $mimeType
704
	 * @param string $extension
705
	 *
706
	 * @return string
707
	 */
708
	private function parseMimeType(string $mimeType, string $extension): string {
709
710
		$parsed = '';
711
		try {
712
			$this->parseMimeTypeText($mimeType, $extension, $parsed);
713
			$this->parseMimeTypePDF($mimeType, $parsed);
714
			$this->parseMimeTypeOffice($mimeType, $parsed);
715
			$this->parseMimeTypeZip($mimeType, $parsed);
716
		} catch (KnownFileMimeTypeException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
717
		}
718
719
		return $parsed;
720
	}
721
722
723
	/**
724
	 * @param string $mimeType
725
	 * @param string $extension
726
	 * @param string $parsed
727
	 *
728
	 * @throws KnownFileMimeTypeException
729
	 */
730
	private function parseMimeTypeText(string $mimeType, string $extension, string &$parsed) {
731
732
		if (substr($mimeType, 0, 5) === 'text/') {
733
			$parsed = self::MIMETYPE_TEXT;
734
			throw new KnownFileMimeTypeException();
735
		}
736
737
		$textMimes = [
738
			'application/epub+zip'
739
		];
740
741 View Code Duplication
		foreach ($textMimes as $mime) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
742
			if (strpos($mimeType, $mime) === 0) {
743
				$parsed = self::MIMETYPE_TEXT;
744
				throw new KnownFileMimeTypeException();
745
			}
746
		}
747
748
		$this->parseMimeTypeTextByExtension($mimeType, $extension, $parsed);
749
	}
750
751
752
	/**
753
	 * @param string $mimeType
754
	 * @param string $extension
755
	 * @param string $parsed
756
	 *
757
	 * @throws KnownFileMimeTypeException
758
	 */
759
	private function parseMimeTypeTextByExtension(
760
		string $mimeType, string $extension, string &$parsed
761
	) {
762
		$textMimes = [
763
			'application/octet-stream'
764
		];
765
		$textExtension = [
766
		];
767
768
		foreach ($textMimes as $mime) {
769
			if (strpos($mimeType, $mime) === 0
770
				&& in_array(
771
					strtolower($extension), $textExtension
772
				)) {
773
				$parsed = self::MIMETYPE_TEXT;
774
				throw new KnownFileMimeTypeException();
775
			}
776
		}
777
	}
778
779
780
	/**
781
	 * @param string $mimeType
782
	 * @param string $parsed
783
	 *
784
	 * @throws KnownFileMimeTypeException
785
	 */
786
	private function parseMimeTypePDF(string $mimeType, string &$parsed) {
787
788
		if ($mimeType === 'application/pdf') {
789
			$parsed = self::MIMETYPE_PDF;
790
			throw new KnownFileMimeTypeException();
791
		}
792
	}
793
794
795
	/**
796
	 * @param string $mimeType
797
	 * @param string $parsed
798
	 *
799
	 * @throws KnownFileMimeTypeException
800
	 */
801
	private function parseMimeTypeZip(string $mimeType, string &$parsed) {
802
		if ($mimeType === 'application/zip') {
803
			$parsed = self::MIMETYPE_ZIP;
804
			throw new KnownFileMimeTypeException();
805
		}
806
	}
807
808
809
	/**
810
	 * @param string $mimeType
811
	 * @param string $parsed
812
	 *
813
	 * @throws KnownFileMimeTypeException
814
	 */
815
	private function parseMimeTypeOffice(string $mimeType, string &$parsed) {
816
817
		$officeMimes = [
818
			'application/msword',
819
			'application/vnd.oasis.opendocument',
820
			'application/vnd.sun.xml',
821
			'application/vnd.openxmlformats-officedocument',
822
			'application/vnd.ms-word',
823
			'application/vnd.ms-powerpoint',
824
			'application/vnd.ms-excel'
825
		];
826
827 View Code Duplication
		foreach ($officeMimes as $mime) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
828
			if (strpos($mimeType, $mime) === 0) {
829
				$parsed = self::MIMETYPE_OFFICE;
830
				throw new KnownFileMimeTypeException();
831
			}
832
		}
833
	}
834
835
836
	/**
837
	 * @param FilesDocument $document
838
	 * @param File $file
839
	 *
840
	 * @throws NotPermittedException
841
	 */
842
	private function extractContentFromFileText(FilesDocument $document, File $file) {
843
		if ($this->parseMimeType($document->getMimeType(), $file->getExtension())
844
			!== self::MIMETYPE_TEXT) {
845
			return;
846
		}
847
848
		if (!$this->isSourceIndexable($document)) {
849
			return;
850
		}
851
852
		$document->setContent(
853
			base64_encode($file->getContent()), IndexDocument::ENCODED_BASE64
854
		);
855
	}
856
857
858
	/**
859
	 * @param FilesDocument $document
860
	 * @param File $file
861
	 *
862
	 * @throws NotPermittedException
863
	 */
864 View Code Duplication
	private function extractContentFromFilePDF(FilesDocument $document, File $file) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
865
		if ($this->parseMimeType($document->getMimeType(), $file->getExtension())
866
			!== self::MIMETYPE_PDF) {
867
			return;
868
		}
869
870
		$this->configService->setDocumentIndexOption($document, ConfigService::FILES_PDF);
871
		if (!$this->isSourceIndexable($document)) {
872
			return;
873
		}
874
875
		if ($this->configService->getAppValue(ConfigService::FILES_PDF) !== '1') {
876
			$document->setContent('');
877
878
			return;
879
		}
880
881
		$document->setContent(
882
			base64_encode($file->getContent()), IndexDocument::ENCODED_BASE64
883
		);
884
	}
885
886
887
	/**
888
	 * @param FilesDocument $document
889
	 * @param File $file
890
	 *
891
	 * @throws NotPermittedException
892
	 */
893 View Code Duplication
	private function extractContentFromFileZip(FilesDocument $document, File $file) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
894
		if ($this->parseMimeType($document->getMimeType(), $file->getExtension())
895
			!== self::MIMETYPE_ZIP) {
896
			return;
897
		}
898
899
		$this->configService->setDocumentIndexOption($document, ConfigService::FILES_ZIP);
900
		if (!$this->isSourceIndexable($document)) {
901
			return;
902
		}
903
904
		if ($this->configService->getAppValue(ConfigService::FILES_ZIP) !== '1') {
905
			$document->setContent('');
906
907
			return;
908
		}
909
910
		$document->setContent(
911
			base64_encode($file->getContent()), IndexDocument::ENCODED_BASE64
912
		);
913
	}
914
915
916
	/**
917
	 * @param FilesDocument $document
918
	 * @param File $file
919
	 *
920
	 * @throws NotPermittedException
921
	 */
922 View Code Duplication
	private function extractContentFromFileOffice(FilesDocument $document, File $file) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
923
		if ($this->parseMimeType($document->getMimeType(), $file->getExtension())
924
			!== self::MIMETYPE_OFFICE) {
925
			return;
926
		}
927
928
		$this->configService->setDocumentIndexOption($document, ConfigService::FILES_OFFICE);
929
		if (!$this->isSourceIndexable($document)) {
930
			return;
931
		}
932
933
		if ($this->configService->getAppValue(ConfigService::FILES_OFFICE) !== '1') {
934
			$document->setContent('');
935
936
			return;
937
		}
938
939
		$document->setContent(
940
			base64_encode($file->getContent()), IndexDocument::ENCODED_BASE64
941
		);
942
	}
943
944
945
	/**
946
	 * @param FilesDocument $document
947
	 *
948
	 * @return bool
949
	 */
950
	private function isSourceIndexable(FilesDocument $document): bool {
951
		$this->configService->setDocumentIndexOption($document, $document->getSource());
952
		if ($this->configService->getAppValue($document->getSource()) !== '1') {
953
			$document->setContent('');
954
955
			return false;
956
		}
957
958
		return true;
959
	}
960
961
962
	/**
963
	 * @param IIndex $index
964
	 */
965
	private function impersonateOwner(IIndex $index) {
966
		if ($index->getOwnerId() !== '') {
967
			return;
968
		}
969
970
		$this->groupFoldersService->impersonateOwner($index);
971
		$this->externalFilesService->impersonateOwner($index);
972
	}
973
974
975
	/**
976
	 * @param $action
977
	 * @param bool $force
978
	 *
979
	 * @throws Exception
980
	 */
981
	private function updateRunnerAction(string $action, bool $force = false) {
982
		if ($this->runner === null) {
983
			return;
984
		}
985
986
		$this->runner->updateAction($action, $force);
987
	}
988
989
990
	/**
991
	 * @param array $data
992
	 */
993
	private function updateRunnerInfo($data) {
994
		if ($this->runner === null) {
995
			return;
996
		}
997
998
		$this->runner->setInfoArray($data);
999
	}
1000
1001
	/**
1002
	 * @param IndexDocument $document
1003
	 * @param Throwable $t
1004
	 */
1005
	private function manageContentErrorException(IndexDocument $document, Throwable $t) {
1006
		$document->getIndex()
1007
				 ->addError(
1008
					 'Error while getting file content', $t->getMessage(), IIndex::ERROR_SEV_3
1009
				 );
1010
		$this->updateNewIndexError(
1011
			$document->getIndex(), 'Error while getting file content', $t->getMessage(),
1012
			IIndex::ERROR_SEV_3
1013
		);
1014
		$this->miscService->log(json_encode($t->getTrace()), 0);
1015
	}
1016
1017
1018
	/**
1019
	 * @param IIndex $index
1020
	 */
1021
	private function updateDirectoryContentIndex(IIndex $index) {
1022
		if (!$index->isStatus(IIndex::INDEX_META)) {
1023
			return;
1024
		}
1025
1026
		try {
1027
			$file = $this->getFileFromIndex($index);
1028
			if ($file->getType() === File::TYPE_FOLDER) {
1029
				/** @var Folder $file */
1030
				$this->updateDirectoryMeta($file);
1031
			}
1032
		} catch (Exception $e) {
1033
		}
1034
	}
1035
1036
1037
	/**
1038
	 * @param Folder $node
1039
	 */
1040
	private function updateDirectoryMeta(Folder $node) {
1041
		try {
1042
			$files = $node->getDirectoryListing();
1043
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
1044
			return;
1045
		}
1046
1047
		foreach ($files as $file) {
1048
			try {
1049
				$this->fullTextSearchManager->updateIndexStatus(
1050
					'files', (string)$file->getId(), IIndex::INDEX_META
1051
				);
1052
			} catch (InvalidPathException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
Bug introduced by
The class OCP\Files\InvalidPathException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
1053
			} catch (NotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
Bug introduced by
The class OCP\Files\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
1054
			}
1055
		}
1056
	}
1057
1058
1059
	/**
1060
	 * @param IIndex $index
1061
	 * @param string $message
1062
	 * @param string $exception
1063
	 * @param int $sev
1064
	 */
1065
	private function updateNewIndexError(IIndex $index, string $message, string $exception, int $sev
1066
	) {
1067
		if ($this->runner === null) {
1068
			return;
1069
		}
1070
1071
		$this->runner->newIndexError($index, $message, $exception, $sev);
1072
	}
1073
1074
1075
	/**
1076
	 * @param string $path
1077
	 * @param string $userId
1078
	 *
1079
	 * @param bool $entrySlash
1080
	 *
1081
	 * @return string
1082
	 */
1083
	private function getPathFromRoot(string $path, string $userId, bool $entrySlash = false) {
1084
		// TODO: better way to do this : we remove the '/userid/files/'
1085
		$path = substr($path, 8 + strlen($userId));
1086
		if (!is_string($path)) {
1087
			$path = '';
1088
		}
1089
1090
		return (($entrySlash) ? '/' : '') . $path;
1091
	}
1092
1093
}
1094
1095