Completed
Pull Request — master (#87)
by Robin
01:37
created

FilesService::getChunksFromDirectory()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 8.8977
c 0
b 0
f 0
cc 6
nc 8
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 OC\FullTextSearch\Model\DocumentAccess;
37
use OCA\Files_FullTextSearch\Exceptions\EmptyUserException;
38
use OCA\Files_FullTextSearch\Exceptions\FileIsNotIndexableException;
39
use OCA\Files_FullTextSearch\Exceptions\FilesNotFoundException;
40
use OCA\Files_FullTextSearch\Exceptions\KnownFileMimeTypeException;
41
use OCA\Files_FullTextSearch\Exceptions\KnownFileSourceException;
42
use OCA\Files_FullTextSearch\Model\FilesDocument;
43
use OCA\Files_FullTextSearch\Provider\FilesProvider;
44
use OCP\App\IAppManager;
45
use OCP\AppFramework\IAppContainer;
46
use OCP\Comments\ICommentsManager;
47
use OCP\Files\File;
48
use OCP\Files\FileInfo;
49
use OCP\Files\Folder;
50
use OCP\Files\InvalidPathException;
51
use OCP\Files\IRootFolder;
52
use OCP\Files\Node;
53
use OCP\Files\NotFoundException;
54
use OCP\Files\NotPermittedException;
55
use OCP\Files\StorageNotAvailableException;
56
use OCP\FullTextSearch\IFullTextSearchManager;
57
use OCP\FullTextSearch\Model\IIndex;
58
use OCP\FullTextSearch\Model\IIndexDocument;
59
use OCP\FullTextSearch\Model\IIndexOptions;
60
use OCP\FullTextSearch\Model\IRunner;
61
use OCP\IUserManager;
62
use OCP\Lock\LockedException;
63
use OCP\Share\IManager as IShareManager;
64
use Throwable;
65
66
67
/**
68
 * Class FilesService
69
 *
70
 * @package OCA\Files_FullTextSearch\Service
71
 */
72
class FilesService {
73
74
75
	use TPathTools;
76
77
78
	const MIMETYPE_TEXT = 'files_text';
79
	const MIMETYPE_PDF = 'files_pdf';
80
	const MIMETYPE_OFFICE = 'files_office';
81
	const MIMETYPE_ZIP = 'files_zip';
82
	const MIMETYPE_IMAGE = 'files_image';
83
	const MIMETYPE_AUDIO = 'files_audio';
84
85
	const CHUNK_TREE_SIZE = 2;
86
87
88
	/** @var IAppContainer */
89
	private $container;
90
91
	/** @var IRootFolder */
92
	private $rootFolder;
93
94
	/** @var IUserManager */
95
	private $userManager;
96
97
	/** @var IAppManager */
98
	private $appManager;
99
100
	/** @var IShareManager */
101
	private $shareManager;
102
103
	/** @var ICommentsManager */
104
	private $commentsManager;
105
106
	/** @var ConfigService */
107
	private $configService;
108
109
	/** @var LocalFilesService */
110
	private $localFilesService;
111
112
	/** @var ExternalFilesService */
113
	private $externalFilesService;
114
115
	/** @var GroupFoldersService */
116
	private $groupFoldersService;
117
118
	/** @var ExtensionService */
119
	private $extensionService;
120
121
	/** @var IFullTextSearchManager */
122
	private $fullTextSearchManager;
123
124
	/** @var MiscService */
125
	private $miscService;
126
127
128
	/** @var IRunner */
129
	private $runner;
130
131
	/** @var int */
132
	private $sumDocuments;
133
134
135
	/**
136
	 * FilesService constructor.
137
	 *
138
	 * @param IAppContainer $container
139
	 * @param IRootFolder $rootFolder
140
	 * @param IAppManager $appManager
141
	 * @param IUserManager $userManager
142
	 * @param IShareManager $shareManager
143
	 * @param ICommentsManager $commentsManager
144
	 * @param ConfigService $configService
145
	 * @param LocalFilesService $localFilesService
146
	 * @param ExternalFilesService $externalFilesService
147
	 * @param GroupFoldersService $groupFoldersService
148
	 * @param ExtensionService $extensionService
149
	 * @param IFullTextSearchManager $fullTextSearchManager
150
	 * @param MiscService $miscService
151
	 *
152
	 * @internal param IProviderFactory $factory
153
	 */
154
	public function __construct(
155
		IAppContainer $container, IRootFolder $rootFolder, IAppManager $appManager,
156
		IUserManager $userManager, IShareManager $shareManager, ICommentsManager $commentsManager,
157
		ConfigService $configService,
158
		LocalFilesService $localFilesService, ExternalFilesService $externalFilesService,
159
		GroupFoldersService $groupFoldersService, ExtensionService $extensionService,
160
		IFullTextSearchManager $fullTextSearchManager, MiscService $miscService
161
	) {
162
		$this->container = $container;
163
		$this->rootFolder = $rootFolder;
164
		$this->appManager = $appManager;
165
		$this->userManager = $userManager;
166
		$this->shareManager = $shareManager;
167
		$this->commentsManager = $commentsManager;
168
169
		$this->configService = $configService;
170
		$this->localFilesService = $localFilesService;
171
		$this->externalFilesService = $externalFilesService;
172
		$this->groupFoldersService = $groupFoldersService;
173
		$this->extensionService = $extensionService;
174
		$this->fullTextSearchManager = $fullTextSearchManager;
175
176
		$this->miscService = $miscService;
177
	}
178
179
180
	/**
181
	 * @param IRunner $runner
182
	 */
183
	public function setRunner(IRunner $runner) {
184
		$this->runner = $runner;
185
	}
186
187
188
	/**
189
	 * @param string $userId
190
	 * @param IIndexOptions $indexOptions
191
	 *
192
	 * @return FilesDocument[]
193
	 * @throws NotFoundException
194
	 * @throws InvalidPathException
195
	 */
196
	public function getChunksFromUser(string $userId, IIndexOptions $indexOptions): array {
197
		$this->initFileSystems($userId);
198
199
		/** @var Folder $files */
200
		$files = $this->rootFolder->getUserFolder($userId)
201
								  ->get($indexOptions->getOption('path', '/'));
202
		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...
203
			return $this->getChunksFromDirectory($userId, $files);
204
		} else {
205
			return [$files];
206
		}
207
	}
208
209
210
	/**
211
	 * @param string $userId
212
	 * @param Folder $node
213
	 * @param int $level
214
	 *
215
	 * @return FilesDocument[]
216
	 * @throws InvalidPathException
217
	 * @throws NotFoundException
218
	 */
219
	private function getChunksFromDirectory(string $userId, Folder $node, $level = 0): array {
220
		$entries = [];
221
		$level++;
222
223
		$files = $node->getDirectoryListing();
224
		if (empty($files)) {
225
			$entries[] = $this->getPathFromRoot($node->getPath(), $userId, true);
226
		}
227
228
		foreach ($files as $file) {
229
			$filePath = $this->getPathFromRoot($file->getPath(), $userId, true);
230
			if ($this->configService->isPathExcluded($filePath)) {
231
				continue;
232
			}
233
			if ($file->getType() === FileInfo::TYPE_FOLDER && $level < self::CHUNK_TREE_SIZE) {
234
				/** @var $file Folder */
235
				$entries =
236
					array_merge($entries, $this->getChunksFromDirectory($userId, $file, $level));
237
			} else {
238
				$entries[] = $filePath;
239
			}
240
		}
241
242
		return $entries;
243
	}
244
245
246
	/**
247
	 * @param string $userId
248
	 * @param string $chunk
249
	 *
250
	 * @return FilesDocument[]
251
	 * @throws InvalidPathException
252
	 * @throws NotFoundException
253
	 */
254
	public function getFilesFromUser(string $userId, string $chunk): array {
255
256
		$this->initFileSystems($userId);
257
		$this->sumDocuments = 0;
258
259
		/** @var Folder $files */
260
		$files = $this->rootFolder->getUserFolder($userId)
261
								  ->get($chunk);
262
263
		$result = [];
264
		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...
265
			$result = $this->generateFilesDocumentFromParent($userId, $files);
266
			$result = array_merge($result, $this->getFilesFromDirectory($userId, $files));
267
		} else {
268
			try {
269
				$result[] = $this->generateFilesDocumentFromFile($userId, $files);
270
			} catch (FileIsNotIndexableException $e) {
271
				/** we do nothin' */
272
			}
273
		}
274
275
		return $result;
276
	}
