This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Gallery |
||
4 | * |
||
5 | * This file is licensed under the Affero General Public License version 3 or |
||
6 | * later. See the COPYING file. |
||
7 | * |
||
8 | * @author Olivier Paroz <[email protected]> |
||
9 | * |
||
10 | * @copyright Olivier Paroz 2016 |
||
11 | */ |
||
12 | |||
13 | namespace OCA\Gallery\Service; |
||
14 | |||
15 | use OCP\Files\Folder; |
||
16 | use OCP\ILogger; |
||
17 | |||
18 | use OCA\Gallery\Config\ConfigParser; |
||
19 | use OCA\Gallery\Config\ConfigException; |
||
20 | use OCA\Gallery\Environment\Environment; |
||
21 | use OCA\Gallery\Preview\Preview; |
||
22 | |||
23 | /** |
||
24 | * Finds configurations files and returns a configuration array |
||
25 | * |
||
26 | * Checks the current and parent folders for configuration files and to see if we're allowed to |
||
27 | * look for media file |
||
28 | * Supports explicit inheritance |
||
29 | * |
||
30 | * @package OCA\Gallery\Service |
||
31 | */ |
||
32 | class ConfigService extends FilesService { |
||
33 | |||
34 | /** @var string */ |
||
35 | private $configName = 'gallery.cnf'; |
||
36 | /** @var array <string,bool> */ |
||
37 | private $completionStatus = ['design' => false, 'information' => false, 'sorting' => false]; |
||
38 | /** @var ConfigParser */ |
||
39 | private $configParser; |
||
40 | /** @var Preview */ |
||
41 | private $previewManager; |
||
42 | /** |
||
43 | * @todo This hard-coded array could be replaced by admin settings |
||
44 | * |
||
45 | * @var string[] |
||
46 | */ |
||
47 | private $baseMimeTypes = [ |
||
48 | 'image/png', |
||
49 | 'image/jpeg', |
||
50 | 'image/gif', |
||
51 | 'image/x-xbitmap', |
||
52 | 'image/bmp', |
||
53 | 'image/tiff', |
||
54 | 'image/x-dcraw', |
||
55 | 'image/heic', |
||
56 | 'image/heif', |
||
57 | 'application/x-photoshop', |
||
58 | 'application/illustrator', |
||
59 | 'application/postscript', |
||
60 | ]; |
||
61 | /** |
||
62 | * These types are useful for files preview in the files app, but |
||
63 | * not for the gallery side |
||
64 | * |
||
65 | * @var string[] |
||
66 | */ |
||
67 | private $slideshowMimeTypes = [ |
||
68 | 'application/font-sfnt', |
||
69 | 'application/x-font', |
||
70 | ]; |
||
71 | |||
72 | /** |
||
73 | * Constructor |
||
74 | * |
||
75 | * @param string $appName |
||
76 | * @param Environment $environment |
||
77 | * @param ConfigParser $configParser |
||
78 | * @param Preview $previewManager |
||
79 | * @param ILogger $logger |
||
80 | */ |
||
81 | public function __construct( |
||
82 | $appName, |
||
83 | Environment $environment, |
||
84 | ConfigParser $configParser, |
||
85 | Preview $previewManager, |
||
86 | ILogger $logger |
||
87 | ) { |
||
88 | parent::__construct($appName, $environment, $logger); |
||
89 | |||
90 | $this->configParser = $configParser; |
||
91 | $this->previewManager = $previewManager; |
||
92 | } |
||
93 | |||
94 | /** |
||
95 | * Returns a list of supported features |
||
96 | * |
||
97 | * @return string[] |
||
0 ignored issues
–
show
|
|||
98 | */ |
||
99 | public function getFeaturesList() { |
||
100 | $featuresList = []; |
||
101 | /** @var Folder $rootFolder */ |
||
102 | $rootFolder = $this->environment->getVirtualRootFolder(); |
||
103 | if ($this->isAllowedAndAvailable($rootFolder) && $this->configExists($rootFolder)) { |
||
104 | try { |
||
105 | $featuresList = |
||
106 | $this->configParser->getFeaturesList($rootFolder, $this->configName); |
||
107 | } catch (ConfigException $exception) { |
||
108 | $featuresList = $this->buildErrorMessage($exception, $rootFolder); |
||
109 | } |
||
110 | } |
||
111 | |||
112 | return $featuresList; |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * This builds and returns a list of all supported media types |
||
117 | * |
||
118 | * @todo Native SVG could be disabled via admin settings |
||
119 | * |
||
120 | * @param bool $extraMediaTypes |
||
121 | * @param bool $nativeSvgSupport |
||
122 | * |
||
123 | * @return string[] all supported media types |
||
124 | */ |
||
125 | public function getSupportedMediaTypes($extraMediaTypes, $nativeSvgSupport) { |
||
126 | $supportedMimes = []; |
||
127 | $wantedMimes = $this->baseMimeTypes; |
||
128 | if ($extraMediaTypes) { |
||
129 | $wantedMimes = \array_merge($wantedMimes, $this->slideshowMimeTypes); |
||
130 | } |
||
131 | foreach ($wantedMimes as $wantedMime) { |
||
132 | // Let's see if a preview of files of that media type can be generated |
||
133 | if ($this->isMimeSupported($wantedMime)) { |
||
134 | // We store the media type |
||
135 | $supportedMimes[] = $wantedMime; |
||
136 | } |
||
137 | } |
||
138 | $supportedMimes = $this->addSvgSupport($supportedMimes, $nativeSvgSupport); |
||
139 | |||
140 | //$this->logger->debug("Supported Mimes: {mimes}", ['mimes' => $supportedMimes]); |
||
141 | |||
142 | return $supportedMimes; |
||
143 | } |
||
144 | |||
145 | /** |
||
146 | * Returns the configuration of the currently selected folder |
||
147 | * |
||
148 | * * information (description, copyright) |
||
149 | * * sorting (date, name, inheritance) |
||
150 | * * design (colour) |
||
151 | * * if the album should be ignored |
||
152 | * |
||
153 | * @param Folder $folderNode the current folder |
||
154 | * @param array $features the list of features retrieved fro the configuration file |
||
155 | * |
||
156 | * @return array|null |
||
157 | * @throws ForbiddenServiceException |
||
158 | */ |
||
159 | public function getConfig($folderNode, $features) { |
||
160 | $this->features = $features; |
||
161 | list($albumConfig, $ignored) = |
||
162 | $this->collectConfig($folderNode, $this->ignoreAlbum, $this->configName); |
||
163 | if ($ignored) { |
||
164 | throw new ForbiddenServiceException( |
||
165 | 'The owner has placed a restriction or the storage location is unavailable' |
||
166 | ); |
||
167 | } |
||
168 | |||
169 | return $albumConfig; |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Throws an exception if the media type of the file is not part of what the app allows |
||
174 | * |
||
175 | * @param $mimeType |
||
176 | * |
||
177 | * @throws ForbiddenServiceException |
||
178 | */ |
||
179 | public function validateMimeType($mimeType) { |
||
180 | if (!\in_array($mimeType, $this->getSupportedMediaTypes(true, true))) { |
||
181 | throw new ForbiddenServiceException('Media type not allowed'); |
||
182 | } |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * Determines if we have a configuration file to work with |
||
187 | * |
||
188 | * @param Folder $rootFolder the virtual root folder |
||
189 | * |
||
190 | * @return bool |
||
191 | */ |
||
192 | private function configExists($rootFolder) { |
||
193 | return $rootFolder && $rootFolder->nodeExists($this->configName); |
||
194 | } |
||
195 | |||
196 | /** |
||
197 | * Adds the SVG media type if it's not already there |
||
198 | * |
||
199 | * If it's enabled, but doesn't work, an exception will be raised when trying to generate a |
||
200 | * preview. If it's disabled, we support it via the browser's native support |
||
201 | * |
||
202 | * @param string[] $supportedMimes |
||
203 | * @param bool $nativeSvgSupport |
||
204 | * |
||
205 | * @return string[] |
||
206 | */ |
||
207 | private function addSvgSupport($supportedMimes, $nativeSvgSupport) { |
||
208 | if (!\in_array('image/svg+xml', $supportedMimes) && $nativeSvgSupport) { |
||
209 | $supportedMimes[] = 'image/svg+xml'; |
||
210 | } |
||
211 | |||
212 | return $supportedMimes; |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * Returns true if the passed mime type is supported |
||
217 | * |
||
218 | * In case of a failure, we just return that the media type is not supported |
||
219 | * |
||
220 | * @param string $mimeType |
||
221 | * |
||
222 | * @return boolean |
||
223 | */ |
||
224 | private function isMimeSupported($mimeType = '*') { |
||
225 | try { |
||
226 | return $this->previewManager->isMimeSupported($mimeType); |
||
227 | } catch (\Exception $exception) { |
||
228 | unset($exception); |
||
229 | |||
230 | return false; |
||
231 | } |
||
232 | } |
||
233 | |||
234 | /** |
||
235 | * Returns an album configuration array |
||
236 | * |
||
237 | * Goes through all the parent folders until either we're told the album is private or we've |
||
238 | * reached the root folder |
||
239 | * |
||
240 | * @param Folder $folder the current folder |
||
241 | * @param string $ignoreAlbum name of the file which blacklists folders |
||
242 | * @param string $configName name of the configuration file |
||
243 | * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder |
||
244 | * @param array $configSoFar the configuration collected so far |
||
245 | * |
||
246 | * @return array <null|array,bool> |
||
247 | */ |
||
248 | private function collectConfig( |
||
249 | $folder, $ignoreAlbum, $configName, $level = 0, $configSoFar = [] |
||
250 | ) { |
||
251 | if ($folder->nodeExists($ignoreAlbum)) { |
||
252 | // Cancel as soon as we find out that the folder is private or external |
||
253 | return [null, true]; |
||
254 | } |
||
255 | $isRootFolder = $this->isRootFolder($folder, $level); |
||
256 | if ($folder->nodeExists($configName)) { |
||
257 | $configSoFar = $this->buildFolderConfig($folder, $configName, $configSoFar, $level); |
||
258 | } |
||
259 | if (!$isRootFolder) { |
||
260 | return $this->getParentConfig($folder, $ignoreAlbum, $configName, $level, $configSoFar); |
||
261 | } |
||
262 | $configSoFar = $this->validatesInfoConfig($configSoFar); |
||
263 | |||
264 | // We have reached the root folder |
||
265 | return [$configSoFar, false]; |
||
266 | } |
||
267 | |||
268 | /** |
||
269 | * Returns a parsed configuration if one was found in the current folder or generates an error |
||
270 | * message to send back |
||
271 | * |
||
272 | * @param Folder $folder the current folder |
||
273 | * @param string $configName name of the configuration file |
||
274 | * @param array $collectedConfig the configuration collected so far |
||
275 | * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder |
||
276 | * |
||
277 | * @return array |
||
278 | */ |
||
279 | private function buildFolderConfig($folder, $configName, $collectedConfig, $level) { |
||
280 | try { |
||
281 | list($collectedConfig, $completionStatus) = $this->configParser->getFolderConfig( |
||
282 | $folder, $configName, $collectedConfig, $this->completionStatus, $level |
||
283 | ); |
||
284 | $this->completionStatus = $completionStatus; |
||
285 | } catch (ConfigException $exception) { |
||
286 | $collectedConfig = $this->buildErrorMessage($exception, $folder); |
||
287 | } |
||
288 | |||
289 | return $collectedConfig; |
||
290 | } |
||
291 | |||
292 | /** |
||
293 | * Builds the error message to send back when there is an error |
||
294 | * |
||
295 | * @fixme Missing translation |
||
296 | * |
||
297 | * @param ConfigException $exception |
||
298 | * @param Folder $folder the current folder |
||
299 | * |
||
300 | * @return array<array<string,string>,bool> |
||
0 ignored issues
–
show
|
|||
301 | */ |
||
302 | private function buildErrorMessage($exception, $folder) { |
||
303 | $configPath = $this->environment->getPathFromVirtualRoot($folder); |
||
304 | $errorMessage = $exception->getMessage() . ". Config location: /$configPath"; |
||
305 | $this->logger->error($errorMessage); |
||
306 | $config = ['error' => ['message' => $errorMessage]]; |
||
307 | |||
308 | $completionStatus = $this->completionStatus; |
||
309 | foreach ($completionStatus as $key) { |
||
310 | $completionStatus[$key] = true; |
||
311 | } |
||
312 | $this->completionStatus = $completionStatus; |
||
313 | |||
314 | return [$config]; |
||
315 | } |
||
316 | |||
317 | /** |
||
318 | * Removes links if they were collected outside of the virtual root |
||
319 | * |
||
320 | * This is for shared folders which have a virtual root |
||
321 | * |
||
322 | * @param array $albumConfig |
||
323 | * |
||
324 | * @return array |
||
325 | */ |
||
326 | private function validatesInfoConfig($albumConfig) { |
||
327 | $this->virtualRootLevel; |
||
328 | if (\array_key_exists('information', $albumConfig)) { |
||
329 | $info = $albumConfig['information']; |
||
330 | if (\array_key_exists('level', $info)) { |
||
331 | $level = $info['level']; |
||
332 | if ($level > $this->virtualRootLevel) { |
||
333 | $albumConfig['information']['description_link'] = null; |
||
334 | $albumConfig['information']['copyright_link'] = null; |
||
335 | } |
||
336 | } |
||
337 | } |
||
338 | |||
339 | return $albumConfig; |
||
340 | } |
||
341 | |||
342 | /** |
||
343 | * Looks for an album configuration in the parent folder |
||
344 | * |
||
345 | * We will look up to the virtual root of a shared folder, for privacy reasons |
||
346 | * |
||
347 | * @param Folder $folder the current folder |
||
348 | * @param string $privacyChecker name of the file which blacklists folders |
||
349 | * @param string $configName name of the configuration file |
||
350 | * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder |
||
351 | * @param array $collectedConfig the configuration collected so far |
||
352 | * |
||
353 | * @return array<null|array,bool> |
||
354 | */ |
||
355 | private function getParentConfig($folder, $privacyChecker, $configName, $level, $collectedConfig |
||
356 | ) { |
||
357 | $parentFolder = $folder->getParent(); |
||
358 | $level++; |
||
359 | |||
360 | return $this->collectConfig( |
||
361 | $parentFolder, $privacyChecker, $configName, $level, $collectedConfig |
||
362 | ); |
||
363 | } |
||
364 | } |
||
365 |
This check compares the return type specified in the
@return
annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.If the return type contains the type array, this check recommends the use of a more specific type like
String[]
orarray<String>
.