1<?php
2/**
3 * @author Piotr Mrowczynski piotr@owncloud.com
4 *
5 * @copyright Copyright (c) 2019, ownCloud GmbH
6 * @license AGPL-3.0
7 *
8 * This code is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License, version 3,
10 * as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License, version 3,
18 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19 *
20 */
21
22namespace OCA\Files_Sharing;
23
24use OCP\Files\File;
25use OCP\Files\Folder;
26use OCP\Files\Node;
27use OCP\Files\NotFoundException;
28
29/**
30 * Handles restricting for download of files
31 */
32class ViewOnly {
33
34	/** @var Folder */
35	private $userFolder;
36
37	public function __construct(Folder $userFolder) {
38		$this->userFolder = $userFolder;
39	}
40
41	/**
42	 * @param string[] $pathsToCheck
43	 * @return bool
44	 */
45	public function check($pathsToCheck) {
46		// If any of elements cannot be downloaded, prevent whole download
47		foreach ($pathsToCheck as $file) {
48			try {
49				$info = $this->userFolder->get($file);
50				if ($info instanceof File) {
51					// access to filecache is expensive in the loop
52					if (!$this->checkFileInfo($info)) {
53						return false;
54					}
55				} elseif ($info instanceof Folder) {
56					// get directory content is rather cheap query
57					if (!$this->dirRecursiveCheck($info)) {
58						return false;
59					}
60				}
61			} catch (NotFoundException $e) {
62				continue;
63			}
64		}
65		return true;
66	}
67
68	/**
69	 * @param Folder $dirInfo
70	 * @return bool
71	 * @throws NotFoundException
72	 */
73	private function dirRecursiveCheck(Folder $dirInfo) {
74		if (!$this->checkFileInfo($dirInfo)) {
75			return false;
76		}
77		// If any of elements cannot be downloaded, prevent whole download
78		$files = $dirInfo->getDirectoryListing();
79		foreach ($files as $file) {
80			if ($file instanceof File) {
81				if (!$this->checkFileInfo($file)) {
82					return false;
83				}
84			} elseif ($file instanceof Folder) {
85				return $this->dirRecursiveCheck($file);
86			}
87		}
88
89		return true;
90	}
91
92	/**
93	 * @param Node $fileInfo
94	 * @return bool
95	 * @throws NotFoundException
96	 */
97	private function checkFileInfo(Node $fileInfo) {
98		// Restrict view-only to nodes which are shared
99		$storage = $fileInfo->getStorage();
100		if (!$storage->instanceOfStorage(SharedStorage::class)) {
101			return true;
102		}
103
104		// Extract extra permissions
105		/** @var \OCA\Files_Sharing\SharedStorage $storage */
106		'@phan-var \OCA\Files_Sharing\SharedStorage $storage';
107		$share = $storage->getShare();
108
109		// Check if read-only and on whether permission can download is both set and disabled.
110
111		$canDownload = $share->getAttributes()->getAttribute('permissions', 'download');
112		if ($canDownload !== null && !$canDownload) {
113			return false;
114		}
115		return true;
116	}
117}
118