277
278
279
	/**
280
	 * @param string $userId
281
	 * @param Folder $node
282
	 *
283
	 * @return FilesDocument[]
284
	 * @throws InvalidPathException
285
	 * @throws NotFoundException
286
	 * @throws Exception
287
	 */
288
	public function getFilesFromDirectory(string $userId, Folder $node): array {
289
		$documents = [];
290
291
		$this->updateRunnerAction('generateIndexFiles', true);
292
		$this->updateRunnerInfo(
293
			[
294
				'info'          => $node->getPath(),
295
				'title'         => '',
296
				'content'       => '',
297
				'documentTotal' => $this->sumDocuments
298
			]
299
		);
300
301
		try {
302
			if ($node->nodeExists('.noindex')) {
303
				return $documents;
304
			}
305
		} 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...
306
			return $documents;
307
		}
308
309
		if ($this->configService->getAppValue(ConfigService::FILES_EXTERNAL) === '2'
310
			&& $node->getMountPoint()
311
					->getMountType() === 'external') {
312
			return $documents;
313
		}
314
315
		$files = $node->getDirectoryListing();
316
		foreach ($files as $file) {
317
318
			try {
319
				$documents[] = $this->generateFilesDocumentFromFile($userId, $file);
320
				$this->sumDocuments++;
321
			} catch (FileIsNotIndexableException $e) {
322
				continue;
323
			}
324
325
			if ($file->getType() === FileInfo::TYPE_FOLDER) {
326
				/** @var $file Folder */
327
				$documents =
328
					array_merge($documents, $this->getFilesFromDirectory($userId, $file));
329
			}
330
		}
