Passed
Push — master ( 22ff60...cd5630 )
by Morris
24:47 queued 11:45
created

Quota::shouldApplyQuota()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 2
rs 10
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Jörn Friedrich Dreyer <[email protected]>
6
 * @author Julius Härtl <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Robin McCorkell <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Vincent Petry <[email protected]>
13
 *
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program. If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OC\Files\Storage\Wrapper;
31
32
use OC\Files\Filesystem;
33
use OCP\Files\Cache\ICacheEntry;
34
use OCP\Files\Storage\IStorage;
35
36
class Quota extends Wrapper {
37
38
	/**
39
	 * @var int $quota
40
	 */
41
	protected $quota;
42
43
	/**
44
	 * @var string $sizeRoot
45
	 */
46
	protected $sizeRoot;
47
48
	private $config;
49
50
	/**
51
	 * @param array $parameters
52
	 */
53
	public function __construct($parameters) {
54
		parent::__construct($parameters);
55
		$this->quota = $parameters['quota'];
56
		$this->sizeRoot = isset($parameters['root']) ? $parameters['root'] : '';
57
		$this->config = \OC::$server->getSystemConfig();
0 ignored issues
show
Deprecated Code introduced by
The function OC\Server::getSystemConfig() has been deprecated. ( Ignorable by Annotation )

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

57
		$this->config = /** @scrutinizer ignore-deprecated */ \OC::$server->getSystemConfig();
Loading history...
58
	}
59
60
	/**
61
	 * @return int quota value
62
	 */
63
	public function getQuota() {
64
		return $this->quota;
65
	}
66
67
	/**
68
	 * @param string $path
69
	 * @param \OC\Files\Storage\Storage $storage
70
	 */
71
	protected function getSize($path, $storage = null) {
72
		if ($this->config->getValue('quota_include_external_storage', false)) {
73
			$rootInfo = Filesystem::getFileInfo('', 'ext');
0 ignored issues
show
Bug introduced by
'ext' of type string is incompatible with the type boolean expected by parameter $includeMountPoints of OC\Files\Filesystem::getFileInfo(). ( Ignorable by Annotation )

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

73
			$rootInfo = Filesystem::getFileInfo('', /** @scrutinizer ignore-type */ 'ext');
Loading history...
74
			return $rootInfo->getSize(true);
75
		} else {
76
			if (is_null($storage)) {
77
				$cache = $this->getCache();
78
			} else {
79
				$cache = $storage->getCache();
80
			}
81
			$data = $cache->get($path);
82
			if ($data instanceof ICacheEntry and isset($data['size'])) {
83
				return $data['size'];
84
			} else {
85
				return \OCP\Files\FileInfo::SPACE_NOT_COMPUTED;
86
			}
87
		}
88
	}
89
90
	/**
91
	 * Get free space as limited by the quota
92
	 *
93
	 * @param string $path
94
	 * @return int
95
	 */
96
	public function free_space($path) {
97
		if ($this->quota < 0 || strpos($path, 'cache') === 0 || strpos($path, 'uploads') === 0) {
98
			return $this->storage->free_space($path);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->storage->free_space($path) could also return false which is incompatible with the documented return type integer. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
99
		} else {
100
			$used = $this->getSize($this->sizeRoot);
101
			if ($used < 0) {
102
				return \OCP\Files\FileInfo::SPACE_NOT_COMPUTED;
103
			} else {
104
				$free = $this->storage->free_space($path);
105
				$quotaFree = max($this->quota - $used, 0);
106
				// if free space is known
107
				if ($free >= 0) {
108
					$free = min($free, $quotaFree);
109
				} else {
110
					$free = $quotaFree;
111
				}
112
				return $free;
113
			}
114
		}
115
	}
116
117
	/**
118
	 * see http://php.net/manual/en/function.file_put_contents.php
119
	 *
120
	 * @param string $path
121
	 * @param string $data
122
	 * @return bool
123
	 */
124
	public function file_put_contents($path, $data) {
125
		$free = $this->free_space($path);
126
		if ($free < 0 or strlen($data) < $free) {
127
			return $this->storage->file_put_contents($path, $data);
128
		} else {
129
			return false;
130
		}
131
	}
132
133
	/**
134
	 * see http://php.net/manual/en/function.copy.php
135
	 *
136
	 * @param string $source
137
	 * @param string $target
138
	 * @return bool
139
	 */
140
	public function copy($source, $target) {
141
		$free = $this->free_space($target);
142
		if ($free < 0 or $this->getSize($source) < $free) {
143
			return $this->storage->copy($source, $target);
144
		} else {
145
			return false;
146
		}
147
	}
148
149
	/**
150
	 * see http://php.net/manual/en/function.fopen.php
151
	 *
152
	 * @param string $path
153
	 * @param string $mode
154
	 * @return resource
155
	 */
156
	public function fopen($path, $mode) {
157
		$source = $this->storage->fopen($path, $mode);
158
159
		// don't apply quota for part files
160
		if (!$this->isPartFile($path)) {
161
			$free = $this->free_space($path);
162
			if ($source && $free >= 0 && $mode !== 'r' && $mode !== 'rb') {
0 ignored issues
show
introduced by
$source is of type false|resource, thus it always evaluated to false.
Loading history...
163
				// only apply quota for files, not metadata, trash or others
164
				if ($this->shouldApplyQuota($path)) {
165
					return \OC\Files\Stream\Quota::wrap($source, $free);
166
				}
167
			}
168
		}
169
		return $source;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $source could also return false which is incompatible with the documented return type resource. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
170
	}
171
172
	/**
173
	 * Checks whether the given path is a part file
174
	 *
175
	 * @param string $path Path that may identify a .part file
176
	 * @return string File path without .part extension
177
	 * @note this is needed for reusing keys
178
	 */
179
	private function isPartFile($path) {
180
		$extension = pathinfo($path, PATHINFO_EXTENSION);
181
182
		return ($extension === 'part');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extension === 'part' returns the type boolean which is incompatible with the documented return type string.
Loading history...
183
	}
184
185
	/**
186
	 * Only apply quota for files, not metadata, trash or others
187
	 */
188
	private function shouldApplyQuota(string $path): bool {
189
		return strpos(ltrim($path, '/'), 'files/') === 0;
190
	}
191
192
	/**
193
	 * @param IStorage $sourceStorage
194
	 * @param string $sourceInternalPath
195
	 * @param string $targetInternalPath
196
	 * @return bool
197
	 */
198
	public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
199
		$free = $this->free_space($targetInternalPath);
200
		if ($free < 0 or $this->getSize($sourceInternalPath, $sourceStorage) < $free) {
201
			return $this->storage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
202
		} else {
203
			return false;
204
		}
205
	}
206
207
	/**
208
	 * @param IStorage $sourceStorage
209
	 * @param string $sourceInternalPath
210
	 * @param string $targetInternalPath
211
	 * @return bool
212
	 */
213
	public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
214
		$free = $this->free_space($targetInternalPath);
215
		if ($free < 0 or $this->getSize($sourceInternalPath, $sourceStorage) < $free) {
216
			return $this->storage->moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
217
		} else {
218
			return false;
219
		}
220
	}
221
222
	public function mkdir($path) {
223
		$free = $this->free_space($path);
224
		if ($this->shouldApplyQuota($path) && $free === 0.0) {
225
			return false;
226
		}
227
228
		return parent::mkdir($path);
229
	}
230
231
	public function touch($path, $mtime = null) {
232
		$free = $this->free_space($path);
233
		if ($free === 0.0) {
234
			return false;
235
		}
236
237
		return parent::touch($path, $mtime);
238
	}
239
}
240