Passed
Pull Request — master (#77)
by Daniel
33:27
created

MiscService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
c 1
b 0
f 0
dl 0
loc 13
ccs 10
cts 10
cp 1
rs 9.9666
cc 1
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * CMS Pico - Create websites using Pico CMS for Nextcloud.
4
 *
5
 * @copyright Copyright (c) 2017, Maxence Lange (<[email protected]>)
6
 * @copyright Copyright (c) 2019, Daniel Rudolf (<[email protected]>)
7
 *
8
 * @license GNU AGPL version 3 or any later version
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License as
12
 * published by the Free Software Foundation, either version 3 of the
13
 * License, or (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
24
namespace OCA\CMSPico\Service;
25
26
use OCA\CMSPico\Files\FileInterface;
27
use OCP\Files\GenericFileException;
28
use OCP\Files\InvalidPathException;
29
use OCP\Files\NotPermittedException;
30
use OCP\Security\ISecureRandom;
31
32
class MiscService
33
{
34
	/** @var string[] */
35
	private $textFileMagic;
36
37
	/** @var string[] */
38
	private $binaryFileMagic;
39
40
	/**
41
	 * MiscService constructor.
42
	 */
43 1
	public function __construct()
44
	{
45 1
		$this->textFileMagic = [
46 1
			hex2bin('EFBBBF'),
47 1
			hex2bin('0000FEFF'),
48 1
			hex2bin('FFFE0000'),
49 1
			hex2bin('FEFF'),
50 1
			hex2bin('FFFE')
51
		];
52
53 1
		$this->binaryFileMagic = [
54 1
			'%PDF',
55 1
			hex2bin('89') . 'PNG'
56
		];
57 1
	}
58
59
	/**
60
	 * @param string $path
61
	 *
62
	 * @return string
63
	 * @throws InvalidPathException
64
	 */
65 7
	public function normalizePath(string $path): string
66
	{
67 7
		$path = str_replace('\\', '/', $path);
68 7
		$pathParts = explode('/', $path);
69
70 7
		$resultParts = [];
71 7
		foreach ($pathParts as $pathPart) {
72 7
			if (($pathPart === '') || ($pathPart === '.')) {
73 7
				continue;
74 7
			} elseif ($pathPart === '..') {
75
				if (empty($resultParts)) {
76
					throw new InvalidPathException();
77
				}
78
79
				array_pop($resultParts);
80
				continue;
81
			}
82
83 7
			$resultParts[] = $pathPart;
84
		}
85
86 7
		return implode('/', $resultParts);
87
	}
88
89
	/**
90
	 * @param string      $path
91
	 * @param string|null $basePath
92
	 * @param string|null $fileExtension
93
	 *
94
	 * @return string
95
	 * @throws InvalidPathException
96
	 */
97 2
	public function getRelativePath(string $path, string $basePath = null, string $fileExtension = null): string
98
	{
99 2
		if (!$basePath) {
100
			$basePath = \OC::$SERVERROOT;
101
		}
102
103 2
		$basePath = $this->normalizePath($basePath);
104 2
		$basePathLength = strlen($basePath);
105
106 2
		$path = $this->normalizePath($path);
107
108 2
		if ($path === $basePath) {
109
			$relativePath = '';
110 2
		} elseif (substr($path, 0, $basePathLength + 1) === $basePath . '/') {
111 2
			$relativePath = substr($path, $basePathLength + 1);
112
		} else {
113 2
			throw new InvalidPathException();
114
		}
115
116 2
		if ($fileExtension) {
117 2
			$fileName = basename($relativePath);
118 2
			$fileExtensionPos = strrpos($fileName, '.');
119 2
			if (($fileExtensionPos === false) || (substr($fileName, $fileExtensionPos) !== $fileExtension)) {
120
				throw new InvalidPathException();
121
			}
122
123 2
			return substr($relativePath, 0, strlen($relativePath) - strlen($fileExtension));
124
		}
125
126 2
		return $relativePath;
127
	}
128
129
	/**
130
	 * @param FileInterface $file
131
	 *
132
	 * @return bool
133
	 * @throws NotPermittedException
134
	 * @throws GenericFileException
135
	 */
136 3
	public function isBinaryFile(FileInterface $file): bool
137
	{
138
		try {
139 3
			$buffer = file_get_contents($file->getLocalPath(), false, null, 0, 1024);
140
		} catch (\Exception $e) {
141
			$buffer = false;
142
		}
143
144 3
		if ($buffer === false) {
145
			$buffer = substr($file->getContent(), 0, 1024);
146
		}
147
148 3
		if ($buffer === '') {
149
			return false;
150
		}
151
152 3
		foreach ($this->textFileMagic as $textFileMagic) {
153 3
			if (substr_compare($buffer, $textFileMagic, 0, strlen($textFileMagic)) === 0) {
154 3
				return false;
155
			}
156
		}
157
158 3
		foreach ($this->binaryFileMagic as $binaryFileMagic) {
159 3
			if (substr_compare($buffer, $binaryFileMagic, 0, strlen($binaryFileMagic)) === 0) {
160 3
				return true;
161
			}
162
		}
163
164 3
		return (strpos($buffer, "\0") !== false);
165
	}
166
167
	/**
168
	 * @param int    $length
169
	 * @param string $prefix
170
	 * @param string $suffix
171
	 *
172
	 * @return string
173
	 */
174 1
	public function getRandom(int $length = 10, string $prefix = '', string $suffix = ''): string
175
	{
176 1
		$randomChars = ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS;
177 1
		$random = \OC::$server->getSecureRandom()->generate($length, $randomChars);
178 1
		return ($prefix ? $prefix . '.' : '') . $random . ($suffix ? '.' . $suffix : '');
179
	}
180
181
	/**
182
	 * @param \Exception $e
183
	 * @param string     ...$classNames
184
	 *
185
	 * @throws \Exception
186
	 */
187 1
	public function consumeException(\Exception $e, string ...$classNames)
188
	{
189 1
		foreach ($classNames as $className) {
190 1
			if (is_a($e, $className)) {
191 1
				return;
192
			}
193
		}
194
195
		throw $e;
196
	}
197
}
198