331
332
		return $documents;
333
	}
334
335
336
	/**
337
	 * @param string $userId
338
	 */
339
	private function initFileSystems(string $userId) {
340
		if ($userId === '') {
341
			return;
342
		}
343
344
		if ($this->userManager->get($userId) === null) {
345
			return;
346
		}
347
348
		$this->externalFilesService->initExternalFilesForUser($userId);
349
		$this->groupFoldersService->initGroupSharesForUser($userId);
350
	}
351
352
353
	/**
354
	 * @param string $userId
355
	 * @param Folder $parent
356
	 *
357
	 * @return array
358
	 */
359
	private function generateFilesDocumentFromParent(string $userId, Folder $parent): array {
360
		$documents = [];
361
		try {
362
			for ($i = 0; $i < self::CHUNK_TREE_SIZE; $i++) {
363
				$parent = $parent->getParent();
364
				$documents[] = $this->generateFilesDocumentFromFile($userId, $parent);
365
			}
366
		} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
367
		}
368
369
		return $documents;
370
	}
371
372
373
	/**
374
	 * @param string $viewerId
375
	 * @param Node $file
376
	 *
377
	 * @return FilesDocument
378
	 * @throws FileIsNotIndexableException
379
	 * @throws InvalidPathException
380
	 * @throws NotFoundException
381
	 * @throws Exception
382
	 */
383
	private function generateFilesDocumentFromFile(string $viewerId, Node $file): FilesDocument {
384
385
		$this->isNodeIndexable($file);
386
387
		$source = $this->getFileSource($file);
388
		$document = new FilesDocument(FilesProvider::FILES_PROVIDER_ID, (string)$file->getId());
389
		$document->setAccess(new DocumentAccess());
390
391
		if ($file->getId() === -1) {
392
			throw new FileIsNotIndexableException();
393
		}
394
395
		if ($file->getExtension() === 'part') {
396
			throw new FileIsNotIndexableException('part files are not indexed');
397
		}
398
399
		$ownerId = '';
400
		if ($file->getOwner() !== null) {
401
			$ownerId = $file->getOwner()
402
							->getUID();
403
		}
404
405
		if (!is_string($ownerId)) {
406
			$ownerId = '';
407
		}
408
409
		$document->setType($file->getType())
410
				 ->setOwnerId($ownerId)
411
				 ->setPath($this->getPathFromViewerId($file->getId(), $viewerId))
412
				 ->setViewerId($viewerId)
413
				 ->setMimetype($file->getMimetype());
414
		$document->setModifiedTime($file->getMTime())
415
				 ->setSource($source);
416
417
		return $document;
418
	}
419
420
421
	/**
422
	 * @param Node $file
423
	 *
424
	 * @return string
425
	 * @throws FileIsNotIndexableException
426
	 */
427
	private function getFileSource(Node $file): string {
428
		$source = '';
429
430
		try {
431
			$this->localFilesService->getFileSource($file, $source);
432
			$this->externalFilesService->getFileSource($file, $source);
433
			$this->groupFoldersService->getFileSource($file, $source);
434
		} catch (KnownFileSourceException $e) {
435
			/** we know the source, just leave. */
436
		}
437
438
		return $source;
439
	}
440
441
442
	/**
443
	 * @param string $userId
444
	 * @param string $path
445
	 *
446
	 * @return Node
447
	 * @throws NotFoundException
448
	 */
449
	public function getFileFromPath(string $userId, string $path): Node {
450
		return $this->rootFolder->getUserFolder($userId)
451
								->get($path);
452
	}
453
454
455
	/**
456
	 * @param string $userId
457
	 * @param int $fileId
458
	 *
459
	 * @return Node
460
	 * @throws FilesNotFoundException
461
	 * @throws EmptyUserException
462
	 */
