Welcome to mirror list, hosted at ThFree Co, Russian Federation.

configservice.php « service - github.com/nextcloud/gallery.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 0ee75200d3e108d182463f9148830dbc08f77c70 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
<?php
/**
 * ownCloud - gallery
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Olivier Paroz <owncloud@interfasys.ch>
 *
 * @copyright Olivier Paroz 2015
 */

namespace OCA\Gallery\Service;

use OCP\Files\Folder;
use OCP\ILogger;

use OCA\Gallery\Config\ConfigParser;
use OCA\Gallery\Config\ConfigException;
use OCA\Gallery\Environment\Environment;
use OCA\Gallery\Preview\Preview;

/**
 * Finds configurations files and returns a configuration array
 *
 * Checks the current and parent folders for configuration files and the privacy flag
 * Supports explicit inheritance
 *
 * @package OCA\Gallery\Service
 */
class ConfigService extends FilesService {

	/**
	 * @var string
	 */
	private $configName = 'gallery.cnf';
	/**
	 * @var string
	 */
	private $privacyChecker = '.nomedia';
	/**
	 * @var array <string,bool>
	 */
	private $completionStatus = ['design' => false, 'information' => false, 'sorting' => false];
	/**
	 * @var ConfigParser
	 */
	private $configParser;
	/** @var Preview */
	private $previewManager;
	/**
	 * @todo This hard-coded array could be replaced by admin settings
	 *
	 * @var string[]
	 */
	private $baseMimeTypes = [
		'image/png',
		'image/jpeg',
		'image/gif',
		'image/x-xbitmap',
		'image/bmp',
		'image/tiff',
		'image/x-dcraw',
		'application/x-photoshop',
		'application/illustrator',
		'application/postscript',
	];
	/**
	 * These types are useful for files preview in the files app, but
	 * not for the gallery side
	 *
	 * @var string[]
	 */
	private $slideshowMimeTypes = [
		'application/font-sfnt',
		'application/x-font',
	];

	/**
	 * Constructor
	 *
	 * @param string $appName
	 * @param Environment $environment
	 * @param ConfigParser $configParser
	 * @param Preview $previewManager
	 * @param ILogger $logger
	 */
	public function __construct(
		$appName,
		Environment $environment,
		ConfigParser $configParser,
		Preview $previewManager,
		ILogger $logger
	) {
		parent::__construct($appName, $environment, $logger);

		$this->configParser = $configParser;
		$this->previewManager = $previewManager;
	}

	/**
	 * Returns a list of supported features
	 *
	 * @return string[]
	 */
	public function getFeaturesList() {
		$featuresList = [];
		/** @var Folder $rootFolder */
		$rootFolder = $this->environment->getVirtualRootFolder();
		if ($this->isAllowedAndAvailable($rootFolder) && $this->configExists($rootFolder)) {
			try {
				$featuresList =
					$this->configParser->getFeaturesList($rootFolder, $this->configName);
			} catch (ConfigException $exception) {
				$featuresList = $this->buildErrorMessage($exception, $rootFolder);
			}
		}

		return $featuresList;
	}

	/**
	 * This builds and returns a list of all supported media types
	 *
	 * @todo Native SVG could be disabled via admin settings
	 *
	 * @param bool $extraMediaTypes
	 * @param bool $nativeSvgSupport
	 *
	 * @return string[] all supported media types
	 */
	public function getSupportedMediaTypes($extraMediaTypes, $nativeSvgSupport) {
		$supportedMimes = [];
		$wantedMimes = $this->baseMimeTypes;
		if ($extraMediaTypes) {
			$wantedMimes = array_merge($wantedMimes, $this->slideshowMimeTypes);
		}
		foreach ($wantedMimes as $wantedMime) {
			// Let's see if a preview of files of that media type can be generated
			if ($this->isMimeSupported($wantedMime)) {
				// We store the media type
				$supportedMimes[] = $wantedMime;
			}
		}
		$supportedMimes = $this->addSvgSupport($supportedMimes, $nativeSvgSupport);

		//$this->logger->debug("Supported Mimes: {mimes}", ['mimes' => $supportedMimes]);

		return $supportedMimes;
	}

	/**
	 * Returns information about the currently selected folder
	 *
	 *    * privacy setting
	 *    * special configuration
	 *    * permissions
	 *    * ID
	 *
	 * @param Folder $folderNode the current folder
	 * @param string $folderPathFromRoot path from the current folder to the virtual root
	 * @param array $features the list of features retrieved fro the configuration file
	 *
	 * @return array|null
	 * @throws ForbiddenServiceException
	 */
	public function getAlbumInfo($folderNode, $folderPathFromRoot, $features) {
		$this->features = $features;
		list ($albumConfig, $privateAlbum) =
			$this->getAlbumConfig($folderNode, $this->privacyChecker, $this->configName);
		if ($privateAlbum) {
			throw new ForbiddenServiceException('Album is private or unavailable');
		}
		$albumInfo = [
			'path'        => $folderPathFromRoot,
			'fileid'      => $folderNode->getID(),
			'permissions' => $folderNode->getPermissions(),
			'etag'        => $folderNode->getEtag()
		];
		// There is always an albumInfo, but the albumConfig may be empty
		$albumConfig = array_merge($albumInfo, $albumConfig);

		return $albumConfig;
	}

