Completed
Pull Request — master (#315)
by
unknown
02:46
created

FilesService::getEXIF()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 13
nc 7
nop 1
1
<?php
2
/**
3
 * Nextcloud - 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 2017
11
 */
12
13
namespace OCA\Gallery\Service;
14
15
use OCP\Files\File;
16
use OCP\Files\Folder;
17
use OCP\Files\Node;
18
19
/**
20
 * Contains various methods to retrieve information from the filesystem
21
 *
22
 * @package OCA\Gallery\Service
23
 */
24
abstract class FilesService extends Service {
25
26
	/** @var int */
27
	protected $virtualRootLevel = null;
28
	/** @var string[] */
29
	protected $features;
30
	/** @var string */
31
	protected $ignoreAlbum = '.nomedia';
32
33
	/**
34
	 * Retrieves all files and sub-folders contained in a folder
35
	 *
36
	 * If we can't find anything in the current folder, we throw an exception as there is no point
37
	 * in doing any more work, but if we're looking at a sub-folder, we return an empty array so
38
	 * that it can be simply ignored
39
	 *
40
	 * @param Folder $folder
41
	 * @param int $subDepth
42
	 *
43
	 * @return array
44
	 */
45
	protected function getNodes($folder, $subDepth) {
46
		try {
47
			$nodes = $folder->getDirectoryListing();
48
		} catch (\Exception $exception) {
49
			$nodes = $this->recoverFromGetNodesError($subDepth, $exception);
50
		}
51
52
		return $nodes;
53
	}
54
55
	/**
56
	 * Determines if the files are hosted locally (shared or not) and can be used by the preview
57
	 * system
58
	 *
59
	 * isMounted() doesn't include externally hosted shares, so we need to exclude those from the
60
	 * non-mounted nodes
61
	 *
62
	 * @param Node $node
63
	 *
64
	 * @return bool
65
	 */
66
	protected function isAllowedAndAvailable($node) {
67
		try {
68
			return $node && $this->isAllowed($node) && $this->isAvailable($node);
69
		} catch (\Exception $exception) {
70
			$message = 'The folder is not available: ' . $exception->getMessage();
71
			$this->logger->error($message);
72
73
			return false;
74
		}
75
	}
76
77
	/**
78
	 * Returns the node type, either 'dir' or 'file'
79
	 *
80
	 * If there is a problem, we return an empty string so that the node can be ignored
81
	 *
82
	 * @param Node $node
83
	 *
84
	 * @return string
85
	 */
86
	protected function getNodeType($node) {
87
		try {
88
			$nodeType = $node->getType();
89
		} catch (\Exception $exception) {
90
			return '';
91
		}
92
93
		return $nodeType;
94
	}
95
96
	/**
97
	 * Returns various information about a node
98
	 *
99
	 * @param Node|File|Folder $node
100
	 *
101
	 * @return array<string,int|string|bool|array<string,int|string>>
102
	 */
103
	protected function getNodeData($node) {
104
		$imagePath = $this->environment->getPathFromVirtualRoot($node);
105
		$nodeId = $node->getId();
106
		$mTime = $node->getMTime();
107
		$etag = $node->getEtag();
108
		$size = $node->getSize();
109
		$sharedWithUser = $node->isShared();
110
		$ownerData = $this->getOwnerData($node);
111
		$permissions = $node->getPermissions();
112
		$exif = $this->getEXIF('data'.$node->getPath());
113
114
		//$this->logger->debug("Image path : {var1}", ['var1' => $imagePath]);
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
115
116
		return $this->formatNodeData(
117
			$imagePath, $nodeId, $mTime, $etag, $size, $sharedWithUser, $ownerData, $permissions, $exif
118
		);
119
	}
120
121
	/**
122
	 * Returns specific EXIF data for given path (null if directory)
123
	 *
124
	 * @param string $path
125
	 *
126
	 * @return null|array<string,int|string>
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use null|array<string,integer>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
127
	 */
128
	protected function getEXIF($path) {
129
		if (is_dir($path)) {
130
			return null;
131
		}
132
		$taken = null;
133
		$data = @exif_read_data($path, 'EXIF');
134
		if ($data) {
135
			$value = @$data['DateTimeOriginal'];    // -> 'date taken' field
136
			if ($value) {
137
				$date = date_parse_from_format('Y:m:d H:i:s', $value);
138
				$taken = mktime(
139
					$date['hour'],  $date['minute'], $date['second'],
140
					$date['month'], $date['day'],    $date['year']
141
				);
142
			}
143
		}
144
		return ['taken_date' => ($taken ? $taken : 0)];
145
	}
146
147
	/**
148
	 * Returns various information about a folder
149
	 *
150
	 * @param Folder $node
151
	 *
152
	 * @return array<string,int|string|bool|array<string,int|string>>
153
	 */
154
	protected function getFolderData($node) {
155
		$folderData = $this->getNodeData($node);
156
		$folderData['freespace'] = $node->getFreeSpace();
157
158
		return $folderData;
159
	}
160
161
	/**
162
	 * Returns the node if it's a folder we have access to
163
	 *
164
	 * @param Folder $node
165
	 * @param string $nodeType
166
	 *
167
	 * @return array|Folder
168
	 */
169
	protected function getAllowedSubFolder($node, $nodeType) {
170
		if ($nodeType === 'dir') {
171
			/** @var Folder $node */
172
			if (!$node->nodeExists($this->ignoreAlbum)) {
173
				return [$node];
174
			}
175
		}
176
177
		return [];
178
	}
179
180
	/**
181
	 * Determines if we've reached the root folder
182
	 *
183
	 * @param Folder $folder
184
	 * @param int $level
185
	 *
186
	 * @return bool
187
	 */
188
	protected function isRootFolder($folder, $level) {
189
		$isRootFolder = false;
190
		$rootFolder = $this->environment->getVirtualRootFolder();
191
		if ($folder->getPath() === $rootFolder->getPath()) {
192
			$isRootFolder = true;
193
		}
194
		$virtualRootFolder = $this->environment->getPathFromVirtualRoot($folder);
195
		if (empty($virtualRootFolder)) {
196
			$this->virtualRootLevel = $level;
197
		}
198
199
		return $isRootFolder;
200
	}
201
202
	/**
203
	 * Throws an exception if this problem occurs in the current folder, otherwise just ignores the
204
	 * sub-folder
205
	 *
206
	 * @param int $subDepth
207
	 * @param \Exception $exception
208
	 *
209
	 * @return array
210
	 * @throws NotFoundServiceException
211
	 */
212
	private function recoverFromGetNodesError($subDepth, $exception) {
213
		if ($subDepth === 0) {
214
			throw new NotFoundServiceException($exception->getMessage());
215
		}
216
217
		return [];
218
	}
219
220
	/**
221
	 * Determines if we can consider the node mounted locally or if it's been authorised to be
222
	 * scanned
223
	 *
224
	 * @param Node $node
225
	 *
226
	 * @return bool
227
	 */
228
	private function isAllowed($node) {
229
		$allowed = true;
230
		if ($this->isExternalShare($node)) {
231
			$allowed = $this->isExternalShareAllowed();
232
		}
233
234
		if ($node->isMounted()) {
235
			$mount = $node->getMountPoint();
236
			$allowed = $mount && $mount->getOption('previews', true);
237
		}
238
239
		return $allowed;
240
	}
241
242
	/**
243
	 * Determines if the node is available, as in readable
244
	 *
245
	 * @todo Test to see by how much using file_exists slows things down
246
	 *
247
	 * @param Node $node
248
	 *
249
	 * @return bool
250
	 */
251
	private function isAvailable($node) {
252
		return $node->isReadable();
253
	}
254
255
	/**
256
	 * Determines if the user has allowed the use of external shares
257
	 *
258
	 * @return bool
259
	 */
260
	private function isExternalShareAllowed() {
261
		$rootFolder = $this->environment->getVirtualRootFolder();
262
263
		return ($this->isExternalShare($rootFolder)
0 ignored issues
show
Bug introduced by
It seems like $rootFolder defined by $this->environment->getVirtualRootFolder() on line 261 can also be of type null; however, OCA\Gallery\Service\File...vice::isExternalShare() does only seem to accept object<OCP\Files\Node>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
264
				|| in_array('external_shares', $this->features));
265
	}
266
267
	/**
268
	 * Determines if the node is a share which is hosted externally
269
	 *
270
	 *
271
	 * @param Node $node
272
	 *
273
	 * @return bool
274
	 */
275
	private function isExternalShare($node) {
276
		$sid = explode(
277
			':',
278
			$node->getStorage()
279
				 ->getId()
280
		);
281
282
		return ($sid[0] === 'shared' && $sid[2][0] !== '/');
283
	}
284
285
	/**
286
	 * Returns what we known about the owner of a node
287
	 *
288
	 * @param Node $node
289
	 *
290
	 * @return null|array<string,int|string>
0 ignored issues
show
Documentation introduced by
Should the return type not be array? Also, consider making the array more specific, something like array<String>, or String[].

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[] or array<String>.

Loading history...
291
	 */
292
	private function getOwnerData($node) {
293
		$owner = $node->getOwner();
294
		$ownerData = [];
295
		if ($owner) {
296
			$ownerData = [
297
				'uid'         => $owner->getUID(),
298
				'displayname' => $owner->getDisplayName()
299
			];
300
		}
301
302
		return $ownerData;
303
	}
304
305
	/**
306
	 * Returns an array containing information about a node
307
	 *
308
	 * @param string $imagePath
309
	 * @param int $nodeId
310
	 * @param int $mTime
311
	 * @param string $etag
312
	 * @param int $size
313
	 * @param bool $sharedWithUser
314
	 * @param array <string,int|string> $ownerData
315
	 * @param int $permissions
316
	 *
317
	 * @return array
318
	 */
319
	private function formatNodeData(
320
		$imagePath, $nodeId, $mTime, $etag, $size, $sharedWithUser, $ownerData, $permissions, $exif
321
	) {
322
		return [
323
			'path'           => $imagePath,
324
			'nodeid'         => $nodeId,
325
			'mtime'          => $mTime,
326
			'etag'           => $etag,
327
			'size'           => $size,
328
			'sharedwithuser' => $sharedWithUser,
329
			'owner'          => $ownerData,
330
			'permissions'    => $permissions,
331
			'exif'           => $exif
332
		];
333
	}
334
335
}
336