Passed
Push — master ( ad6eb8...bba3a1 )
by Julius
14:09 queued 18s
created

QuotaPlugin::beforeMove()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 13
c 0
b 0
f 0
nc 4
nop 2
dl 0
loc 20
rs 9.8333
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 * @copyright Copyright (C) 2012 entreCables S.L. All rights reserved.
5
 * @copyright Copyright (C) 2012 entreCables S.L. All rights reserved.
6
 *
7
 * @author Christoph Wurst <[email protected]>
8
 * @author Felix Moeller <[email protected]>
9
 * @author Joas Schilling <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author scambra <[email protected]>
13
 * @author Thomas Müller <[email protected]>
14
 * @author Vincent Petry <[email protected]>
15
 *
16
 * @license AGPL-3.0
17
 *
18
 * This code is free software: you can redistribute it and/or modify
19
 * it under the terms of the GNU Affero General Public License, version 3,
20
 * as published by the Free Software Foundation.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
 * GNU Affero General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU Affero General Public License, version 3,
28
 * along with this program. If not, see <http://www.gnu.org/licenses/>
29
 *
30
 */
31
namespace OCA\DAV\Connector\Sabre;
32
33
use OCA\DAV\Upload\FutureFile;
34
use OCP\Files\StorageNotAvailableException;
35
use Sabre\DAV\Exception\InsufficientStorage;
36
use Sabre\DAV\Exception\ServiceUnavailable;
37
use Sabre\DAV\INode;
38
39
/**
40
 * This plugin check user quota and deny creating files when they exceeds the quota.
41
 *
42
 * @author Sergio Cambra
43
 * @copyright Copyright (C) 2012 entreCables S.L. All rights reserved.
44
 * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
45
 */
46
class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
47
	/** @var \OC\Files\View */
48
	private $view;
49
50
	/**
51
	 * Reference to main server object
52
	 *
53
	 * @var \Sabre\DAV\Server
54
	 */
55
	private $server;
56
57
	/**
58
	 * @param \OC\Files\View $view
59
	 */
60
	public function __construct($view) {
61
		$this->view = $view;
62
	}
63
64
	/**
65
	 * This initializes the plugin.
66
	 *
67
	 * This function is called by \Sabre\DAV\Server, after
68
	 * addPlugin is called.
69
	 *
70
	 * This method should set up the requires event subscriptions.
71
	 *
72
	 * @param \Sabre\DAV\Server $server
73
	 * @return void
74
	 */
75
	public function initialize(\Sabre\DAV\Server $server) {
76
		$this->server = $server;
77
78
		$server->on('beforeWriteContent', [$this, 'beforeWriteContent'], 10);
79
		$server->on('beforeCreateFile', [$this, 'beforeCreateFile'], 10);
80
		$server->on('beforeMove', [$this, 'beforeMove'], 10);
81
		$server->on('beforeCopy', [$this, 'beforeCopy'], 10);
82
	}
83
84
	/**
85
	 * Check quota before creating file
86
	 *
87
	 * @param string $uri target file URI
88
	 * @param resource $data data
89
	 * @param INode $parent Sabre Node
90
	 * @param bool $modified modified
91
	 */
92
	public function beforeCreateFile($uri, $data, INode $parent, $modified) {
0 ignored issues
show
Unused Code introduced by
The parameter $modified is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

92
	public function beforeCreateFile($uri, $data, INode $parent, /** @scrutinizer ignore-unused */ $modified) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
93
		if (!$parent instanceof Node) {
94
			return;
95
		}
96
97
		return $this->checkQuota($parent->getPath() . '/' . basename($uri));
98
	}
99
100
	/**
101
	 * Check quota before writing content
102
	 *
103
	 * @param string $uri target file URI
104
	 * @param INode $node Sabre Node
105
	 * @param resource $data data
106
	 * @param bool $modified modified
107
	 */
108
	public function beforeWriteContent($uri, INode $node, $data, $modified) {
0 ignored issues
show
Unused Code introduced by
The parameter $modified is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

108
	public function beforeWriteContent($uri, INode $node, $data, /** @scrutinizer ignore-unused */ $modified) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $data is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

108
	public function beforeWriteContent($uri, INode $node, /** @scrutinizer ignore-unused */ $data, $modified) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
109
		if (!$node instanceof Node) {
110
			return;
111
		}
112
113
		return $this->checkQuota($node->getPath());
114
	}
115
116
	/**
117
	 * Check if we're moving a Futurefile in which case we need to check
118
	 * the quota on the target destination.
119
	 *
120
	 * @param string $source source path
121
	 * @param string $destination destination path
122
	 */
