Completed
Pull Request — master (#35)
by Maxence
01:38
created

FilesService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
cc 1
nc 1
nop 11

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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