Completed
Push — master ( 28c345...2d40e0 )
by Thomas
15:42
created

Storage::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Björn Schießle <[email protected]>
4
 * @author Morris Jobke <[email protected]>
5
 * @author Robin Appelman <[email protected]>
6
 * @author Thomas Müller <[email protected]>
7
 * @author Vincent Petry <[email protected]>
8
 *
9
 * @copyright Copyright (c) 2017, ownCloud GmbH
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
26
namespace OCA\Files_Trashbin;
27
28
use OC\Files\Filesystem;
29
use OC\Files\Storage\Wrapper\Wrapper;
30
use OC\Files\View;
31
use OCP\IUserManager;
32
33
class Storage extends Wrapper {
34
35
	private $mountPoint;
36
	// remember already deleted files to avoid infinite loops if the trash bin
37
	// move files across storages
38
	private $deletedFiles = [];
39
40
	/**
41
	 * Disable trash logic
42
	 *
43
	 * @var bool
44
	 */
45
	private static $disableTrash = false;
46
47
	/** @var  IUserManager */
48
	private $userManager;
49
50
	function __construct($parameters, IUserManager $userManager = null) {
51
		$this->mountPoint = $parameters['mountPoint'];
52
		$this->userManager = $userManager;
53
		parent::__construct($parameters);
54
	}
55
56
	/**
57
	 * @internal
58
	 */
59
	public static function preRenameHook($params) {
60
		// in cross-storage cases, a rename is a copy + unlink,
61
		// that last unlink must not go to trash
62
		self::$disableTrash = true;
63
64
		$path1 = $params[Filesystem::signal_param_oldpath];
65
		$path2 = $params[Filesystem::signal_param_newpath];
66
67
		$view = Filesystem::getView();
68
		$absolutePath1 = Filesystem::normalizePath($view->getAbsolutePath($path1));
69
70
		$mount1 = $view->getMount($path1);
71
		$mount2 = $view->getMount($path2);
72
		$sourceStorage = $mount1->getStorage();
73
		$targetStorage = $mount2->getStorage();
74
		$sourceInternalPath = $mount1->getInternalPath($absolutePath1);
75
		// check whether this is a cross-storage move from a *local* shared storage
76
		if ($sourceInternalPath !== '' && $sourceStorage !== $targetStorage && $sourceStorage->instanceOfStorage('OCA\Files_Sharing\SharedStorage')) {
77
			$ownerPath = $sourceStorage->getSourcePath($sourceInternalPath);
78
			$owner = $sourceStorage->getOwner($sourceInternalPath);
79
			if ($owner !== null && $owner !== '' && $ownerPath !== null && substr($ownerPath, 0, 6) === 'files/') {
80
				// ownerPath is in the format "files/path/to/file.txt", strip "files"
81
				$ownerPath = substr($ownerPath, 6);
82
83
				// make a backup copy for the owner
84
				\OCA\Files_Trashbin\Trashbin::copyBackupForOwner($ownerPath, $owner, time());
85
			}
86
		}
87
	}
88
89
	/**
90
	 * @internal
91
	 */
92
	public static function postRenameHook($params) {
0 ignored issues
show
Unused Code introduced by
The parameter $params is not used and could be removed.

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

Loading history...
93
		self::$disableTrash = false;
94
	}
95
96
	/**
97
	 * Rename path1 to path2 by calling the wrapped storage.
98
	 *
99
	 * @param string $path1 first path
100
	 * @param string $path2 second path
101
	 */
102
	public function rename($path1, $path2) {
103
		$result = $this->storage->rename($path1, $path2);
104
		if ($result === false) {
105
			// when rename failed, the post_rename hook isn't triggered,
106
			// but we still want to reenable the trash logic
107
			self::$disableTrash = false;
108
		}
109
		return $result;
110
	}
111
112
	/**
113
	 * Deletes the given file by moving it into the trashbin.
114
	 *
115
	 * @param string $path path of file or folder to delete
116
	 *
117
	 * @return bool true if the operation succeeded, false otherwise
118
	 */
119
	public function unlink($path) {
120
		return $this->doDelete($path, 'unlink');
121
	}
122
123
	/**
124
	 * Deletes the given folder by moving it into the trashbin.
125
	 *
126
	 * @param string $path path of folder to delete
127
	 *
128
	 * @return bool true if the operation succeeded, false otherwise
129
	 */
130
	public function rmdir($path) {
131
		return $this->doDelete($path, 'rmdir');
132
	}
133
134
	/**
135
	 * check if it is a file located in data/user/files only files in the
136
	 * 'files' directory should be moved to the trash
137
	 *
138
	 * @param $path
139
	 * @return bool
140
	 */
141
	protected function shouldMoveToTrash($path){
142
		$normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path);
143
		$parts = explode('/', $normalized);
144
		if (count($parts) < 4) {
145
			return false;
146
		}
147
148
		if ($this->userManager->userExists($parts[1]) && $parts[2] == 'files') {
149
			return true;
150
		}
151
152
		return false;
153
	}
154
155
	/**
156
	 * Run the delete operation with the given method
157
	 *
158
	 * @param string $path path of file or folder to delete
159
	 * @param string $method either "unlink" or "rmdir"
160
	 *
161
	 * @return bool true if the operation succeeded, false otherwise
162
	 */
163
	private function doDelete($path, $method) {
164
		if (self::$disableTrash
165
			|| !\OC_App::isEnabled('files_trashbin')
166
			|| (pathinfo($path, PATHINFO_EXTENSION) === 'part')
167
			|| $this->shouldMoveToTrash($path) === false
168
		) {
169
			return call_user_func_array([$this->storage, $method], [$path]);
170
		}
171
172
		// check permissions before we continue, this is especially important for
173
		// shared files
174
		if (!$this->isDeletable($path)) {
175
			return false;
176
		}
177
178
		$normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path, true, false, true);
179
		$result = true;
180
		$view = Filesystem::getView();
181
		if (!isset($this->deletedFiles[$normalized]) && $view instanceof View) {
182
			$this->deletedFiles[$normalized] = $normalized;
183
			if ($filesPath = $view->getRelativePath($normalized)) {
184
				$filesPath = trim($filesPath, '/');
185
				$result = \OCA\Files_Trashbin\Trashbin::move2trash($filesPath);
186
				// in cross-storage cases the file will be copied
187
				// but not deleted, so we delete it here
188
				if ($result) {
189
					call_user_func_array([$this->storage, $method], [$path]);
190
				}
191
			} else {
192
				$result = call_user_func_array([$this->storage, $method], [$path]);
193
			}
194
			unset($this->deletedFiles[$normalized]);
195
		} else if ($this->storage->file_exists($path)) {
196
			$result = call_user_func_array([$this->storage, $method], [$path]);
197
		}
198
199
		return $result;
200
	}
201
202
	/**
203
	 * Setup the storate wrapper callback
204
	 */
205
	public static function setupStorage() {
206
		\OC\Files\Filesystem::addStorageWrapper('oc_trashbin', function ($mountPoint, $storage) {
207
			return new \OCA\Files_Trashbin\Storage(
208
				['storage' => $storage, 'mountPoint' => $mountPoint],
209
				\OC::$server->getUserManager()
210
			);
211
		}, 1);
212
	}
213
214
}
215