123
	public function beforeMove($source, $destination) {
124
		$sourceNode = $this->server->tree->getNodeForPath($source);
125
		if (!$sourceNode instanceof FutureFile) {
126
			return;
127
		}
128
129
		// get target node for proper path conversion
130
		if ($this->server->tree->nodeExists($destination)) {
131
			$destinationNode = $this->server->tree->getNodeForPath($destination);
132
			$path = $destinationNode->getPath();
0 ignored issues
show
Bug introduced by
The method getPath() does not exist on Sabre\DAV\INode. It seems like you code against a sub-type of Sabre\DAV\INode such as OCA\DAV\Connector\Sabre\Node or OCA\DAV\Connector\Sabre\Directory or OCA\DAV\Connector\Sabre\Directory or OCA\DAV\Connector\Sabre\Directory or OCA\DAV\Connector\Sabre\Directory or OCA\DAV\Connector\Sabre\File. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

132
			/** @scrutinizer ignore-call */ 
133
   $path = $destinationNode->getPath();
Loading history...
133
		} else {
134
			$parent = dirname($destination);
135
			if ($parent === '.') {
136
				$parent = '';
137
			}
138
			$parentNode = $this->server->tree->getNodeForPath($parent);
139
			$path = $parentNode->getPath();
140
		}
141
142
		return $this->checkQuota($path, $sourceNode->getSize());
143
	}
144
145
	/**
146
	 * Check quota on the target destination before a copy.
147
	 */
148
	public function beforeCopy(string $sourcePath, string $destinationPath): bool {
149
		$sourceNode = $this->server->tree->getNodeForPath($sourcePath);
150
		if (!$sourceNode instanceof Node) {
151
			return true;
152
		}
153
154
		// get target node for proper path conversion
155
		if ($this->server->tree->nodeExists($destinationPath)) {
156
			$destinationNode = $this->server->tree->getNodeForPath($destinationPath);
157
			if (!$destinationNode instanceof Node) {
158
				return true;
159
			}
160
			$path = $destinationNode->getPath();
161
		} else {
162
			$parent = dirname($destinationPath);
163
			if ($parent === '.') {
164
				$parent = '';
165
			}
166
			$parentNode = $this->server->tree->getNodeForPath($parent);
167
			if (!$parentNode instanceof Node) {
168
				return true;
169
			}
170
			$path = $parentNode->getPath();
171
		}
172
173
		return $this->checkQuota($path, $sourceNode->getSize());
174
	}
175
176
177
	/**
178
	 * This method is called before any HTTP method and validates there is enough free space to store the file
179
	 *
180
	 * @param string $path relative to the users home
181
	 * @param int|float|null $length
182
	 * @throws InsufficientStorage
183
	 * @return bool
184
	 */
185
	public function checkQuota($path, $length = null) {
186
		if ($length === null) {
187
			$length = $this->getLength();
188
		}
189
190
		if ($length) {
191
			[$parentPath, $newName] = \Sabre\Uri\split($path);
192
			if (is_null($parentPath)) {
193
				$parentPath = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $parentPath is dead and can be removed.
Loading history...
194
			}
195
			$req = $this->server->httpRequest;
0 ignored issues
show
Unused Code introduced by
The assignment to $req is dead and can be removed.
Loading history...
196
			$freeSpace = $this->getFreeSpace($path);
197
			if ($freeSpace >= 0 && $length > $freeSpace) {
198
				throw new InsufficientStorage("Insufficient space in $path, $length required, $freeSpace available");
199
			}
200
		}
201
		return true;
202
	}
203
204
	public function getLength() {
205
		$req = $this->server->httpRequest;
206
		$length = $req->getHeader('X-Expected-Entity-Length');
207
		if (!is_numeric($length)) {
208
			$length = $req->getHeader('Content-Length');
209
			$length = is_numeric($length) ? $length : null;
210
		}
211
212
		$ocLength = $req->getHeader('OC-Total-Length');
213
		if (is_numeric($length) && is_numeric($ocLength)) {
214
			return max($length, $ocLength);
215
		}
216
217
		return $length;
218
	}
219
220
	/**
221
	 * @param string $uri
222
	 * @return mixed
223
	 * @throws ServiceUnavailable
224
	 */
225
	public function getFreeSpace($uri) {
226
		try {
227
			$freeSpace = $this->view->free_space(ltrim($uri, '/'));
228
			return $freeSpace;
229
		} catch (StorageNotAvailableException $e) {
230
			throw new ServiceUnavailable($e->getMessage());
231
		}
232
	}
233
}
234