Completed
Push — master ( 8b9e59...b8ecd8 )
by Maxence
04:05 queued 02:02
created

FilesService::getPathFromRoot()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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