463
	public function getFileFromId(string $userId, int $fileId): Node {
464
		if ($userId === '') {
465
			throw new EmptyUserException();
466
		}
467
468
		$files = $this->rootFolder->getUserFolder($userId)
469
								  ->getById($fileId);
470
		if (sizeof($files) === 0) {
471
			throw new FilesNotFoundException();
472
		}
473
474
		return array_shift($files);
475
	}
476
477
478
	/**
479
	 * @param IIndex $index
480
	 *
481
	 * @return Node
482
	 * @throws EmptyUserException
483
	 * @throws FilesNotFoundException
484
	 */
485
	public function getFileFromIndex(IIndex $index): Node {
486
		// it seems the method is already call slightly earlier in the process
487
//		$this->impersonateOwner($index);
488
489
		return $this->getFileFromId($index->getOwnerId(), (int)$index->getDocumentId());
490
	}
491
492
493
	/**
494
	 * @param int $fileId
495
	 * @param string $viewerId
496
	 *
497
	 * @return string
498
	 * @throws Exception
499
	 */
500
	private function getPathFromViewerId(int $fileId, string $viewerId): string {
501
502
		$viewerFiles = $this->rootFolder->getUserFolder($viewerId)
503
										->getById($fileId);
504
505
		if (sizeof($viewerFiles) === 0) {
506
			return '';
507
		}
508
509
		$file = array_shift($viewerFiles);
510
511
		// TODO: better way to do this : we remove the '/userid/files/'
512
		$path = $this->getPathFromRoot($file->getPath(), $viewerId);
513
		if (!is_string($path)) {
514
			throw new FileIsNotIndexableException();
515
		}
516
517
		$path = $this->withoutEndSlash($path);
518
519
		return $path;
520
	}
521
522
523
	/**
524
	 * @param FilesDocument $document
525
	 */
526
	public function generateDocument(FilesDocument $document) {
527
528
		try {
529
			$this->updateFilesDocument($document);
530
		} catch (Exception $e) {
531
			// TODO - update $document with a error status instead of just ignore !
532
			$document->getIndex()
533
					 ->setStatus(IIndex::INDEX_IGNORE);
534
			$this->miscService->log(
535
				'Exception while generateDocument: ' . $e->getMessage() . ' - trace: '
536
				. json_encode($e->getTrace())
537
			);
538
		}
539
	}
540
541
542
	/**
543
	 * @param IIndex $index
544
	 *
545
	 * @return FilesDocument
546
	 * @throws FileIsNotIndexableException
547
	 * @throws InvalidPathException
548
	 * @throws NotFoundException
549
	 */
550
	private function generateDocumentFromIndex(IIndex $index): FilesDocument {
551
552
		try {
553
			$file = $this->getFileFromIndex($index);
554
555
			if ($file->getMountPoint()
556
					 ->getMountType() === 'external'
557
				&& $this->configService->getAppValue(ConfigService::FILES_EXTERNAL) === '2') {
558
				throw new Exception();
559
			}
560
		} catch (Exception $e) {
561
			$index->setStatus(IIndex::INDEX_REMOVE);
562
			$document = new FilesDocument($index->getProviderId(), $index->getDocumentId());
563
			$document->setIndex($index);
564
565
			return $document;
566
		}
567
568
		$this->isNodeIndexable($file);
569
570
		$document = $this->generateFilesDocumentFromFile($index->getOwnerId(), $file);
571
		$document->setIndex($index);
572
573
		$this->updateFilesDocumentFromFile($document, $file);
574
575
		return $document;
576
	}
577
578
579
	/**
580
	 * @param IIndexDocument $document
581
	 *
582
	 * @return bool
583
	 */
584
	public function isDocumentUpToDate(IIndexDocument $document): bool {
585
		$this->extensionService->indexComparing($document);
586
587
		$index = $document->getIndex();
588
589
590
		if (!$this->configService->compareIndexOptions($index)) {
591
			$index->setStatus(IIndex::INDEX_CONTENT);
592
			$document->setIndex($index);
593
594
			return false;
595
		}
596
597
		if ($index->getStatus() !== IIndex::INDEX_OK) {
598
			return false;
599
		}
600
601
		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...
602
			return true;
603
		}
604
605
		return false;
606
	}
607
608
609
	/**
610
	 * @param IIndex $index
611
	 *
612
	 * @return FilesDocument
613
	 * @throws InvalidPathException
614
	 * @throws NotFoundException
615
	 * @throws FileIsNotIndexableException
616
	 */