	/**
	 * Throws an exception if the media type of the file is not part of what the app allows
	 *
	 * @param $mimeType
	 *
	 * @throws ForbiddenServiceException
	 */
	public function validateMimeType($mimeType) {
		if (!in_array($mimeType, $this->getSupportedMediaTypes(true, true))) {
			throw new ForbiddenServiceException('Media type not allowed');
		}
	}

	/**
	 * Determines if we have a configuration file to work with
	 *
	 * @param Folder $rootFolder the virtual root folder
	 *
	 * @return bool
	 */
	private function configExists($rootFolder) {
		return $rootFolder && $rootFolder->nodeExists($this->configName);
	}

	/**
	 * Adds the SVG media type if it's not already there
	 *
	 * If it's enabled, but doesn't work, an exception will be raised when trying to generate a
	 * preview. If it's disabled, we support it via the browser's native support
	 *
	 * @param string[] $supportedMimes
	 * @param bool $nativeSvgSupport
	 *
	 * @return string[]
	 */
	private function addSvgSupport($supportedMimes, $nativeSvgSupport) {
		if (!in_array('image/svg+xml', $supportedMimes) && $nativeSvgSupport) {
			$supportedMimes[] = 'image/svg+xml';
		}

		return $supportedMimes;
	}

	/**
	 * Returns true if the passed mime type is supported
	 *
	 * In case of a failure, we just return that the media type is not supported
	 *
	 * @param string $mimeType
	 *
	 * @return boolean
	 */
	private function isMimeSupported($mimeType = '*') {
		try {
			return $this->previewManager->isMimeSupported($mimeType);
		} catch (\Exception $exception) {
			unset($exception);

			return false;
		}
	}

	/**
	 * Returns an album configuration array
	 *
	 * Goes through all the parent folders until either we're told the album is private or we've
	 * reached the root folder
	 *
	 * @param Folder $folder the current folder
	 * @param string $privacyChecker name of the file which blacklists folders
	 * @param string $configName name of the configuration file
	 * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder
	 * @param array $config the configuration collected so far
	 *
	 * @return array<null|array,bool>
	 */
	private function getAlbumConfig(
		$folder, $privacyChecker, $configName, $level = 0, $config = []
	) {
		if ($folder->nodeExists($privacyChecker)) {
			// Cancel as soon as we find out that the folder is private or external
			return [null, true];
		}
		$isRootFolder = $this->isRootFolder($folder, $level);
		if ($folder->nodeExists($configName)) {
			$config = $this->buildFolderConfig($folder, $configName, $config, $level);
		}
		if (!$isRootFolder) {
			return $this->getParentConfig(
				$folder, $privacyChecker, $configName, $level, $config
			);
		}
		$config = $this->validatesInfoConfig($config);

		// We have reached the root folder
		return [$config, false];
	}

	/**
	 * Returns a parsed configuration if one was found in the current folder or generates an error
	 * message to send back
	 *
	 * @param Folder $folder the current folder
	 * @param string $configName name of the configuration file
	 * @param array $config the configuration collected so far
	 * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder
	 *
	 * @return array
	 */
	private function buildFolderConfig($folder, $configName, $config, $level) {
		try {
			list($config, $completionStatus) = $this->configParser->getFolderConfig(
				$folder, $configName, $config, $this->completionStatus, $level
			);
			$this->completionStatus = $completionStatus;
		} catch (ConfigException $exception) {
			$config = $this->buildErrorMessage($exception, $folder);
		}

		return $config;
	}

	/**
	 * Builds the error message to send back when there is an error
	 *
	 * @fixme Missing translation
	 *
	 * @param ConfigException $exception
	 * @param Folder $folder the current folder
	 *
	 * @return array<array<string,string>,bool>
	 */
	private function buildErrorMessage($exception, $folder) {
		$configPath = $this->environment->getPathFromVirtualRoot($folder);
		$errorMessage = $exception->getMessage() . ". Config location: /$configPath";
		$this->logger->error($errorMessage);
		$config = ['error' => ['message' => $errorMessage]];

		$completionStatus = $this->completionStatus;
		foreach ($completionStatus as $key) {
			$completionStatus[$key] = true;
		}
		$this->completionStatus = $completionStatus;

		return [$config];
	}

	/**
	 * Removes links if they were collected outside of the virtual root
	 *
	 * This is for shared folders which have a virtual root
	 *
	 * @param array $albumConfig
	 *
	 * @return array
	 */
	private function validatesInfoConfig($albumConfig) {
		$this->virtualRootLevel;
		if (array_key_exists('information', $albumConfig)) {
			$info = $albumConfig['information'];
			if (array_key_exists('level', $info)) {
				$level = $info['level'];
				if ($level > $this->virtualRootLevel) {
					$albumConfig['information']['description_link'] = null;
					$albumConfig['information']['copyright_link'] = null;
				}
			}
		}

		return $albumConfig;
	}

	/**
	 * Looks for an album configuration in the parent folder
	 *
	 * We will look up to the virtual root of a shared folder, for privacy reasons
	 *
	 * @param Folder $folder the current folder
	 * @param string $privacyChecker name of the file which blacklists folders
	 * @param string $configName name of the configuration file
	 * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder
	 * @param array $config the configuration collected so far
	 *
	 * @return array<null|array,bool>
	 */
	private function getParentConfig($folder, $privacyChecker, $configName, $level, $config) {
		$parentFolder = $folder->getParent();
		$level++;

		return $this->getAlbumConfig(
			$parentFolder, $privacyChecker, $configName, $level, $config
		);
	}

}