617
	public function updateDocument(IIndex $index): FilesDocument {
618
		$this->impersonateOwner($index);
619
		$this->initFileSystems($index->getOwnerId());
620
621
		$document = $this->generateDocumentFromIndex($index);
622
		$this->updateDirectoryContentIndex($index);
623
624
		return $document;
625
	}
626
627
628
	/**
629
	 * @param FilesDocument $document
630
	 *
631
	 * @throws NotFoundException
632
	 */
633
	private function updateFilesDocument(FilesDocument $document) {
634
		$userFolder = $this->rootFolder->getUserFolder($document->getViewerId());
635
		$file = $userFolder->get($document->getPath());
636
637
		try {
638
			$this->updateFilesDocumentFromFile($document, $file);
639
		} catch (FileIsNotIndexableException $e) {
640
			$document->getIndex()
641
					 ->setStatus(IIndex::INDEX_IGNORE);
642
		}
643
	}
644
645
646
	/**
647
	 * @param FilesDocument $document
648
	 * @param Node $file
649
	 *
650
	 * @throws FileIsNotIndexableException
651
	 */
652
	private function updateFilesDocumentFromFile(FilesDocument $document, Node $file) {
653
654
		$document->getIndex()
655
				 ->setSource($document->getSource());
656
657
		$this->updateDocumentAccess($document, $file);
658
		$this->updateContentFromFile($document, $file);
659
660
		$document->addMetaTag($document->getSource());
661
	}
662
663
664
	/**
665
	 * @param FilesDocument $document
666
	 * @param Node $file
667
	 *
668
	 * @throws FileIsNotIndexableException
669
	 */
670
	private function updateDocumentAccess(FilesDocument $document, Node $file) {
671
672
//		$index = $document->getIndex();
673
		// This should not be needed, let's assume we _need_ to update document access
674
//		if (!$index->isStatus(IIndex::INDEX_FULL)
675
//			&& !$index->isStatus(IIndex::INDEX_META)) {
676
//			return;
677
//		}
678
679
		$this->localFilesService->updateDocumentAccess($document, $file);
680
		$this->externalFilesService->updateDocumentAccess($document, $file);
681
		$this->groupFoldersService->updateDocumentAccess($document, $file);
682
683
		$this->updateShareNames($document, $file);
684
	}
685
686
687
	/**
688
	 * @param FilesDocument $document
689
	 * @param Node $file
690
	 */
691
	private function updateContentFromFile(FilesDocument $document, Node $file) {
692
693
		$document->setTitle($document->getPath());
694
695
		if ((!$document->getIndex()
696
					   ->isStatus(IIndex::INDEX_CONTENT)
697
			 && !$document->getIndex()
698
						  ->isStatus(IIndex::INDEX_META)
699
			)
700
			|| $file->getType() !== FileInfo::TYPE_FILE) {
701
			return;
702
		}
703
704
		try {
705
			/** @var File $file */
706
			if ($file->getSize() <
707
				($this->configService->getAppValue(ConfigService::FILES_SIZE) * 1024 * 1024)) {
708
				$this->extractContentFromFileText($document, $file);
709
				$this->extractContentFromFileOffice($document, $file);
710
				$this->extractContentFromFilePDF($document, $file);
711
				$this->extractContentFromFileZip($document, $file);
712
713
				$this->extensionService->fileIndexing($document, $file);
714
			}
715
		} 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...
716
			$this->manageContentErrorException($document, $t);
717
		}
718
719
		if ($document->getContent() === null) {
720
			$document->getIndex()
721
					 ->unsetStatus(IIndex::INDEX_CONTENT);
722
		}
723
724
		$this->updateCommentsFromFile($document);
725
	}
726
727
728
	/**
729
	 * @param FilesDocument $document
730
	 */
731
	private function updateCommentsFromFile(FilesDocument $document) {
732
		$comments = $this->commentsManager->getForObject('files', $document->getId());
733
734
		$part = [];
735
		foreach ($comments as $comment) {
736
			$part[] = '<' . $comment->getActorId() . '> ' . $comment->getMessage();
737
		}
738
739
		$document->addPart('comments', implode(" \n ", $part));
740
	}
741
742
743
	/**
744
	 * @param FilesDocument $document
745
	 * @param Node $file
746
	 *
747
	 * @return array
748
	 */
749
	private function updateShareNames(FilesDocument $document, Node $file): array {
750
751
		$users = [];
752
753
		$this->localFilesService->getShareUsersFromFile($file, $users);
754
		$this->externalFilesService->getShareUsers($document, $users);
755
		$this->groupFoldersService->getShareUsers($document, $users);
756
757
		$shareNames = [];
758
		foreach ($users as $username) {
759
			$username = (string)$username;
760
761
			try {
762
				$user = $this->userManager->get($username);
763
				if ($user === null || $user->getLastLogin() === 0) {
764
					continue;
765
				}
766
767
				$path = $this->getPathFromViewerId($file->getId(), $username);
768
				$shareNames[$this->miscService->secureUsername($username)] =
769
					(!is_string($path)) ? $path = '' : $path;
770
771
			} catch (Exception $e) {
772
				$this->miscService->log(
773
					'Issue while getting information on documentId:' . $document->getId(), 0
774
				);
775
			}
776
		}
777
778
		$document->setInfoArray('share_names', $shareNames);
779
780
		return $shareNames;
781
	}
782
783
784
	/**
785
	 * @param string $mimeType
786
	 * @param string $extension
787
	 *
788
	 * @return string
789
	 */
790
	private function parseMimeType(string $mimeType, string $extension): string {
791
792
		$parsed = '';
793
		try {
794
			$this->parseMimeTypeText($mimeType, $extension, $parsed);
795
			$this->parseMimeTypePDF($mimeType, $parsed);
796
			$this->parseMimeTypeOffice($mimeType, $parsed);
797
			$this->parseMimeTypeZip($mimeType, $parsed);
798
		} catch (KnownFileMimeTypeException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
799
		}
800
801
		return $parsed;
802
	}
803
804
805
	/**
806
	 * @param string $mimeType
807
	 * @param string $extension
808
	 * @param string $parsed
809
	 *
810
	 * @throws KnownFileMimeTypeException
811
	 */
812
	private function parseMimeTypeText(string $mimeType, string $extension, string &$parsed) {
813
814
		if (substr($mimeType, 0, 5) === 'text/') {
815
			$parsed = self::MIMETYPE_TEXT;
816
			throw new KnownFileMimeTypeException();
817
		}
818
819
		$textMimes = [
820
			'application/epub+zip'
821
		];
822
823
		foreach ($textMimes as $mime) {
824
			if (strpos($mimeType, $mime) === 0) {
825
				$parsed = self::MIMETYPE_TEXT;
826
				throw new KnownFileMimeTypeException();
827
			}
828
		}
829
830
		$this->parseMimeTypeTextByExtension($mimeType, $extension, $parsed);
831
	}
832
833
834
	/**
835
	 * @param string $mimeType
836
	 * @param string $extension
837
	 * @param string $parsed
838
	 *
839
	 * @throws KnownFileMimeTypeException
840
	 */
841
	private function parseMimeTypeTextByExtension(
842
		string $mimeType, string $extension, string &$parsed
843
	) {
844
		$textMimes = [
845
			'application/octet-stream'
846
		];
847
		$textExtension = [
848
		];
849
850
		foreach ($textMimes as $mime) {
851
			if (strpos($mimeType, $mime) === 0
852
				&& in_array(
853
					strtolower($extension), $textExtension
854
				)) {
855
				$parsed = self::MIMETYPE_TEXT;
856
				throw new KnownFileMimeTypeException();
857
			}
858
		}
859
	}
860
861
862
	/**
863
	 * @param string $mimeType
864
	 * @param string $parsed
865
	 *
866
	 * @throws KnownFileMimeTypeException
867
	 */
868
	private function parseMimeTypePDF(string $mimeType, string &$parsed) {
869
870
		if ($mimeType === 'application/pdf') {
871
			$parsed = self::MIMETYPE_PDF;
872
			throw new KnownFileMimeTypeException();
873
		}
874
	}
875
876
877
	/**
878
	 * @param string $mimeType
879
	 * @param string $parsed
880
	 *
881
	 * @throws KnownFileMimeTypeException
882
	 */
883
	private function parseMimeTypeZip(string $mimeType, string &$parsed) {
884
		if ($mimeType === 'application/zip') {
885
			$parsed = self::MIMETYPE_ZIP;
886
			throw new KnownFileMimeTypeException();
887
		}
888
	}
889
890
891
	/**
892
	 * @param string $mimeType
893
	 * @param string $parsed
894
	 *
895
	 * @throws KnownFileMimeTypeException
896
	 */
897
	private function parseMimeTypeOffice(string $mimeType, string &$parsed) {
898
899
		$officeMimes = [
900
			'application/msword',
901
			'application/vnd.oasis.opendocument',
902
			'application/vnd.sun.xml',
903
			'application/vnd.openxmlformats-officedocument',
904
			'application/vnd.ms-word',
905
			'application/vnd.ms-powerpoint',
906
			'application/vnd.ms-excel'
907
		];
908
909
		foreach ($officeMimes as $mime) {
910
			if (strpos($mimeType, $mime) === 0) {
911
				$parsed = self::MIMETYPE_OFFICE;
912
				throw new KnownFileMimeTypeException();
913
			}
914
		}
915
	}
916
917
918
	/**
919
	 * @param FilesDocument $document
920
	 * @param File $file
921
	 */
922
	private function extractContentFromFileText(FilesDocument $document, File $file) {
923
		if ($this->parseMimeType($document->getMimeType(), $file->getExtension())
924
			!== self::MIMETYPE_TEXT) {
925
			return;
926
		}
927
928
		if (!$this->isSourceIndexable($document)) {
929
			return;
930
		}
931
932
		try {
933
			$document->setContent(
934
				base64_encode($file->getContent()), IIndexDocument::ENCODED_BASE64
935
			);
936
		} catch (NotPermittedException | LockedException $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\Lock\LockedException 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...
937
		}
938
	}
939
940
941
	/**
942
	 * @param FilesDocument $document
943
	 * @param File $file
944
	 */
945
	private function extractContentFromFilePDF(FilesDocument $document, File $file) {
946
		if ($this->parseMimeType($document->getMimeType(), $file->getExtension())
947
			!== self::MIMETYPE_PDF) {
948
			return;
949
		}
950
951
		$this->configService->setDocumentIndexOption($document, ConfigService::FILES_PDF);
952
		if (!$this->isSourceIndexable($document)) {
953
			return;
954
		}
955
956
		if ($this->configService->getAppValue(ConfigService::FILES_PDF) !== '1') {
957
			$document->setContent('');
958
959
			return;
960
		}
961
962
		try {
963
			$document->setContent(
964
				base64_encode($file->getContent()), IIndexDocument::ENCODED_BASE64
965
			);
966
		} catch (NotPermittedException | LockedException $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\Lock\LockedException 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...
967
		}
968
	}
969
970
971
	/**
972
	 * @param FilesDocument $document
973
	 * @param File $file
974
	 */
975
	private function extractContentFromFileZip(FilesDocument $document, File $file) {
976
		if ($this->parseMimeType($document->getMimeType(), $file->getExtension())
977
			!== self::MIMETYPE_ZIP) {
978
			return;
979
		}
980
981
		$this->configService->setDocumentIndexOption($document, ConfigService::FILES_ZIP);
982
		if (!$this->isSourceIndexable($document)) {
983
			return;
984
		}
985
986
		if ($this->configService->getAppValue(ConfigService::FILES_ZIP) !== '1') {
987
			$document->setContent('');
988
989
			return;
990
		}
991
992
		try {
993
			$document->setContent(
994
				base64_encode($file->getContent()), IIndexDocument::ENCODED_BASE64
995
			);
996
		} catch (NotPermittedException | LockedException $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\Lock\LockedException 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...
997
		}
998
	}
999
1000
1001
	/**
1002
	 * @param FilesDocument $document
1003
	 * @param File $file
1004
	 *
1005
	 * @throws NotPermittedException
1006
	 */
1007
	private function extractContentFromFileOffice(FilesDocument $document, File $file) {
1008
		if ($this->parseMimeType($document->getMimeType(), $file->getExtension())
1009
			!== self::MIMETYPE_OFFICE) {
1010
			return;
1011
		}
1012
1013
		$this->configService->setDocumentIndexOption($document, ConfigService::FILES_OFFICE);
1014
		if (!$this->isSourceIndexable($document)) {
1015
			return;
1016
		}
1017
1018
		if ($this->configService->getAppValue(ConfigService::FILES_OFFICE) !== '1') {
1019
			$document->setContent('');
1020
1021
			return;
1022
		}
1023
1024
		try {
1025
			$document->setContent(
1026
				base64_encode($file->getContent()), IIndexDocument::ENCODED_BASE64
1027
			);
1028
		} catch (NotPermittedException | LockedException $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\Lock\LockedException 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...
1029
		}
1030
	}
1031
1032
1033
	/**
1034
	 * @param FilesDocument $document
1035
	 *
1036
	 * @return bool
1037
	 */
1038
	private function isSourceIndexable(FilesDocument $document): bool {
1039
		$this->configService->setDocumentIndexOption($document, $document->getSource());
1040
		if ($this->configService->getAppValue($document->getSource()) !== '1') {
1041
			$document->setContent('');
1042
1043
			return false;
1044
		}
1045
1046
		return true;
1047
	}
1048
1049
1050
	/**
1051
	 * @param IIndex $index
1052
	 */
1053
	private function impersonateOwner(IIndex $index) {
1054
		if ($index->getOwnerId() !== '') {
1055
			return;
1056
		}
1057
1058
		$this->groupFoldersService->impersonateOwner($index);
1059
		$this->externalFilesService->impersonateOwner($index);
1060
	}
1061
1062
1063
	/**
1064
	 * @param $action
1065
	 * @param bool $force
1066
	 *
1067
	 * @throws Exception
1068
	 */
1069
	private function updateRunnerAction(string $action, bool $force = false) {
1070
		if ($this->runner === null) {
1071
			return;
1072
		}
1073
1074
		$this->runner->updateAction($action, $force);
1075
	}
1076
1077
1078
	/**
1079
	 * @param array $data
1080
	 */
1081
	private function updateRunnerInfo($data) {
1082
		if ($this->runner === null) {
1083
			return;
1084
		}
1085
1086
		$this->runner->setInfoArray($data);
1087
	}
1088
1089
	/**
1090
	 * @param IIndexDocument $document
1091
	 * @param Throwable $t
1092
	 */
1093
	private function manageContentErrorException(IIndexDocument $document, Throwable $t) {
1094
		$document->getIndex()
1095
				 ->addError(
1096
					 'Error while getting file content', $t->getMessage(), IIndex::ERROR_SEV_3
1097
				 );
1098
		$this->updateNewIndexError(
1099
			$document->getIndex(), 'Error while getting file content', $t->getMessage(),
1100
			IIndex::ERROR_SEV_3
1101
		);
1102
1103
		$trace = $t->getTrace();
1104
		if (is_array($trace)) {
1105
			$trace = json_encode($trace);
1106
		}
1107
		if (is_string($trace)) {
1108
			$this->miscService->log($trace, 0);
1109
		}
1110
	}
1111
1112
1113
	/**
1114
	 * @param IIndex $index
1115
	 */
1116
	private function updateDirectoryContentIndex(IIndex $index) {
1117
		if (!$index->isStatus(IIndex::INDEX_META)) {
1118
			return;
1119
		}
1120
1121
		try {
1122
			$file = $this->getFileFromIndex($index);
1123
			if ($file->getType() === File::TYPE_FOLDER) {
1124
				/** @var Folder $file */
1125
				$this->updateDirectoryMeta($file);
1126
			}
1127
		} catch (Exception $e) {
1128
		}
1129
	}
1130
1131
1132
	/**
1133
	 * @param Folder $node
1134
	 */
1135
	private function updateDirectoryMeta(Folder $node) {
1136
		try {
1137
			$files = $node->getDirectoryListing();
1138
		} 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...
1139
			return;
1140
		}
1141
1142
		foreach ($files as $file) {
1143
			try {
1144
				$this->fullTextSearchManager->updateIndexStatus(
1145
					'files', (string)$file->getId(), IIndex::INDEX_META
1146
				);
1147
			} 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...
1148
			} 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...
1149
			}
1150
		}
1151
	}
1152
1153
1154
	/**
1155
	 * @param IIndex $index
1156
	 * @param string $message
1157
	 * @param string $exception
1158
	 * @param int $sev
1159
	 */
1160
	private function updateNewIndexError(IIndex $index, string $message, string $exception, int $sev
1161
	) {
1162
		if ($this->runner === null) {
1163
			return;
1164
		}
1165
1166
		$this->runner->newIndexError($index, $message, $exception, $sev);
1167
	}
1168
1169
1170
	/**
1171
	 * @param Node $file
1172
	 *
1173
	 * @throws FileIsNotIndexableException
1174
	 */
1175
	private function isNodeIndexable(Node $file) {
1176
1177
		if ($file->getType() === File::TYPE_FOLDER) {
1178
			/** @var Folder $file */
1179
			if ($file->nodeExists('.noindex')) {
1180
				throw new FileIsNotIndexableException();
1181
			}
1182
		}
1183
1184
		$parent = $file->getParent();
1185
		$parentPath = $this->withoutBeginSlash($parent->getPath());
1186
		$path = substr($parent->getPath(), 8 + strpos($parentPath, '/'));
1187
		if (is_string($path)) {
1188
			$this->isNodeIndexable($file->getParent());
1189
		}
1190
	}
1191
1192
1193
	/**
1194
	 * @param string $path
1195
	 * @param string $userId
1196
	 *
1197
	 * @param bool $entrySlash
1198
	 *
1199
	 * @return string
1200
	 */
1201
	private function getPathFromRoot(string $path, string $userId, bool $entrySlash = false) {
1202
		// TODO: better way to do this : we remove the '/userid/files/'
1203
		// TODO: do we need userId, or can we crop the path like in isNodeIndexable()
1204
		$path = substr($path, 8 + strlen($userId));
1205
		if (!is_string($path)) {
1206
			$path = '';
1207
		}
1208
1209
		return (($entrySlash) ? '/' : '') . $path;
1210
	}
1211
1212
}
1213
1214