Completed
Pull Request — master (#5776)
by Lukas
28:48 queued 14:05
created
lib/private/Share20/ProviderFactory.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -176,7 +176,7 @@
 block discarded – undo
176 176
 	/**
177 177
 	 * Create the circle share provider
178 178
 	 *
179
-	 * @return FederatedShareProvider
179
+	 * @return null|\OCA\Circles\ShareByCircleProvider
180 180
 	 *
181 181
 	 * @suppress PhanUndeclaredClassMethod
182 182
 	 */
Please login to merge, or discard this patch.
Indentation   +222 added lines, -222 removed lines patch added patch discarded remove patch
@@ -43,233 +43,233 @@
 block discarded – undo
43 43
  */
44 44
 class ProviderFactory implements IProviderFactory {
45 45
 
46
-	/** @var IServerContainer */
47
-	private $serverContainer;
48
-	/** @var DefaultShareProvider */
49
-	private $defaultProvider = null;
50
-	/** @var FederatedShareProvider */
51
-	private $federatedProvider = null;
52
-	/** @var  ShareByMailProvider */
53
-	private $shareByMailProvider;
54
-	/** @var  \OCA\Circles\ShareByCircleProvider */
55
-	private $shareByCircleProvider = null;
56
-	/** @var bool */
57
-	private $circlesAreNotAvailable = false;
58
-
59
-	/**
60
-	 * IProviderFactory constructor.
61
-	 *
62
-	 * @param IServerContainer $serverContainer
63
-	 */
64
-	public function __construct(IServerContainer $serverContainer) {
65
-		$this->serverContainer = $serverContainer;
66
-	}
67
-
68
-	/**
69
-	 * Create the default share provider.
70
-	 *
71
-	 * @return DefaultShareProvider
72
-	 */
73
-	protected function defaultShareProvider() {
74
-		if ($this->defaultProvider === null) {
75
-			$this->defaultProvider = new DefaultShareProvider(
76
-				$this->serverContainer->getDatabaseConnection(),
77
-				$this->serverContainer->getUserManager(),
78
-				$this->serverContainer->getGroupManager(),
79
-				$this->serverContainer->getLazyRootFolder()
80
-			);
81
-		}
82
-
83
-		return $this->defaultProvider;
84
-	}
85
-
86
-	/**
87
-	 * Create the federated share provider
88
-	 *
89
-	 * @return FederatedShareProvider
90
-	 */
91
-	protected function federatedShareProvider() {
92
-		if ($this->federatedProvider === null) {
93
-			/*
46
+    /** @var IServerContainer */
47
+    private $serverContainer;
48
+    /** @var DefaultShareProvider */
49
+    private $defaultProvider = null;
50
+    /** @var FederatedShareProvider */
51
+    private $federatedProvider = null;
52
+    /** @var  ShareByMailProvider */
53
+    private $shareByMailProvider;
54
+    /** @var  \OCA\Circles\ShareByCircleProvider */
55
+    private $shareByCircleProvider = null;
56
+    /** @var bool */
57
+    private $circlesAreNotAvailable = false;
58
+
59
+    /**
60
+     * IProviderFactory constructor.
61
+     *
62
+     * @param IServerContainer $serverContainer
63
+     */
64
+    public function __construct(IServerContainer $serverContainer) {
65
+        $this->serverContainer = $serverContainer;
66
+    }
67
+
68
+    /**
69
+     * Create the default share provider.
70
+     *
71
+     * @return DefaultShareProvider
72
+     */
73
+    protected function defaultShareProvider() {
74
+        if ($this->defaultProvider === null) {
75
+            $this->defaultProvider = new DefaultShareProvider(
76
+                $this->serverContainer->getDatabaseConnection(),
77
+                $this->serverContainer->getUserManager(),
78
+                $this->serverContainer->getGroupManager(),
79
+                $this->serverContainer->getLazyRootFolder()
80
+            );
81
+        }
82
+
83
+        return $this->defaultProvider;
84
+    }
85
+
86
+    /**
87
+     * Create the federated share provider
88
+     *
89
+     * @return FederatedShareProvider
90
+     */
91
+    protected function federatedShareProvider() {
92
+        if ($this->federatedProvider === null) {
93
+            /*
94 94
 			 * Check if the app is enabled
95 95
 			 */
96
-			$appManager = $this->serverContainer->getAppManager();
97
-			if (!$appManager->isEnabledForUser('federatedfilesharing')) {
98
-				return null;
99
-			}
96
+            $appManager = $this->serverContainer->getAppManager();
97
+            if (!$appManager->isEnabledForUser('federatedfilesharing')) {
98
+                return null;
99
+            }
100 100
 
101
-			/*
101
+            /*
102 102
 			 * TODO: add factory to federated sharing app
103 103
 			 */
104
-			$l = $this->serverContainer->getL10N('federatedfilessharing');
105
-			$addressHandler = new AddressHandler(
106
-				$this->serverContainer->getURLGenerator(),
107
-				$l,
108
-				$this->serverContainer->getCloudIdManager()
109
-			);
110
-			$notifications = new Notifications(
111
-				$addressHandler,
112
-				$this->serverContainer->getHTTPClientService(),
113
-				$this->serverContainer->query(\OCP\OCS\IDiscoveryService::class),
114
-				$this->serverContainer->getJobList()
115
-			);
116
-			$tokenHandler = new TokenHandler(
117
-				$this->serverContainer->getSecureRandom()
118
-			);
119
-
120
-			$this->federatedProvider = new FederatedShareProvider(
121
-				$this->serverContainer->getDatabaseConnection(),
122
-				$addressHandler,
123
-				$notifications,
124
-				$tokenHandler,
125
-				$l,
126
-				$this->serverContainer->getLogger(),
127
-				$this->serverContainer->getLazyRootFolder(),
128
-				$this->serverContainer->getConfig(),
129
-				$this->serverContainer->getUserManager(),
130
-				$this->serverContainer->getCloudIdManager(),
131
-				$this->serverContainer->query(Config::class)
132
-			);
133
-		}
134
-
135
-		return $this->federatedProvider;
136
-	}
137
-
138
-	/**
139
-	 * Create the federated share provider
140
-	 *
141
-	 * @return ShareByMailProvider
142
-	 */
143
-	protected function getShareByMailProvider() {
144
-		if ($this->shareByMailProvider === null) {
145
-			/*
104
+            $l = $this->serverContainer->getL10N('federatedfilessharing');
105
+            $addressHandler = new AddressHandler(
106
+                $this->serverContainer->getURLGenerator(),
107
+                $l,
108
+                $this->serverContainer->getCloudIdManager()
109
+            );
110
+            $notifications = new Notifications(
111
+                $addressHandler,
112
+                $this->serverContainer->getHTTPClientService(),
113
+                $this->serverContainer->query(\OCP\OCS\IDiscoveryService::class),
114
+                $this->serverContainer->getJobList()
115
+            );
116
+            $tokenHandler = new TokenHandler(
117
+                $this->serverContainer->getSecureRandom()
118
+            );
119
+
120
+            $this->federatedProvider = new FederatedShareProvider(
121
+                $this->serverContainer->getDatabaseConnection(),
122
+                $addressHandler,
123
+                $notifications,
124
+                $tokenHandler,
125
+                $l,
126
+                $this->serverContainer->getLogger(),
127
+                $this->serverContainer->getLazyRootFolder(),
128
+                $this->serverContainer->getConfig(),
129
+                $this->serverContainer->getUserManager(),
130
+                $this->serverContainer->getCloudIdManager(),
131
+                $this->serverContainer->query(Config::class)
132
+            );
133
+        }
134
+
135
+        return $this->federatedProvider;
136
+    }
137
+
138
+    /**
139
+     * Create the federated share provider
140
+     *
141
+     * @return ShareByMailProvider
142
+     */
143
+    protected function getShareByMailProvider() {
144
+        if ($this->shareByMailProvider === null) {
145
+            /*
146 146
 			 * Check if the app is enabled
147 147
 			 */
148
-			$appManager = $this->serverContainer->getAppManager();
149
-			if (!$appManager->isEnabledForUser('sharebymail')) {
150
-				return null;
151
-			}
152
-
153
-			$settingsManager = new SettingsManager($this->serverContainer->getConfig());
154
-
155
-			$this->shareByMailProvider = new ShareByMailProvider(
156
-				$this->serverContainer->getDatabaseConnection(),
157
-				$this->serverContainer->getSecureRandom(),
158
-				$this->serverContainer->getUserManager(),
159
-				$this->serverContainer->getLazyRootFolder(),
160
-				$this->serverContainer->getL10N('sharebymail'),
161
-				$this->serverContainer->getLogger(),
162
-				$this->serverContainer->getMailer(),
163
-				$this->serverContainer->getURLGenerator(),
164
-				$this->serverContainer->getActivityManager(),
165
-				$settingsManager,
166
-				$this->serverContainer->query(Defaults::class),
167
-				$this->serverContainer->getHasher(),
168
-				$this->serverContainer->query(CapabilitiesManager::class)
169
-			);
170
-		}
171
-
172
-		return $this->shareByMailProvider;
173
-	}
174
-
175
-
176
-	/**
177
-	 * Create the circle share provider
178
-	 *
179
-	 * @return FederatedShareProvider
180
-	 *
181
-	 * @suppress PhanUndeclaredClassMethod
182
-	 */
183
-	protected function getShareByCircleProvider() {
184
-
185
-		if ($this->circlesAreNotAvailable) {
186
-			return null;
187
-		}
188
-
189
-		if (!$this->serverContainer->getAppManager()->isEnabledForUser('circles') ||
190
-			!class_exists('\OCA\Circles\ShareByCircleProvider')
191
-		) {
192
-			$this->circlesAreNotAvailable = true;
193
-			return null;
194
-		}
195
-
196
-		if ($this->shareByCircleProvider === null) {
197
-
198
-			$this->shareByCircleProvider = new \OCA\Circles\ShareByCircleProvider(
199
-				$this->serverContainer->getDatabaseConnection(),
200
-				$this->serverContainer->getSecureRandom(),
201
-				$this->serverContainer->getUserManager(),
202
-				$this->serverContainer->getLazyRootFolder(),
203
-				$this->serverContainer->getL10N('circles'),
204
-				$this->serverContainer->getLogger(),
205
-				$this->serverContainer->getURLGenerator()
206
-			);
207
-		}
208
-
209
-		return $this->shareByCircleProvider;
210
-	}
211
-
212
-
213
-	/**
214
-	 * @inheritdoc
215
-	 */
216
-	public function getProvider($id) {
217
-		$provider = null;
218
-		if ($id === 'ocinternal') {
219
-			$provider = $this->defaultShareProvider();
220
-		} else if ($id === 'ocFederatedSharing') {
221
-			$provider = $this->federatedShareProvider();
222
-		} else if ($id === 'ocMailShare') {
223
-			$provider = $this->getShareByMailProvider();
224
-		} else if ($id === 'ocCircleShare') {
225
-			$provider = $this->getShareByCircleProvider();
226
-		}
227
-
228
-		if ($provider === null) {
229
-			throw new ProviderException('No provider with id .' . $id . ' found.');
230
-		}
231
-
232
-		return $provider;
233
-	}
234
-
235
-	/**
236
-	 * @inheritdoc
237
-	 */
238
-	public function getProviderForType($shareType) {
239
-		$provider = null;
240
-
241
-		if ($shareType === \OCP\Share::SHARE_TYPE_USER ||
242
-			$shareType === \OCP\Share::SHARE_TYPE_GROUP ||
243
-			$shareType === \OCP\Share::SHARE_TYPE_LINK
244
-		) {
245
-			$provider = $this->defaultShareProvider();
246
-		} else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
247
-			$provider = $this->federatedShareProvider();
248
-		} else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) {
249
-			$provider = $this->getShareByMailProvider();
250
-		} else if ($shareType === \OCP\Share::SHARE_TYPE_CIRCLE) {
251
-			$provider = $this->getShareByCircleProvider();
252
-		}
253
-
254
-
255
-		if ($provider === null) {
256
-			throw new ProviderException('No share provider for share type ' . $shareType);
257
-		}
258
-
259
-		return $provider;
260
-	}
261
-
262
-	public function getAllProviders() {
263
-		$shares = [$this->defaultShareProvider(), $this->federatedShareProvider()];
264
-		$shareByMail = $this->getShareByMailProvider();
265
-		if ($shareByMail !== null) {
266
-			$shares[] = $shareByMail;
267
-		}
268
-		$shareByCircle = $this->getShareByCircleProvider();
269
-		if ($shareByCircle !== null) {
270
-			$shares[] = $shareByCircle;
271
-		}
272
-
273
-		return $shares;
274
-	}
148
+            $appManager = $this->serverContainer->getAppManager();
149
+            if (!$appManager->isEnabledForUser('sharebymail')) {
150
+                return null;
151
+            }
152
+
153
+            $settingsManager = new SettingsManager($this->serverContainer->getConfig());
154
+
155
+            $this->shareByMailProvider = new ShareByMailProvider(
156
+                $this->serverContainer->getDatabaseConnection(),
157
+                $this->serverContainer->getSecureRandom(),
158
+                $this->serverContainer->getUserManager(),
159
+                $this->serverContainer->getLazyRootFolder(),
160
+                $this->serverContainer->getL10N('sharebymail'),
161
+                $this->serverContainer->getLogger(),
162
+                $this->serverContainer->getMailer(),
163
+                $this->serverContainer->getURLGenerator(),
164
+                $this->serverContainer->getActivityManager(),
165
+                $settingsManager,
166
+                $this->serverContainer->query(Defaults::class),
167
+                $this->serverContainer->getHasher(),
168
+                $this->serverContainer->query(CapabilitiesManager::class)
169
+            );
170
+        }
171
+
172
+        return $this->shareByMailProvider;
173
+    }
174
+
175
+
176
+    /**
177
+     * Create the circle share provider
178
+     *
179
+     * @return FederatedShareProvider
180
+     *
181
+     * @suppress PhanUndeclaredClassMethod
182
+     */
183
+    protected function getShareByCircleProvider() {
184
+
185
+        if ($this->circlesAreNotAvailable) {
186
+            return null;
187
+        }
188
+
189
+        if (!$this->serverContainer->getAppManager()->isEnabledForUser('circles') ||
190
+            !class_exists('\OCA\Circles\ShareByCircleProvider')
191
+        ) {
192
+            $this->circlesAreNotAvailable = true;
193
+            return null;
194
+        }
195
+
196
+        if ($this->shareByCircleProvider === null) {
197
+
198
+            $this->shareByCircleProvider = new \OCA\Circles\ShareByCircleProvider(
199
+                $this->serverContainer->getDatabaseConnection(),
200
+                $this->serverContainer->getSecureRandom(),
201
+                $this->serverContainer->getUserManager(),
202
+                $this->serverContainer->getLazyRootFolder(),
203
+                $this->serverContainer->getL10N('circles'),
204
+                $this->serverContainer->getLogger(),
205
+                $this->serverContainer->getURLGenerator()
206
+            );
207
+        }
208
+
209
+        return $this->shareByCircleProvider;
210
+    }
211
+
212
+
213
+    /**
214
+     * @inheritdoc
215
+     */
216
+    public function getProvider($id) {
217
+        $provider = null;
218
+        if ($id === 'ocinternal') {
219
+            $provider = $this->defaultShareProvider();
220
+        } else if ($id === 'ocFederatedSharing') {
221
+            $provider = $this->federatedShareProvider();
222
+        } else if ($id === 'ocMailShare') {
223
+            $provider = $this->getShareByMailProvider();
224
+        } else if ($id === 'ocCircleShare') {
225
+            $provider = $this->getShareByCircleProvider();
226
+        }
227
+
228
+        if ($provider === null) {
229
+            throw new ProviderException('No provider with id .' . $id . ' found.');
230
+        }
231
+
232
+        return $provider;
233
+    }
234
+
235
+    /**
236
+     * @inheritdoc
237
+     */
238
+    public function getProviderForType($shareType) {
239
+        $provider = null;
240
+
241
+        if ($shareType === \OCP\Share::SHARE_TYPE_USER ||
242
+            $shareType === \OCP\Share::SHARE_TYPE_GROUP ||
243
+            $shareType === \OCP\Share::SHARE_TYPE_LINK
244
+        ) {
245
+            $provider = $this->defaultShareProvider();
246
+        } else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
247
+            $provider = $this->federatedShareProvider();
248
+        } else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) {
249
+            $provider = $this->getShareByMailProvider();
250
+        } else if ($shareType === \OCP\Share::SHARE_TYPE_CIRCLE) {
251
+            $provider = $this->getShareByCircleProvider();
252
+        }
253
+
254
+
255
+        if ($provider === null) {
256
+            throw new ProviderException('No share provider for share type ' . $shareType);
257
+        }
258
+
259
+        return $provider;
260
+    }
261
+
262
+    public function getAllProviders() {
263
+        $shares = [$this->defaultShareProvider(), $this->federatedShareProvider()];
264
+        $shareByMail = $this->getShareByMailProvider();
265
+        if ($shareByMail !== null) {
266
+            $shares[] = $shareByMail;
267
+        }
268
+        $shareByCircle = $this->getShareByCircleProvider();
269
+        if ($shareByCircle !== null) {
270
+            $shares[] = $shareByCircle;
271
+        }
272
+
273
+        return $shares;
274
+    }
275 275
 }
Please login to merge, or discard this patch.
lib/private/Files/View.php 1 patch
Indentation   +2053 added lines, -2053 removed lines patch added patch discarded remove patch
@@ -82,2057 +82,2057 @@
 block discarded – undo
82 82
  * \OC\Files\Storage\Storage object
83 83
  */
84 84
 class View {
85
-	/** @var string */
86
-	private $fakeRoot = '';
87
-
88
-	/**
89
-	 * @var \OCP\Lock\ILockingProvider
90
-	 */
91
-	protected $lockingProvider;
92
-
93
-	private $lockingEnabled;
94
-
95
-	private $updaterEnabled = true;
96
-
97
-	/** @var \OC\User\Manager */
98
-	private $userManager;
99
-
100
-	/** @var \OCP\ILogger */
101
-	private $logger;
102
-
103
-	/**
104
-	 * @param string $root
105
-	 * @throws \Exception If $root contains an invalid path
106
-	 */
107
-	public function __construct($root = '') {
108
-		if (is_null($root)) {
109
-			throw new \InvalidArgumentException('Root can\'t be null');
110
-		}
111
-		if (!Filesystem::isValidPath($root)) {
112
-			throw new \Exception();
113
-		}
114
-
115
-		$this->fakeRoot = $root;
116
-		$this->lockingProvider = \OC::$server->getLockingProvider();
117
-		$this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider);
118
-		$this->userManager = \OC::$server->getUserManager();
119
-		$this->logger = \OC::$server->getLogger();
120
-	}
121
-
122
-	public function getAbsolutePath($path = '/') {
123
-		if ($path === null) {
124
-			return null;
125
-		}
126
-		$this->assertPathLength($path);
127
-		if ($path === '') {
128
-			$path = '/';
129
-		}
130
-		if ($path[0] !== '/') {
131
-			$path = '/' . $path;
132
-		}
133
-		return $this->fakeRoot . $path;
134
-	}
135
-
136
-	/**
137
-	 * change the root to a fake root
138
-	 *
139
-	 * @param string $fakeRoot
140
-	 * @return boolean|null
141
-	 */
142
-	public function chroot($fakeRoot) {
143
-		if (!$fakeRoot == '') {
144
-			if ($fakeRoot[0] !== '/') {
145
-				$fakeRoot = '/' . $fakeRoot;
146
-			}
147
-		}
148
-		$this->fakeRoot = $fakeRoot;
149
-	}
150
-
151
-	/**
152
-	 * get the fake root
153
-	 *
154
-	 * @return string
155
-	 */
156
-	public function getRoot() {
157
-		return $this->fakeRoot;
158
-	}
159
-
160
-	/**
161
-	 * get path relative to the root of the view
162
-	 *
163
-	 * @param string $path
164
-	 * @return string
165
-	 */
166
-	public function getRelativePath($path) {
167
-		$this->assertPathLength($path);
168
-		if ($this->fakeRoot == '') {
169
-			return $path;
170
-		}
171
-
172
-		if (rtrim($path, '/') === rtrim($this->fakeRoot, '/')) {
173
-			return '/';
174
-		}
175
-
176
-		// missing slashes can cause wrong matches!
177
-		$root = rtrim($this->fakeRoot, '/') . '/';
178
-
179
-		if (strpos($path, $root) !== 0) {
180
-			return null;
181
-		} else {
182
-			$path = substr($path, strlen($this->fakeRoot));
183
-			if (strlen($path) === 0) {
184
-				return '/';
185
-			} else {
186
-				return $path;
187
-			}
188
-		}
189
-	}
190
-
191
-	/**
192
-	 * get the mountpoint of the storage object for a path
193
-	 * ( note: because a storage is not always mounted inside the fakeroot, the
194
-	 * returned mountpoint is relative to the absolute root of the filesystem
195
-	 * and does not take the chroot into account )
196
-	 *
197
-	 * @param string $path
198
-	 * @return string
199
-	 */
200
-	public function getMountPoint($path) {
201
-		return Filesystem::getMountPoint($this->getAbsolutePath($path));
202
-	}
203
-
204
-	/**
205
-	 * get the mountpoint of the storage object for a path
206
-	 * ( note: because a storage is not always mounted inside the fakeroot, the
207
-	 * returned mountpoint is relative to the absolute root of the filesystem
208
-	 * and does not take the chroot into account )
209
-	 *
210
-	 * @param string $path
211
-	 * @return \OCP\Files\Mount\IMountPoint
212
-	 */
213
-	public function getMount($path) {
214
-		return Filesystem::getMountManager()->find($this->getAbsolutePath($path));
215
-	}
216
-
217
-	/**
218
-	 * resolve a path to a storage and internal path
219
-	 *
220
-	 * @param string $path
221
-	 * @return array an array consisting of the storage and the internal path
222
-	 */
223
-	public function resolvePath($path) {
224
-		$a = $this->getAbsolutePath($path);
225
-		$p = Filesystem::normalizePath($a);
226
-		return Filesystem::resolvePath($p);
227
-	}
228
-
229
-	/**
230
-	 * return the path to a local version of the file
231
-	 * we need this because we can't know if a file is stored local or not from
232
-	 * outside the filestorage and for some purposes a local file is needed
233
-	 *
234
-	 * @param string $path
235
-	 * @return string
236
-	 */
237
-	public function getLocalFile($path) {
238
-		$parent = substr($path, 0, strrpos($path, '/'));
239
-		$path = $this->getAbsolutePath($path);
240
-		list($storage, $internalPath) = Filesystem::resolvePath($path);
241
-		if (Filesystem::isValidPath($parent) and $storage) {
242
-			return $storage->getLocalFile($internalPath);
243
-		} else {
244
-			return null;
245
-		}
246
-	}
247
-
248
-	/**
249
-	 * @param string $path
250
-	 * @return string
251
-	 */
252
-	public function getLocalFolder($path) {
253
-		$parent = substr($path, 0, strrpos($path, '/'));
254
-		$path = $this->getAbsolutePath($path);
255
-		list($storage, $internalPath) = Filesystem::resolvePath($path);
256
-		if (Filesystem::isValidPath($parent) and $storage) {
257
-			return $storage->getLocalFolder($internalPath);
258
-		} else {
259
-			return null;
260
-		}
261
-	}
262
-
263
-	/**
264
-	 * the following functions operate with arguments and return values identical
265
-	 * to those of their PHP built-in equivalents. Mostly they are merely wrappers
266
-	 * for \OC\Files\Storage\Storage via basicOperation().
267
-	 */
268
-	public function mkdir($path) {
269
-		return $this->basicOperation('mkdir', $path, array('create', 'write'));
270
-	}
271
-
272
-	/**
273
-	 * remove mount point
274
-	 *
275
-	 * @param \OC\Files\Mount\MoveableMount $mount
276
-	 * @param string $path relative to data/
277
-	 * @return boolean
278
-	 */
279
-	protected function removeMount($mount, $path) {
280
-		if ($mount instanceof MoveableMount) {
281
-			// cut of /user/files to get the relative path to data/user/files
282
-			$pathParts = explode('/', $path, 4);
283
-			$relPath = '/' . $pathParts[3];
284
-			$this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true);
285
-			\OC_Hook::emit(
286
-				Filesystem::CLASSNAME, "umount",
287
-				array(Filesystem::signal_param_path => $relPath)
288
-			);
289
-			$this->changeLock($relPath, ILockingProvider::LOCK_EXCLUSIVE, true);
290
-			$result = $mount->removeMount();
291
-			$this->changeLock($relPath, ILockingProvider::LOCK_SHARED, true);
292
-			if ($result) {
293
-				\OC_Hook::emit(
294
-					Filesystem::CLASSNAME, "post_umount",
295
-					array(Filesystem::signal_param_path => $relPath)
296
-				);
297
-			}
298
-			$this->unlockFile($relPath, ILockingProvider::LOCK_SHARED, true);
299
-			return $result;
300
-		} else {
301
-			// do not allow deleting the storage's root / the mount point
302
-			// because for some storages it might delete the whole contents
303
-			// but isn't supposed to work that way
304
-			return false;
305
-		}
306
-	}
307
-
308
-	public function disableCacheUpdate() {
309
-		$this->updaterEnabled = false;
310
-	}
311
-
312
-	public function enableCacheUpdate() {
313
-		$this->updaterEnabled = true;
314
-	}
315
-
316
-	protected function writeUpdate(Storage $storage, $internalPath, $time = null) {
317
-		if ($this->updaterEnabled) {
318
-			if (is_null($time)) {
319
-				$time = time();
320
-			}
321
-			$storage->getUpdater()->update($internalPath, $time);
322
-		}
323
-	}
324
-
325
-	protected function removeUpdate(Storage $storage, $internalPath) {
326
-		if ($this->updaterEnabled) {
327
-			$storage->getUpdater()->remove($internalPath);
328
-		}
329
-	}
330
-
331
-	protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) {
332
-		if ($this->updaterEnabled) {
333
-			$targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
334
-		}
335
-	}
336
-
337
-	/**
338
-	 * @param string $path
339
-	 * @return bool|mixed
340
-	 */
341
-	public function rmdir($path) {
342
-		$absolutePath = $this->getAbsolutePath($path);
343
-		$mount = Filesystem::getMountManager()->find($absolutePath);
344
-		if ($mount->getInternalPath($absolutePath) === '') {
345
-			return $this->removeMount($mount, $absolutePath);
346
-		}
347
-		if ($this->is_dir($path)) {
348
-			$result = $this->basicOperation('rmdir', $path, array('delete'));
349
-		} else {
350
-			$result = false;
351
-		}
352
-
353
-		if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
354
-			$storage = $mount->getStorage();
355
-			$internalPath = $mount->getInternalPath($absolutePath);
356
-			$storage->getUpdater()->remove($internalPath);
357
-		}
358
-		return $result;
359
-	}
360
-
361
-	/**
362
-	 * @param string $path
363
-	 * @return resource
364
-	 */
365
-	public function opendir($path) {
366
-		return $this->basicOperation('opendir', $path, array('read'));
367
-	}
368
-
369
-	/**
370
-	 * @param $handle
371
-	 * @return mixed
372
-	 */
373
-	public function readdir($handle) {
374
-		$fsLocal = new Storage\Local(array('datadir' => '/'));
375
-		return $fsLocal->readdir($handle);
376
-	}
377
-
378
-	/**
379
-	 * @param string $path
380
-	 * @return bool|mixed
381
-	 */
382
-	public function is_dir($path) {
383
-		if ($path == '/') {
384
-			return true;
385
-		}
386
-		return $this->basicOperation('is_dir', $path);
387
-	}
388
-
389
-	/**
390
-	 * @param string $path
391
-	 * @return bool|mixed
392
-	 */
393
-	public function is_file($path) {
394
-		if ($path == '/') {
395
-			return false;
396
-		}
397
-		return $this->basicOperation('is_file', $path);
398
-	}
399
-
400
-	/**
401
-	 * @param string $path
402
-	 * @return mixed
403
-	 */
404
-	public function stat($path) {
405
-		return $this->basicOperation('stat', $path);
406
-	}
407
-
408
-	/**
409
-	 * @param string $path
410
-	 * @return mixed
411
-	 */
412
-	public function filetype($path) {
413
-		return $this->basicOperation('filetype', $path);
414
-	}
415
-
416
-	/**
417
-	 * @param string $path
418
-	 * @return mixed
419
-	 */
420
-	public function filesize($path) {
421
-		return $this->basicOperation('filesize', $path);
422
-	}
423
-
424
-	/**
425
-	 * @param string $path
426
-	 * @return bool|mixed
427
-	 * @throws \OCP\Files\InvalidPathException
428
-	 */
429
-	public function readfile($path) {
430
-		$this->assertPathLength($path);
431
-		@ob_end_clean();
432
-		$handle = $this->fopen($path, 'rb');
433
-		if ($handle) {
434
-			$chunkSize = 8192; // 8 kB chunks
435
-			while (!feof($handle)) {
436
-				echo fread($handle, $chunkSize);
437
-				flush();
438
-			}
439
-			fclose($handle);
440
-			$size = $this->filesize($path);
441
-			return $size;
442
-		}
443
-		return false;
444
-	}
445
-
446
-	/**
447
-	 * @param string $path
448
-	 * @param int $from
449
-	 * @param int $to
450
-	 * @return bool|mixed
451
-	 * @throws \OCP\Files\InvalidPathException
452
-	 * @throws \OCP\Files\UnseekableException
453
-	 */
454
-	public function readfilePart($path, $from, $to) {
455
-		$this->assertPathLength($path);
456
-		@ob_end_clean();
457
-		$handle = $this->fopen($path, 'rb');
458
-		if ($handle) {
459
-			if (fseek($handle, $from) === 0) {
460
-				$chunkSize = 8192; // 8 kB chunks
461
-				$end = $to + 1;
462
-				while (!feof($handle) && ftell($handle) < $end) {
463
-					$len = $end - ftell($handle);
464
-					if ($len > $chunkSize) {
465
-						$len = $chunkSize;
466
-					}
467
-					echo fread($handle, $len);
468
-					flush();
469
-				}
470
-				$size = ftell($handle) - $from;
471
-				return $size;
472
-			}
473
-
474
-			throw new \OCP\Files\UnseekableException('fseek error');
475
-		}
476
-		return false;
477
-	}
478
-
479
-	/**
480
-	 * @param string $path
481
-	 * @return mixed
482
-	 */
483
-	public function isCreatable($path) {
484
-		return $this->basicOperation('isCreatable', $path);
485
-	}
486
-
487
-	/**
488
-	 * @param string $path
489
-	 * @return mixed
490
-	 */
491
-	public function isReadable($path) {
492
-		return $this->basicOperation('isReadable', $path);
493
-	}
494
-
495
-	/**
496
-	 * @param string $path
497
-	 * @return mixed
498
-	 */
499
-	public function isUpdatable($path) {
500
-		return $this->basicOperation('isUpdatable', $path);
501
-	}
502
-
503
-	/**
504
-	 * @param string $path
505
-	 * @return bool|mixed
506
-	 */
507
-	public function isDeletable($path) {
508
-		$absolutePath = $this->getAbsolutePath($path);
509
-		$mount = Filesystem::getMountManager()->find($absolutePath);
510
-		if ($mount->getInternalPath($absolutePath) === '') {
511
-			return $mount instanceof MoveableMount;
512
-		}
513
-		return $this->basicOperation('isDeletable', $path);
514
-	}
515
-
516
-	/**
517
-	 * @param string $path
518
-	 * @return mixed
519
-	 */
520
-	public function isSharable($path) {
521
-		return $this->basicOperation('isSharable', $path);
522
-	}
523
-
524
-	/**
525
-	 * @param string $path
526
-	 * @return bool|mixed
527
-	 */
528
-	public function file_exists($path) {
529
-		if ($path == '/') {
530
-			return true;
531
-		}
532
-		return $this->basicOperation('file_exists', $path);
533
-	}
534
-
535
-	/**
536
-	 * @param string $path
537
-	 * @return mixed
538
-	 */
539
-	public function filemtime($path) {
540
-		return $this->basicOperation('filemtime', $path);
541
-	}
542
-
543
-	/**
544
-	 * @param string $path
545
-	 * @param int|string $mtime
546
-	 * @return bool
547
-	 */
548
-	public function touch($path, $mtime = null) {
549
-		if (!is_null($mtime) and !is_numeric($mtime)) {
550
-			$mtime = strtotime($mtime);
551
-		}
552
-
553
-		$hooks = array('touch');
554
-
555
-		if (!$this->file_exists($path)) {
556
-			$hooks[] = 'create';
557
-			$hooks[] = 'write';
558
-		}
559
-		$result = $this->basicOperation('touch', $path, $hooks, $mtime);
560
-		if (!$result) {
561
-			// If create file fails because of permissions on external storage like SMB folders,
562
-			// check file exists and return false if not.
563
-			if (!$this->file_exists($path)) {
564
-				return false;
565
-			}
566
-			if (is_null($mtime)) {
567
-				$mtime = time();
568
-			}
569
-			//if native touch fails, we emulate it by changing the mtime in the cache
570
-			$this->putFileInfo($path, array('mtime' => floor($mtime)));
571
-		}
572
-		return true;
573
-	}
574
-
575
-	/**
576
-	 * @param string $path
577
-	 * @return mixed
578
-	 */
579
-	public function file_get_contents($path) {
580
-		return $this->basicOperation('file_get_contents', $path, array('read'));
581
-	}
582
-
583
-	/**
584
-	 * @param bool $exists
585
-	 * @param string $path
586
-	 * @param bool $run
587
-	 */
588
-	protected function emit_file_hooks_pre($exists, $path, &$run) {
589
-		if (!$exists) {
590
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array(
591
-				Filesystem::signal_param_path => $this->getHookPath($path),
592
-				Filesystem::signal_param_run => &$run,
593
-			));
594
-		} else {
595
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array(
596
-				Filesystem::signal_param_path => $this->getHookPath($path),
597
-				Filesystem::signal_param_run => &$run,
598
-			));
599
-		}
600
-		\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array(
601
-			Filesystem::signal_param_path => $this->getHookPath($path),
602
-			Filesystem::signal_param_run => &$run,
603
-		));
604
-	}
605
-
606
-	/**
607
-	 * @param bool $exists
608
-	 * @param string $path
609
-	 */
610
-	protected function emit_file_hooks_post($exists, $path) {
611
-		if (!$exists) {
612
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array(
613
-				Filesystem::signal_param_path => $this->getHookPath($path),
614
-			));
615
-		} else {
616
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array(
617
-				Filesystem::signal_param_path => $this->getHookPath($path),
618
-			));
619
-		}
620
-		\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array(
621
-			Filesystem::signal_param_path => $this->getHookPath($path),
622
-		));
623
-	}
624
-
625
-	/**
626
-	 * @param string $path
627
-	 * @param mixed $data
628
-	 * @return bool|mixed
629
-	 * @throws \Exception
630
-	 */
631
-	public function file_put_contents($path, $data) {
632
-		if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier
633
-			$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
634
-			if (Filesystem::isValidPath($path)
635
-				and !Filesystem::isFileBlacklisted($path)
636
-			) {
637
-				$path = $this->getRelativePath($absolutePath);
638
-
639
-				$this->lockFile($path, ILockingProvider::LOCK_SHARED);
640
-
641
-				$exists = $this->file_exists($path);
642
-				$run = true;
643
-				if ($this->shouldEmitHooks($path)) {
644
-					$this->emit_file_hooks_pre($exists, $path, $run);
645
-				}
646
-				if (!$run) {
647
-					$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
648
-					return false;
649
-				}
650
-
651
-				$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
652
-
653
-				/** @var \OC\Files\Storage\Storage $storage */
654
-				list($storage, $internalPath) = $this->resolvePath($path);
655
-				$target = $storage->fopen($internalPath, 'w');
656
-				if ($target) {
657
-					list (, $result) = \OC_Helper::streamCopy($data, $target);
658
-					fclose($target);
659
-					fclose($data);
660
-
661
-					$this->writeUpdate($storage, $internalPath);
662
-
663
-					$this->changeLock($path, ILockingProvider::LOCK_SHARED);
664
-
665
-					if ($this->shouldEmitHooks($path) && $result !== false) {
666
-						$this->emit_file_hooks_post($exists, $path);
667
-					}
668
-					$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
669
-					return $result;
670
-				} else {
671
-					$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
672
-					return false;
673
-				}
674
-			} else {
675
-				return false;
676
-			}
677
-		} else {
678
-			$hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write');
679
-			return $this->basicOperation('file_put_contents', $path, $hooks, $data);
680
-		}
681
-	}
682
-
683
-	/**
684
-	 * @param string $path
685
-	 * @return bool|mixed
686
-	 */
687
-	public function unlink($path) {
688
-		if ($path === '' || $path === '/') {
689
-			// do not allow deleting the root
690
-			return false;
691
-		}
692
-		$postFix = (substr($path, -1, 1) === '/') ? '/' : '';
693
-		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
694
-		$mount = Filesystem::getMountManager()->find($absolutePath . $postFix);
695
-		if ($mount and $mount->getInternalPath($absolutePath) === '') {
696
-			return $this->removeMount($mount, $absolutePath);
697
-		}
698
-		if ($this->is_dir($path)) {
699
-			$result = $this->basicOperation('rmdir', $path, ['delete']);
700
-		} else {
701
-			$result = $this->basicOperation('unlink', $path, ['delete']);
702
-		}
703
-		if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
704
-			$storage = $mount->getStorage();
705
-			$internalPath = $mount->getInternalPath($absolutePath);
706
-			$storage->getUpdater()->remove($internalPath);
707
-			return true;
708
-		} else {
709
-			return $result;
710
-		}
711
-	}
712
-
713
-	/**
714
-	 * @param string $directory
715
-	 * @return bool|mixed
716
-	 */
717
-	public function deleteAll($directory) {
718
-		return $this->rmdir($directory);
719
-	}
720
-
721
-	/**
722
-	 * Rename/move a file or folder from the source path to target path.
723
-	 *
724
-	 * @param string $path1 source path
725
-	 * @param string $path2 target path
726
-	 *
727
-	 * @return bool|mixed
728
-	 */
729
-	public function rename($path1, $path2) {
730
-		$absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
731
-		$absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
732
-		$result = false;
733
-		if (
734
-			Filesystem::isValidPath($path2)
735
-			and Filesystem::isValidPath($path1)
736
-			and !Filesystem::isFileBlacklisted($path2)
737
-		) {
738
-			$path1 = $this->getRelativePath($absolutePath1);
739
-			$path2 = $this->getRelativePath($absolutePath2);
740
-			$exists = $this->file_exists($path2);
741
-
742
-			if ($path1 == null or $path2 == null) {
743
-				return false;
744
-			}
745
-
746
-			$this->lockFile($path1, ILockingProvider::LOCK_SHARED, true);
747
-			try {
748
-				$this->lockFile($path2, ILockingProvider::LOCK_SHARED, true);
749
-			} catch (LockedException $e) {
750
-				$this->unlockFile($path1, ILockingProvider::LOCK_SHARED);
751
-				throw $e;
752
-			}
753
-
754
-			$run = true;
755
-			if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) {
756
-				// if it was a rename from a part file to a regular file it was a write and not a rename operation
757
-				$this->emit_file_hooks_pre($exists, $path2, $run);
758
-			} elseif ($this->shouldEmitHooks($path1)) {
759
-				\OC_Hook::emit(
760
-					Filesystem::CLASSNAME, Filesystem::signal_rename,
761
-					array(
762
-						Filesystem::signal_param_oldpath => $this->getHookPath($path1),
763
-						Filesystem::signal_param_newpath => $this->getHookPath($path2),
764
-						Filesystem::signal_param_run => &$run
765
-					)
766
-				);
767
-			}
768
-			if ($run) {
769
-				$this->verifyPath(dirname($path2), basename($path2));
770
-
771
-				$manager = Filesystem::getMountManager();
772
-				$mount1 = $this->getMount($path1);
773
-				$mount2 = $this->getMount($path2);
774
-				$storage1 = $mount1->getStorage();
775
-				$storage2 = $mount2->getStorage();
776
-				$internalPath1 = $mount1->getInternalPath($absolutePath1);
777
-				$internalPath2 = $mount2->getInternalPath($absolutePath2);
778
-
779
-				$this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true);
780
-				$this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true);
781
-
782
-				if ($internalPath1 === '' and $mount1 instanceof MoveableMount) {
783
-					if ($this->isTargetAllowed($absolutePath2)) {
784
-						/**
785
-						 * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
786
-						 */
787
-						$sourceMountPoint = $mount1->getMountPoint();
788
-						$result = $mount1->moveMount($absolutePath2);
789
-						$manager->moveMount($sourceMountPoint, $mount1->getMountPoint());
790
-					} else {
791
-						$result = false;
792
-					}
793
-					// moving a file/folder within the same mount point
794
-				} elseif ($storage1 === $storage2) {
795
-					if ($storage1) {
796
-						$result = $storage1->rename($internalPath1, $internalPath2);
797
-					} else {
798
-						$result = false;
799
-					}
800
-					// moving a file/folder between storages (from $storage1 to $storage2)
801
-				} else {
802
-					$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
803
-				}
804
-
805
-				if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
806
-					// if it was a rename from a part file to a regular file it was a write and not a rename operation
807
-
808
-					$this->writeUpdate($storage2, $internalPath2);
809
-				} else if ($result) {
810
-					if ($internalPath1 !== '') { // don't do a cache update for moved mounts
811
-						$this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2);
812
-					}
813
-				}
814
-
815
-				$this->changeLock($path1, ILockingProvider::LOCK_SHARED, true);
816
-				$this->changeLock($path2, ILockingProvider::LOCK_SHARED, true);
817
-
818
-				if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
819
-					if ($this->shouldEmitHooks()) {
820
-						$this->emit_file_hooks_post($exists, $path2);
821
-					}
822
-				} elseif ($result) {
823
-					if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) {
824
-						\OC_Hook::emit(
825
-							Filesystem::CLASSNAME,
826
-							Filesystem::signal_post_rename,
827
-							array(
828
-								Filesystem::signal_param_oldpath => $this->getHookPath($path1),
829
-								Filesystem::signal_param_newpath => $this->getHookPath($path2)
830
-							)
831
-						);
832
-					}
833
-				}
834
-			}
835
-			$this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true);
836
-			$this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true);
837
-		}
838
-		return $result;
839
-	}
840
-
841
-	/**
842
-	 * Copy a file/folder from the source path to target path
843
-	 *
844
-	 * @param string $path1 source path
845
-	 * @param string $path2 target path
846
-	 * @param bool $preserveMtime whether to preserve mtime on the copy
847
-	 *
848
-	 * @return bool|mixed
849
-	 */
850
-	public function copy($path1, $path2, $preserveMtime = false) {
851
-		$absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
852
-		$absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
853
-		$result = false;
854
-		if (
855
-			Filesystem::isValidPath($path2)
856
-			and Filesystem::isValidPath($path1)
857
-			and !Filesystem::isFileBlacklisted($path2)
858
-		) {
859
-			$path1 = $this->getRelativePath($absolutePath1);
860
-			$path2 = $this->getRelativePath($absolutePath2);
861
-
862
-			if ($path1 == null or $path2 == null) {
863
-				return false;
864
-			}
865
-			$run = true;
866
-
867
-			$this->lockFile($path2, ILockingProvider::LOCK_SHARED);
868
-			$this->lockFile($path1, ILockingProvider::LOCK_SHARED);
869
-			$lockTypePath1 = ILockingProvider::LOCK_SHARED;
870
-			$lockTypePath2 = ILockingProvider::LOCK_SHARED;
871
-
872
-			try {
873
-
874
-				$exists = $this->file_exists($path2);
875
-				if ($this->shouldEmitHooks()) {
876
-					\OC_Hook::emit(
877
-						Filesystem::CLASSNAME,
878
-						Filesystem::signal_copy,
879
-						array(
880
-							Filesystem::signal_param_oldpath => $this->getHookPath($path1),
881
-							Filesystem::signal_param_newpath => $this->getHookPath($path2),
882
-							Filesystem::signal_param_run => &$run
883
-						)
884
-					);
885
-					$this->emit_file_hooks_pre($exists, $path2, $run);
886
-				}
887
-				if ($run) {
888
-					$mount1 = $this->getMount($path1);
889
-					$mount2 = $this->getMount($path2);
890
-					$storage1 = $mount1->getStorage();
891
-					$internalPath1 = $mount1->getInternalPath($absolutePath1);
892
-					$storage2 = $mount2->getStorage();
893
-					$internalPath2 = $mount2->getInternalPath($absolutePath2);
894
-
895
-					$this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE);
896
-					$lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE;
897
-
898
-					if ($mount1->getMountPoint() == $mount2->getMountPoint()) {
899
-						if ($storage1) {
900
-							$result = $storage1->copy($internalPath1, $internalPath2);
901
-						} else {
902
-							$result = false;
903
-						}
904
-					} else {
905
-						$result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2);
906
-					}
907
-
908
-					$this->writeUpdate($storage2, $internalPath2);
909
-
910
-					$this->changeLock($path2, ILockingProvider::LOCK_SHARED);
911
-					$lockTypePath2 = ILockingProvider::LOCK_SHARED;
912
-
913
-					if ($this->shouldEmitHooks() && $result !== false) {
914
-						\OC_Hook::emit(
915
-							Filesystem::CLASSNAME,
916
-							Filesystem::signal_post_copy,
917
-							array(
918
-								Filesystem::signal_param_oldpath => $this->getHookPath($path1),
919
-								Filesystem::signal_param_newpath => $this->getHookPath($path2)
920
-							)
921
-						);
922
-						$this->emit_file_hooks_post($exists, $path2);
923
-					}
924
-
925
-				}
926
-			} catch (\Exception $e) {
927
-				$this->unlockFile($path2, $lockTypePath2);
928
-				$this->unlockFile($path1, $lockTypePath1);
929
-				throw $e;
930
-			}
931
-
932
-			$this->unlockFile($path2, $lockTypePath2);
933
-			$this->unlockFile($path1, $lockTypePath1);
934
-
935
-		}
936
-		return $result;
937
-	}
938
-
939
-	/**
940
-	 * @param string $path
941
-	 * @param string $mode 'r' or 'w'
942
-	 * @return resource
943
-	 */
944
-	public function fopen($path, $mode) {
945
-		$mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support
946
-		$hooks = array();
947
-		switch ($mode) {
948
-			case 'r':
949
-				$hooks[] = 'read';
950
-				break;
951
-			case 'r+':
952
-			case 'w+':
953
-			case 'x+':
954
-			case 'a+':
955
-				$hooks[] = 'read';
956
-				$hooks[] = 'write';
957
-				break;
958
-			case 'w':
959
-			case 'x':
960
-			case 'a':
961
-				$hooks[] = 'write';
962
-				break;
963
-			default:
964
-				\OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR);
965
-		}
966
-
967
-		if ($mode !== 'r' && $mode !== 'w') {
968
-			\OC::$server->getLogger()->info('Trying to open a file with a mode other than "r" or "w" can cause severe performance issues with some backends');
969
-		}
970
-
971
-		return $this->basicOperation('fopen', $path, $hooks, $mode);
972
-	}
973
-
974
-	/**
975
-	 * @param string $path
976
-	 * @return bool|string
977
-	 * @throws \OCP\Files\InvalidPathException
978
-	 */
979
-	public function toTmpFile($path) {
980
-		$this->assertPathLength($path);
981
-		if (Filesystem::isValidPath($path)) {
982
-			$source = $this->fopen($path, 'r');
983
-			if ($source) {
984
-				$extension = pathinfo($path, PATHINFO_EXTENSION);
985
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
986
-				file_put_contents($tmpFile, $source);
987
-				return $tmpFile;
988
-			} else {
989
-				return false;
990
-			}
991
-		} else {
992
-			return false;
993
-		}
994
-	}
995
-
996
-	/**
997
-	 * @param string $tmpFile
998
-	 * @param string $path
999
-	 * @return bool|mixed
1000
-	 * @throws \OCP\Files\InvalidPathException
1001
-	 */
1002
-	public function fromTmpFile($tmpFile, $path) {
1003
-		$this->assertPathLength($path);
1004
-		if (Filesystem::isValidPath($path)) {
1005
-
1006
-			// Get directory that the file is going into
1007
-			$filePath = dirname($path);
1008
-
1009
-			// Create the directories if any
1010
-			if (!$this->file_exists($filePath)) {
1011
-				$result = $this->createParentDirectories($filePath);
1012
-				if ($result === false) {
1013
-					return false;
1014
-				}
1015
-			}
1016
-
1017
-			$source = fopen($tmpFile, 'r');
1018
-			if ($source) {
1019
-				$result = $this->file_put_contents($path, $source);
1020
-				// $this->file_put_contents() might have already closed
1021
-				// the resource, so we check it, before trying to close it
1022
-				// to avoid messages in the error log.
1023
-				if (is_resource($source)) {
1024
-					fclose($source);
1025
-				}
1026
-				unlink($tmpFile);
1027
-				return $result;
1028
-			} else {
1029
-				return false;
1030
-			}
1031
-		} else {
1032
-			return false;
1033
-		}
1034
-	}
1035
-
1036
-
1037
-	/**
1038
-	 * @param string $path
1039
-	 * @return mixed
1040
-	 * @throws \OCP\Files\InvalidPathException
1041
-	 */
1042
-	public function getMimeType($path) {
1043
-		$this->assertPathLength($path);
1044
-		return $this->basicOperation('getMimeType', $path);
1045
-	}
1046
-
1047
-	/**
1048
-	 * @param string $type
1049
-	 * @param string $path
1050
-	 * @param bool $raw
1051
-	 * @return bool|null|string
1052
-	 */
1053
-	public function hash($type, $path, $raw = false) {
1054
-		$postFix = (substr($path, -1, 1) === '/') ? '/' : '';
1055
-		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1056
-		if (Filesystem::isValidPath($path)) {
1057
-			$path = $this->getRelativePath($absolutePath);
1058
-			if ($path == null) {
1059
-				return false;
1060
-			}
1061
-			if ($this->shouldEmitHooks($path)) {
1062
-				\OC_Hook::emit(
1063
-					Filesystem::CLASSNAME,
1064
-					Filesystem::signal_read,
1065
-					array(Filesystem::signal_param_path => $this->getHookPath($path))
1066
-				);
1067
-			}
1068
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1069
-			if ($storage) {
1070
-				$result = $storage->hash($type, $internalPath, $raw);
1071
-				return $result;
1072
-			}
1073
-		}
1074
-		return null;
1075
-	}
1076
-
1077
-	/**
1078
-	 * @param string $path
1079
-	 * @return mixed
1080
-	 * @throws \OCP\Files\InvalidPathException
1081
-	 */
1082
-	public function free_space($path = '/') {
1083
-		$this->assertPathLength($path);
1084
-		$result = $this->basicOperation('free_space', $path);
1085
-		if ($result === null) {
1086
-			throw new InvalidPathException();
1087
-		}
1088
-		return $result;
1089
-	}
1090
-
1091
-	/**
1092
-	 * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage
1093
-	 *
1094
-	 * @param string $operation
1095
-	 * @param string $path
1096
-	 * @param array $hooks (optional)
1097
-	 * @param mixed $extraParam (optional)
1098
-	 * @return mixed
1099
-	 * @throws \Exception
1100
-	 *
1101
-	 * This method takes requests for basic filesystem functions (e.g. reading & writing
1102
-	 * files), processes hooks and proxies, sanitises paths, and finally passes them on to
1103
-	 * \OC\Files\Storage\Storage for delegation to a storage backend for execution
1104
-	 */
1105
-	private function basicOperation($operation, $path, $hooks = [], $extraParam = null) {
1106
-		$postFix = (substr($path, -1, 1) === '/') ? '/' : '';
1107
-		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1108
-		if (Filesystem::isValidPath($path)
1109
-			and !Filesystem::isFileBlacklisted($path)
1110
-		) {
1111
-			$path = $this->getRelativePath($absolutePath);
1112
-			if ($path == null) {
1113
-				return false;
1114
-			}
1115
-
1116
-			if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) {
1117
-				// always a shared lock during pre-hooks so the hook can read the file
1118
-				$this->lockFile($path, ILockingProvider::LOCK_SHARED);
1119
-			}
1120
-
1121
-			$run = $this->runHooks($hooks, $path);
1122
-			/** @var \OC\Files\Storage\Storage $storage */
1123
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1124
-			if ($run and $storage) {
1125
-				if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1126
-					$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
1127
-				}
1128
-				try {
1129
-					if (!is_null($extraParam)) {
1130
-						$result = $storage->$operation($internalPath, $extraParam);
1131
-					} else {
1132
-						$result = $storage->$operation($internalPath);
1133
-					}
1134
-				} catch (\Exception $e) {
1135
-					if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1136
-						$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1137
-					} else if (in_array('read', $hooks)) {
1138
-						$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1139
-					}
1140
-					throw $e;
1141
-				}
1142
-
1143
-				if ($result && in_array('delete', $hooks) and $result) {
1144
-					$this->removeUpdate($storage, $internalPath);
1145
-				}
1146
-				if ($result && in_array('write', $hooks) and $operation !== 'fopen') {
1147
-					$this->writeUpdate($storage, $internalPath);
1148
-				}
1149
-				if ($result && in_array('touch', $hooks)) {
1150
-					$this->writeUpdate($storage, $internalPath, $extraParam);
1151
-				}
1152
-
1153
-				if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) {
1154
-					$this->changeLock($path, ILockingProvider::LOCK_SHARED);
1155
-				}
1156
-
1157
-				$unlockLater = false;
1158
-				if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) {
1159
-					$unlockLater = true;
1160
-					// make sure our unlocking callback will still be called if connection is aborted
1161
-					ignore_user_abort(true);
1162
-					$result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
1163
-						if (in_array('write', $hooks)) {
1164
-							$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1165
-						} else if (in_array('read', $hooks)) {
1166
-							$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1167
-						}
1168
-					});
1169
-				}
1170
-
1171
-				if ($this->shouldEmitHooks($path) && $result !== false) {
1172
-					if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open
1173
-						$this->runHooks($hooks, $path, true);
1174
-					}
1175
-				}
1176
-
1177
-				if (!$unlockLater
1178
-					&& (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks))
1179
-				) {
1180
-					$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1181
-				}
1182
-				return $result;
1183
-			} else {
1184
-				$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1185
-			}
1186
-		}
1187
-		return null;
1188
-	}
1189
-
1190
-	/**
1191
-	 * get the path relative to the default root for hook usage
1192
-	 *
1193
-	 * @param string $path
1194
-	 * @return string
1195
-	 */
1196
-	private function getHookPath($path) {
1197
-		if (!Filesystem::getView()) {
1198
-			return $path;
1199
-		}
1200
-		return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path));
1201
-	}
1202
-
1203
-	private function shouldEmitHooks($path = '') {
1204
-		if ($path && Cache\Scanner::isPartialFile($path)) {
1205
-			return false;
1206
-		}
1207
-		if (!Filesystem::$loaded) {
1208
-			return false;
1209
-		}
1210
-		$defaultRoot = Filesystem::getRoot();
1211
-		if ($defaultRoot === null) {
1212
-			return false;
1213
-		}
1214
-		if ($this->fakeRoot === $defaultRoot) {
1215
-			return true;
1216
-		}
1217
-		$fullPath = $this->getAbsolutePath($path);
1218
-
1219
-		if ($fullPath === $defaultRoot) {
1220
-			return true;
1221
-		}
1222
-
1223
-		return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
1224
-	}
1225
-
1226
-	/**
1227
-	 * @param string[] $hooks
1228
-	 * @param string $path
1229
-	 * @param bool $post
1230
-	 * @return bool
1231
-	 */
1232
-	private function runHooks($hooks, $path, $post = false) {
1233
-		$relativePath = $path;
1234
-		$path = $this->getHookPath($path);
1235
-		$prefix = ($post) ? 'post_' : '';
1236
-		$run = true;
1237
-		if ($this->shouldEmitHooks($relativePath)) {
1238
-			foreach ($hooks as $hook) {
1239
-				if ($hook != 'read') {
1240
-					\OC_Hook::emit(
1241
-						Filesystem::CLASSNAME,
1242
-						$prefix . $hook,
1243
-						array(
1244
-							Filesystem::signal_param_run => &$run,
1245
-							Filesystem::signal_param_path => $path
1246
-						)
1247
-					);
1248
-				} elseif (!$post) {
1249
-					\OC_Hook::emit(
1250
-						Filesystem::CLASSNAME,
1251
-						$prefix . $hook,
1252
-						array(
1253
-							Filesystem::signal_param_path => $path
1254
-						)
1255
-					);
1256
-				}
1257
-			}
1258
-		}
1259
-		return $run;
1260
-	}
1261
-
1262
-	/**
1263
-	 * check if a file or folder has been updated since $time
1264
-	 *
1265
-	 * @param string $path
1266
-	 * @param int $time
1267
-	 * @return bool
1268
-	 */
1269
-	public function hasUpdated($path, $time) {
1270
-		return $this->basicOperation('hasUpdated', $path, array(), $time);
1271
-	}
1272
-
1273
-	/**
1274
-	 * @param string $ownerId
1275
-	 * @return \OC\User\User
1276
-	 */
1277
-	private function getUserObjectForOwner($ownerId) {
1278
-		$owner = $this->userManager->get($ownerId);
1279
-		if ($owner instanceof IUser) {
1280
-			return $owner;
1281
-		} else {
1282
-			return new User($ownerId, null);
1283
-		}
1284
-	}
1285
-
1286
-	/**
1287
-	 * Get file info from cache
1288
-	 *
1289
-	 * If the file is not in cached it will be scanned
1290
-	 * If the file has changed on storage the cache will be updated
1291
-	 *
1292
-	 * @param \OC\Files\Storage\Storage $storage
1293
-	 * @param string $internalPath
1294
-	 * @param string $relativePath
1295
-	 * @return ICacheEntry|bool
1296
-	 */
1297
-	private function getCacheEntry($storage, $internalPath, $relativePath) {
1298
-		$cache = $storage->getCache($internalPath);
1299
-		$data = $cache->get($internalPath);
1300
-		$watcher = $storage->getWatcher($internalPath);
1301
-
1302
-		try {
1303
-			// if the file is not in the cache or needs to be updated, trigger the scanner and reload the data
1304
-			if (!$data || $data['size'] === -1) {
1305
-				$this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1306
-				if (!$storage->file_exists($internalPath)) {
1307
-					$this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1308
-					return false;
1309
-				}
1310
-				$scanner = $storage->getScanner($internalPath);
1311
-				$scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1312
-				$data = $cache->get($internalPath);
1313
-				$this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1314
-			} else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) {
1315
-				$this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1316
-				$watcher->update($internalPath, $data);
1317
-				$storage->getPropagator()->propagateChange($internalPath, time());
1318
-				$data = $cache->get($internalPath);
1319
-				$this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1320
-			}
1321
-		} catch (LockedException $e) {
1322
-			// if the file is locked we just use the old cache info
1323
-		}
1324
-
1325
-		return $data;
1326
-	}
1327
-
1328
-	/**
1329
-	 * get the filesystem info
1330
-	 *
1331
-	 * @param string $path
1332
-	 * @param boolean|string $includeMountPoints true to add mountpoint sizes,
1333
-	 * 'ext' to add only ext storage mount point sizes. Defaults to true.
1334
-	 * defaults to true
1335
-	 * @return \OC\Files\FileInfo|false False if file does not exist
1336
-	 */
1337
-	public function getFileInfo($path, $includeMountPoints = true) {
1338
-		$this->assertPathLength($path);
1339
-		if (!Filesystem::isValidPath($path)) {
1340
-			return false;
1341
-		}
1342
-		if (Cache\Scanner::isPartialFile($path)) {
1343
-			return $this->getPartFileInfo($path);
1344
-		}
1345
-		$relativePath = $path;
1346
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1347
-
1348
-		$mount = Filesystem::getMountManager()->find($path);
1349
-		$storage = $mount->getStorage();
1350
-		$internalPath = $mount->getInternalPath($path);
1351
-		if ($storage) {
1352
-			$data = $this->getCacheEntry($storage, $internalPath, $relativePath);
1353
-
1354
-			if (!$data instanceof ICacheEntry) {
1355
-				return false;
1356
-			}
1357
-
1358
-			if ($mount instanceof MoveableMount && $internalPath === '') {
1359
-				$data['permissions'] |= \OCP\Constants::PERMISSION_DELETE;
1360
-			}
1361
-
1362
-			$owner = $this->getUserObjectForOwner($storage->getOwner($internalPath));
1363
-			$info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner);
1364
-
1365
-			if ($data and isset($data['fileid'])) {
1366
-				if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') {
1367
-					//add the sizes of other mount points to the folder
1368
-					$extOnly = ($includeMountPoints === 'ext');
1369
-					$mounts = Filesystem::getMountManager()->findIn($path);
1370
-					$info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
1371
-						$subStorage = $mount->getStorage();
1372
-						return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
1373
-					}));
1374
-				}
1375
-			}
1376
-
1377
-			return $info;
1378
-		}
1379
-
1380
-		return false;
1381
-	}
1382
-
1383
-	/**
1384
-	 * get the content of a directory
1385
-	 *
1386
-	 * @param string $directory path under datadirectory
1387
-	 * @param string $mimetype_filter limit returned content to this mimetype or mimepart
1388
-	 * @return FileInfo[]
1389
-	 */
1390
-	public function getDirectoryContent($directory, $mimetype_filter = '') {
1391
-		$this->assertPathLength($directory);
1392
-		if (!Filesystem::isValidPath($directory)) {
1393
-			return [];
1394
-		}
1395
-		$path = $this->getAbsolutePath($directory);
1396
-		$path = Filesystem::normalizePath($path);
1397
-		$mount = $this->getMount($directory);
1398
-		$storage = $mount->getStorage();
1399
-		$internalPath = $mount->getInternalPath($path);
1400
-		if ($storage) {
1401
-			$cache = $storage->getCache($internalPath);
1402
-			$user = \OC_User::getUser();
1403
-
1404
-			$data = $this->getCacheEntry($storage, $internalPath, $directory);
1405
-
1406
-			if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) {
1407
-				return [];
1408
-			}
1409
-
1410
-			$folderId = $data['fileid'];
1411
-			$contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter
1412
-
1413
-			$sharingDisabled = \OCP\Util::isSharingDisabledForUser();
1414
-			/**
1415
-			 * @var \OC\Files\FileInfo[] $files
1416
-			 */
1417
-			$files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1418
-				if ($sharingDisabled) {
1419
-					$content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1420
-				}
1421
-				$owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
1422
-				return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
1423
-			}, $contents);
1424
-
1425
-			//add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
1426
-			$mounts = Filesystem::getMountManager()->findIn($path);
1427
-			$dirLength = strlen($path);
1428
-			foreach ($mounts as $mount) {
1429
-				$mountPoint = $mount->getMountPoint();
1430
-				$subStorage = $mount->getStorage();
1431
-				if ($subStorage) {
1432
-					$subCache = $subStorage->getCache('');
1433
-
1434
-					$rootEntry = $subCache->get('');
1435
-					if (!$rootEntry) {
1436
-						$subScanner = $subStorage->getScanner('');
1437
-						try {
1438
-							$subScanner->scanFile('');
1439
-						} catch (\OCP\Files\StorageNotAvailableException $e) {
1440
-							continue;
1441
-						} catch (\OCP\Files\StorageInvalidException $e) {
1442
-							continue;
1443
-						} catch (\Exception $e) {
1444
-							// sometimes when the storage is not available it can be any exception
1445
-							\OCP\Util::writeLog(
1446
-								'core',
1447
-								'Exception while scanning storage "' . $subStorage->getId() . '": ' .
1448
-								get_class($e) . ': ' . $e->getMessage(),
1449
-								\OCP\Util::ERROR
1450
-							);
1451
-							continue;
1452
-						}
1453
-						$rootEntry = $subCache->get('');
1454
-					}
1455
-
1456
-					if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) {
1457
-						$relativePath = trim(substr($mountPoint, $dirLength), '/');
1458
-						if ($pos = strpos($relativePath, '/')) {
1459
-							//mountpoint inside subfolder add size to the correct folder
1460
-							$entryName = substr($relativePath, 0, $pos);
1461
-							foreach ($files as &$entry) {
1462
-								if ($entry->getName() === $entryName) {
1463
-									$entry->addSubEntry($rootEntry, $mountPoint);
1464
-								}
1465
-							}
1466
-						} else { //mountpoint in this folder, add an entry for it
1467
-							$rootEntry['name'] = $relativePath;
1468
-							$rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
1469
-							$permissions = $rootEntry['permissions'];
1470
-							// do not allow renaming/deleting the mount point if they are not shared files/folders
1471
-							// for shared files/folders we use the permissions given by the owner
1472
-							if ($mount instanceof MoveableMount) {
1473
-								$rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
1474
-							} else {
1475
-								$rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
1476
-							}
1477
-
1478
-							//remove any existing entry with the same name
1479
-							foreach ($files as $i => $file) {
1480
-								if ($file['name'] === $rootEntry['name']) {
1481
-									unset($files[$i]);
1482
-									break;
1483
-								}
1484
-							}
1485
-							$rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
1486
-
1487
-							// if sharing was disabled for the user we remove the share permissions
1488
-							if (\OCP\Util::isSharingDisabledForUser()) {
1489
-								$rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1490
-							}
1491
-
1492
-							$owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
1493
-							$files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1494
-						}
1495
-					}
1496
-				}
1497
-			}
1498
-
1499
-			if ($mimetype_filter) {
1500
-				$files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
1501
-					if (strpos($mimetype_filter, '/')) {
1502
-						return $file->getMimetype() === $mimetype_filter;
1503
-					} else {
1504
-						return $file->getMimePart() === $mimetype_filter;
1505
-					}
1506
-				});
1507
-			}
1508
-
1509
-			return $files;
1510
-		} else {
1511
-			return [];
1512
-		}
1513
-	}
1514
-
1515
-	/**
1516
-	 * change file metadata
1517
-	 *
1518
-	 * @param string $path
1519
-	 * @param array|\OCP\Files\FileInfo $data
1520
-	 * @return int
1521
-	 *
1522
-	 * returns the fileid of the updated file
1523
-	 */
1524
-	public function putFileInfo($path, $data) {
1525
-		$this->assertPathLength($path);
1526
-		if ($data instanceof FileInfo) {
1527
-			$data = $data->getData();
1528
-		}
1529
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1530
-		/**
1531
-		 * @var \OC\Files\Storage\Storage $storage
1532
-		 * @var string $internalPath
1533
-		 */
1534
-		list($storage, $internalPath) = Filesystem::resolvePath($path);
1535
-		if ($storage) {
1536
-			$cache = $storage->getCache($path);
1537
-
1538
-			if (!$cache->inCache($internalPath)) {
1539
-				$scanner = $storage->getScanner($internalPath);
1540
-				$scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1541
-			}
1542
-
1543
-			return $cache->put($internalPath, $data);
1544
-		} else {
1545
-			return -1;
1546
-		}
1547
-	}
1548
-
1549
-	/**
1550
-	 * search for files with the name matching $query
1551
-	 *
1552
-	 * @param string $query
1553
-	 * @return FileInfo[]
1554
-	 */
1555
-	public function search($query) {
1556
-		return $this->searchCommon('search', array('%' . $query . '%'));
1557
-	}
1558
-
1559
-	/**
1560
-	 * search for files with the name matching $query
1561
-	 *
1562
-	 * @param string $query
1563
-	 * @return FileInfo[]
1564
-	 */
1565
-	public function searchRaw($query) {
1566
-		return $this->searchCommon('search', array($query));
1567
-	}
1568
-
1569
-	/**
1570
-	 * search for files by mimetype
1571
-	 *
1572
-	 * @param string $mimetype
1573
-	 * @return FileInfo[]
1574
-	 */
1575
-	public function searchByMime($mimetype) {
1576
-		return $this->searchCommon('searchByMime', array($mimetype));
1577
-	}
1578
-
1579
-	/**
1580
-	 * search for files by tag
1581
-	 *
1582
-	 * @param string|int $tag name or tag id
1583
-	 * @param string $userId owner of the tags
1584
-	 * @return FileInfo[]
1585
-	 */
1586
-	public function searchByTag($tag, $userId) {
1587
-		return $this->searchCommon('searchByTag', array($tag, $userId));
1588
-	}
1589
-
1590
-	/**
1591
-	 * @param string $method cache method
1592
-	 * @param array $args
1593
-	 * @return FileInfo[]
1594
-	 */
1595
-	private function searchCommon($method, $args) {
1596
-		$files = array();
1597
-		$rootLength = strlen($this->fakeRoot);
1598
-
1599
-		$mount = $this->getMount('');
1600
-		$mountPoint = $mount->getMountPoint();
1601
-		$storage = $mount->getStorage();
1602
-		if ($storage) {
1603
-			$cache = $storage->getCache('');
1604
-
1605
-			$results = call_user_func_array(array($cache, $method), $args);
1606
-			foreach ($results as $result) {
1607
-				if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
1608
-					$internalPath = $result['path'];
1609
-					$path = $mountPoint . $result['path'];
1610
-					$result['path'] = substr($mountPoint . $result['path'], $rootLength);
1611
-					$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1612
-					$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1613
-				}
1614
-			}
1615
-
1616
-			$mounts = Filesystem::getMountManager()->findIn($this->fakeRoot);
1617
-			foreach ($mounts as $mount) {
1618
-				$mountPoint = $mount->getMountPoint();
1619
-				$storage = $mount->getStorage();
1620
-				if ($storage) {
1621
-					$cache = $storage->getCache('');
1622
-
1623
-					$relativeMountPoint = substr($mountPoint, $rootLength);
1624
-					$results = call_user_func_array(array($cache, $method), $args);
1625
-					if ($results) {
1626
-						foreach ($results as $result) {
1627
-							$internalPath = $result['path'];
1628
-							$result['path'] = rtrim($relativeMountPoint . $result['path'], '/');
1629
-							$path = rtrim($mountPoint . $internalPath, '/');
1630
-							$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1631
-							$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1632
-						}
1633
-					}
1634
-				}
1635
-			}
1636
-		}
1637
-		return $files;
1638
-	}
1639
-
1640
-	/**
1641
-	 * Get the owner for a file or folder
1642
-	 *
1643
-	 * @param string $path
1644
-	 * @return string the user id of the owner
1645
-	 * @throws NotFoundException
1646
-	 */
1647
-	public function getOwner($path) {
1648
-		$info = $this->getFileInfo($path);
1649
-		if (!$info) {
1650
-			throw new NotFoundException($path . ' not found while trying to get owner');
1651
-		}
1652
-		return $info->getOwner()->getUID();
1653
-	}
1654
-
1655
-	/**
1656
-	 * get the ETag for a file or folder
1657
-	 *
1658
-	 * @param string $path
1659
-	 * @return string
1660
-	 */
1661
-	public function getETag($path) {
1662
-		/**
1663
-		 * @var Storage\Storage $storage
1664
-		 * @var string $internalPath
1665
-		 */
1666
-		list($storage, $internalPath) = $this->resolvePath($path);
1667
-		if ($storage) {
1668
-			return $storage->getETag($internalPath);
1669
-		} else {
1670
-			return null;
1671
-		}
1672
-	}
1673
-
1674
-	/**
1675
-	 * Get the path of a file by id, relative to the view
1676
-	 *
1677
-	 * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
1678
-	 *
1679
-	 * @param int $id
1680
-	 * @throws NotFoundException
1681
-	 * @return string
1682
-	 */
1683
-	public function getPath($id) {
1684
-		$id = (int)$id;
1685
-		$manager = Filesystem::getMountManager();
1686
-		$mounts = $manager->findIn($this->fakeRoot);
1687
-		$mounts[] = $manager->find($this->fakeRoot);
1688
-		// reverse the array so we start with the storage this view is in
1689
-		// which is the most likely to contain the file we're looking for
1690
-		$mounts = array_reverse($mounts);
1691
-		foreach ($mounts as $mount) {
1692
-			/**
1693
-			 * @var \OC\Files\Mount\MountPoint $mount
1694
-			 */
1695
-			if ($mount->getStorage()) {
1696
-				$cache = $mount->getStorage()->getCache();
1697
-				$internalPath = $cache->getPathById($id);
1698
-				if (is_string($internalPath)) {
1699
-					$fullPath = $mount->getMountPoint() . $internalPath;
1700
-					if (!is_null($path = $this->getRelativePath($fullPath))) {
1701
-						return $path;
1702
-					}
1703
-				}
1704
-			}
1705
-		}
1706
-		throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id));
1707
-	}
1708
-
1709
-	/**
1710
-	 * @param string $path
1711
-	 * @throws InvalidPathException
1712
-	 */
1713
-	private function assertPathLength($path) {
1714
-		$maxLen = min(PHP_MAXPATHLEN, 4000);
1715
-		// Check for the string length - performed using isset() instead of strlen()
1716
-		// because isset() is about 5x-40x faster.
1717
-		if (isset($path[$maxLen])) {
1718
-			$pathLen = strlen($path);
1719
-			throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path");
1720
-		}
1721
-	}
1722
-
1723
-	/**
1724
-	 * check if it is allowed to move a mount point to a given target.
1725
-	 * It is not allowed to move a mount point into a different mount point or
1726
-	 * into an already shared folder
1727
-	 *
1728
-	 * @param string $target path
1729
-	 * @return boolean
1730
-	 */
1731
-	private function isTargetAllowed($target) {
1732
-
1733
-		list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target);
1734
-		if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
1735
-			\OCP\Util::writeLog('files',
1736
-				'It is not allowed to move one mount point into another one',
1737
-				\OCP\Util::DEBUG);
1738
-			return false;
1739
-		}
1740
-
1741
-		// note: cannot use the view because the target is already locked
1742
-		$fileId = (int)$targetStorage->getCache()->getId($targetInternalPath);
1743
-		if ($fileId === -1) {
1744
-			// target might not exist, need to check parent instead
1745
-			$fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath));
1746
-		}
1747
-
1748
-		// check if any of the parents were shared by the current owner (include collections)
1749
-		$shares = \OCP\Share::getItemShared(
1750
-			'folder',
1751
-			$fileId,
1752
-			\OCP\Share::FORMAT_NONE,
1753
-			null,
1754
-			true
1755
-		);
1756
-
1757
-		if (count($shares) > 0) {
1758
-			\OCP\Util::writeLog('files',
1759
-				'It is not allowed to move one mount point into a shared folder',
1760
-				\OCP\Util::DEBUG);
1761
-			return false;
1762
-		}
1763
-
1764
-		return true;
1765
-	}
1766
-
1767
-	/**
1768
-	 * Get a fileinfo object for files that are ignored in the cache (part files)
1769
-	 *
1770
-	 * @param string $path
1771
-	 * @return \OCP\Files\FileInfo
1772
-	 */
1773
-	private function getPartFileInfo($path) {
1774
-		$mount = $this->getMount($path);
1775
-		$storage = $mount->getStorage();
1776
-		$internalPath = $mount->getInternalPath($this->getAbsolutePath($path));
1777
-		$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1778
-		return new FileInfo(
1779
-			$this->getAbsolutePath($path),
1780
-			$storage,
1781
-			$internalPath,
1782
-			[
1783
-				'fileid' => null,
1784
-				'mimetype' => $storage->getMimeType($internalPath),
1785
-				'name' => basename($path),
1786
-				'etag' => null,
1787
-				'size' => $storage->filesize($internalPath),
1788
-				'mtime' => $storage->filemtime($internalPath),
1789
-				'encrypted' => false,
1790
-				'permissions' => \OCP\Constants::PERMISSION_ALL
1791
-			],
1792
-			$mount,
1793
-			$owner
1794
-		);
1795
-	}
1796
-
1797
-	/**
1798
-	 * @param string $path
1799
-	 * @param string $fileName
1800
-	 * @throws InvalidPathException
1801
-	 */
1802
-	public function verifyPath($path, $fileName) {
1803
-		try {
1804
-			/** @type \OCP\Files\Storage $storage */
1805
-			list($storage, $internalPath) = $this->resolvePath($path);
1806
-			$storage->verifyPath($internalPath, $fileName);
1807
-		} catch (ReservedWordException $ex) {
1808
-			$l = \OC::$server->getL10N('lib');
1809
-			throw new InvalidPathException($l->t('File name is a reserved word'));
1810
-		} catch (InvalidCharacterInPathException $ex) {
1811
-			$l = \OC::$server->getL10N('lib');
1812
-			throw new InvalidPathException($l->t('File name contains at least one invalid character'));
1813
-		} catch (FileNameTooLongException $ex) {
1814
-			$l = \OC::$server->getL10N('lib');
1815
-			throw new InvalidPathException($l->t('File name is too long'));
1816
-		} catch (InvalidDirectoryException $ex) {
1817
-			$l = \OC::$server->getL10N('lib');
1818
-			throw new InvalidPathException($l->t('Dot files are not allowed'));
1819
-		} catch (EmptyFileNameException $ex) {
1820
-			$l = \OC::$server->getL10N('lib');
1821
-			throw new InvalidPathException($l->t('Empty filename is not allowed'));
1822
-		}
1823
-	}
1824
-
1825
-	/**
1826
-	 * get all parent folders of $path
1827
-	 *
1828
-	 * @param string $path
1829
-	 * @return string[]
1830
-	 */
1831
-	private function getParents($path) {
1832
-		$path = trim($path, '/');
1833
-		if (!$path) {
1834
-			return [];
1835
-		}
1836
-
1837
-		$parts = explode('/', $path);
1838
-
1839
-		// remove the single file
1840
-		array_pop($parts);
1841
-		$result = array('/');
1842
-		$resultPath = '';
1843
-		foreach ($parts as $part) {
1844
-			if ($part) {
1845
-				$resultPath .= '/' . $part;
1846
-				$result[] = $resultPath;
1847
-			}
1848
-		}
1849
-		return $result;
1850
-	}
1851
-
1852
-	/**
1853
-	 * Returns the mount point for which to lock
1854
-	 *
1855
-	 * @param string $absolutePath absolute path
1856
-	 * @param bool $useParentMount true to return parent mount instead of whatever
1857
-	 * is mounted directly on the given path, false otherwise
1858
-	 * @return \OC\Files\Mount\MountPoint mount point for which to apply locks
1859
-	 */
1860
-	private function getMountForLock($absolutePath, $useParentMount = false) {
1861
-		$results = [];
1862
-		$mount = Filesystem::getMountManager()->find($absolutePath);
1863
-		if (!$mount) {
1864
-			return $results;
1865
-		}
1866
-
1867
-		if ($useParentMount) {
1868
-			// find out if something is mounted directly on the path
1869
-			$internalPath = $mount->getInternalPath($absolutePath);
1870
-			if ($internalPath === '') {
1871
-				// resolve the parent mount instead
1872
-				$mount = Filesystem::getMountManager()->find(dirname($absolutePath));
1873
-			}
1874
-		}
1875
-
1876
-		return $mount;
1877
-	}
1878
-
1879
-	/**
1880
-	 * Lock the given path
1881
-	 *
1882
-	 * @param string $path the path of the file to lock, relative to the view
1883
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1884
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1885
-	 *
1886
-	 * @return bool False if the path is excluded from locking, true otherwise
1887
-	 * @throws \OCP\Lock\LockedException if the path is already locked
1888
-	 */
1889
-	private function lockPath($path, $type, $lockMountPoint = false) {
1890
-		$absolutePath = $this->getAbsolutePath($path);
1891
-		$absolutePath = Filesystem::normalizePath($absolutePath);
1892
-		if (!$this->shouldLockFile($absolutePath)) {
1893
-			return false;
1894
-		}
1895
-
1896
-		$mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1897
-		if ($mount) {
1898
-			try {
1899
-				$storage = $mount->getStorage();
1900
-				if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1901
-					$storage->acquireLock(
1902
-						$mount->getInternalPath($absolutePath),
1903
-						$type,
1904
-						$this->lockingProvider
1905
-					);
1906
-				}
1907
-			} catch (\OCP\Lock\LockedException $e) {
1908
-				// rethrow with the a human-readable path
1909
-				throw new \OCP\Lock\LockedException(
1910
-					$this->getPathRelativeToFiles($absolutePath),
1911
-					$e
1912
-				);
1913
-			}
1914
-		}
1915
-
1916
-		return true;
1917
-	}
1918
-
1919
-	/**
1920
-	 * Change the lock type
1921
-	 *
1922
-	 * @param string $path the path of the file to lock, relative to the view
1923
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1924
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1925
-	 *
1926
-	 * @return bool False if the path is excluded from locking, true otherwise
1927
-	 * @throws \OCP\Lock\LockedException if the path is already locked
1928
-	 */
1929
-	public function changeLock($path, $type, $lockMountPoint = false) {
1930
-		$path = Filesystem::normalizePath($path);
1931
-		$absolutePath = $this->getAbsolutePath($path);
1932
-		$absolutePath = Filesystem::normalizePath($absolutePath);
1933
-		if (!$this->shouldLockFile($absolutePath)) {
1934
-			return false;
1935
-		}
1936
-
1937
-		$mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1938
-		if ($mount) {
1939
-			try {
1940
-				$storage = $mount->getStorage();
1941
-				if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1942
-					$storage->changeLock(
1943
-						$mount->getInternalPath($absolutePath),
1944
-						$type,
1945
-						$this->lockingProvider
1946
-					);
1947
-				}
1948
-			} catch (\OCP\Lock\LockedException $e) {
1949
-				// rethrow with the a human-readable path
1950
-				throw new \OCP\Lock\LockedException(
1951
-					$this->getPathRelativeToFiles($absolutePath),
1952
-					$e
1953
-				);
1954
-			}
1955
-		}
1956
-
1957
-		return true;
1958
-	}
1959
-
1960
-	/**
1961
-	 * Unlock the given path
1962
-	 *
1963
-	 * @param string $path the path of the file to unlock, relative to the view
1964
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1965
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1966
-	 *
1967
-	 * @return bool False if the path is excluded from locking, true otherwise
1968
-	 */
1969
-	private function unlockPath($path, $type, $lockMountPoint = false) {
1970
-		$absolutePath = $this->getAbsolutePath($path);
1971
-		$absolutePath = Filesystem::normalizePath($absolutePath);
1972
-		if (!$this->shouldLockFile($absolutePath)) {
1973
-			return false;
1974
-		}
1975
-
1976
-		$mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1977
-		if ($mount) {
1978
-			$storage = $mount->getStorage();
1979
-			if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1980
-				$storage->releaseLock(
1981
-					$mount->getInternalPath($absolutePath),
1982
-					$type,
1983
-					$this->lockingProvider
1984
-				);
1985
-			}
1986
-		}
1987
-
1988
-		return true;
1989
-	}
1990
-
1991
-	/**
1992
-	 * Lock a path and all its parents up to the root of the view
1993
-	 *
1994
-	 * @param string $path the path of the file to lock relative to the view
1995
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1996
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1997
-	 *
1998
-	 * @return bool False if the path is excluded from locking, true otherwise
1999
-	 */
2000
-	public function lockFile($path, $type, $lockMountPoint = false) {
2001
-		$absolutePath = $this->getAbsolutePath($path);
2002
-		$absolutePath = Filesystem::normalizePath($absolutePath);
2003
-		if (!$this->shouldLockFile($absolutePath)) {
2004
-			return false;
2005
-		}
2006
-
2007
-		$this->lockPath($path, $type, $lockMountPoint);
2008
-
2009
-		$parents = $this->getParents($path);
2010
-		foreach ($parents as $parent) {
2011
-			$this->lockPath($parent, ILockingProvider::LOCK_SHARED);
2012
-		}
2013
-
2014
-		return true;
2015
-	}
2016
-
2017
-	/**
2018
-	 * Unlock a path and all its parents up to the root of the view
2019
-	 *
2020
-	 * @param string $path the path of the file to lock relative to the view
2021
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2022
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2023
-	 *
2024
-	 * @return bool False if the path is excluded from locking, true otherwise
2025
-	 */
2026
-	public function unlockFile($path, $type, $lockMountPoint = false) {
2027
-		$absolutePath = $this->getAbsolutePath($path);
2028
-		$absolutePath = Filesystem::normalizePath($absolutePath);
2029
-		if (!$this->shouldLockFile($absolutePath)) {
2030
-			return false;
2031
-		}
2032
-
2033
-		$this->unlockPath($path, $type, $lockMountPoint);
2034
-
2035
-		$parents = $this->getParents($path);
2036
-		foreach ($parents as $parent) {
2037
-			$this->unlockPath($parent, ILockingProvider::LOCK_SHARED);
2038
-		}
2039
-
2040
-		return true;
2041
-	}
2042
-
2043
-	/**
2044
-	 * Only lock files in data/user/files/
2045
-	 *
2046
-	 * @param string $path Absolute path to the file/folder we try to (un)lock
2047
-	 * @return bool
2048
-	 */
2049
-	protected function shouldLockFile($path) {
2050
-		$path = Filesystem::normalizePath($path);
2051
-
2052
-		$pathSegments = explode('/', $path);
2053
-		if (isset($pathSegments[2])) {
2054
-			// E.g.: /username/files/path-to-file
2055
-			return ($pathSegments[2] === 'files') && (count($pathSegments) > 3);
2056
-		}
2057
-
2058
-		return true;
2059
-	}
2060
-
2061
-	/**
2062
-	 * Shortens the given absolute path to be relative to
2063
-	 * "$user/files".
2064
-	 *
2065
-	 * @param string $absolutePath absolute path which is under "files"
2066
-	 *
2067
-	 * @return string path relative to "files" with trimmed slashes or null
2068
-	 * if the path was NOT relative to files
2069
-	 *
2070
-	 * @throws \InvalidArgumentException if the given path was not under "files"
2071
-	 * @since 8.1.0
2072
-	 */
2073
-	public function getPathRelativeToFiles($absolutePath) {
2074
-		$path = Filesystem::normalizePath($absolutePath);
2075
-		$parts = explode('/', trim($path, '/'), 3);
2076
-		// "$user", "files", "path/to/dir"
2077
-		if (!isset($parts[1]) || $parts[1] !== 'files') {
2078
-			$this->logger->error(
2079
-				'$absolutePath must be relative to "files", value is "%s"',
2080
-				[
2081
-					$absolutePath
2082
-				]
2083
-			);
2084
-			throw new \InvalidArgumentException('$absolutePath must be relative to "files"');
2085
-		}
2086
-		if (isset($parts[2])) {
2087
-			return $parts[2];
2088
-		}
2089
-		return '';
2090
-	}
2091
-
2092
-	/**
2093
-	 * @param string $filename
2094
-	 * @return array
2095
-	 * @throws \OC\User\NoUserException
2096
-	 * @throws NotFoundException
2097
-	 */
2098
-	public function getUidAndFilename($filename) {
2099
-		$info = $this->getFileInfo($filename);
2100
-		if (!$info instanceof \OCP\Files\FileInfo) {
2101
-			throw new NotFoundException($this->getAbsolutePath($filename) . ' not found');
2102
-		}
2103
-		$uid = $info->getOwner()->getUID();
2104
-		if ($uid != \OCP\User::getUser()) {
2105
-			Filesystem::initMountPoints($uid);
2106
-			$ownerView = new View('/' . $uid . '/files');
2107
-			try {
2108
-				$filename = $ownerView->getPath($info['fileid']);
2109
-			} catch (NotFoundException $e) {
2110
-				throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid);
2111
-			}
2112
-		}
2113
-		return [$uid, $filename];
2114
-	}
2115
-
2116
-	/**
2117
-	 * Creates parent non-existing folders
2118
-	 *
2119
-	 * @param string $filePath
2120
-	 * @return bool
2121
-	 */
2122
-	private function createParentDirectories($filePath) {
2123
-		$directoryParts = explode('/', $filePath);
2124
-		$directoryParts = array_filter($directoryParts);
2125
-		foreach ($directoryParts as $key => $part) {
2126
-			$currentPathElements = array_slice($directoryParts, 0, $key);
2127
-			$currentPath = '/' . implode('/', $currentPathElements);
2128
-			if ($this->is_file($currentPath)) {
2129
-				return false;
2130
-			}
2131
-			if (!$this->file_exists($currentPath)) {
2132
-				$this->mkdir($currentPath);
2133
-			}
2134
-		}
2135
-
2136
-		return true;
2137
-	}
85
+    /** @var string */
86
+    private $fakeRoot = '';
87
+
88
+    /**
89
+     * @var \OCP\Lock\ILockingProvider
90
+     */
91
+    protected $lockingProvider;
92
+
93
+    private $lockingEnabled;
94
+
95
+    private $updaterEnabled = true;
96
+
97
+    /** @var \OC\User\Manager */
98
+    private $userManager;
99
+
100
+    /** @var \OCP\ILogger */
101
+    private $logger;
102
+
103
+    /**
104
+     * @param string $root
105
+     * @throws \Exception If $root contains an invalid path
106
+     */
107
+    public function __construct($root = '') {
108
+        if (is_null($root)) {
109
+            throw new \InvalidArgumentException('Root can\'t be null');
110
+        }
111
+        if (!Filesystem::isValidPath($root)) {
112
+            throw new \Exception();
113
+        }
114
+
115
+        $this->fakeRoot = $root;
116
+        $this->lockingProvider = \OC::$server->getLockingProvider();
117
+        $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider);
118
+        $this->userManager = \OC::$server->getUserManager();
119
+        $this->logger = \OC::$server->getLogger();
120
+    }
121
+
122
+    public function getAbsolutePath($path = '/') {
123
+        if ($path === null) {
124
+            return null;
125
+        }
126
+        $this->assertPathLength($path);
127
+        if ($path === '') {
128
+            $path = '/';
129
+        }
130
+        if ($path[0] !== '/') {
131
+            $path = '/' . $path;
132
+        }
133
+        return $this->fakeRoot . $path;
134
+    }
135
+
136
+    /**
137
+     * change the root to a fake root
138
+     *
139
+     * @param string $fakeRoot
140
+     * @return boolean|null
141
+     */
142
+    public function chroot($fakeRoot) {
143
+        if (!$fakeRoot == '') {
144
+            if ($fakeRoot[0] !== '/') {
145
+                $fakeRoot = '/' . $fakeRoot;
146
+            }
147
+        }
148
+        $this->fakeRoot = $fakeRoot;
149
+    }
150
+
151
+    /**
152
+     * get the fake root
153
+     *
154
+     * @return string
155
+     */
156
+    public function getRoot() {
157
+        return $this->fakeRoot;
158
+    }
159
+
160
+    /**
161
+     * get path relative to the root of the view
162
+     *
163
+     * @param string $path
164
+     * @return string
165
+     */
166
+    public function getRelativePath($path) {
167
+        $this->assertPathLength($path);
168
+        if ($this->fakeRoot == '') {
169
+            return $path;
170
+        }
171
+
172
+        if (rtrim($path, '/') === rtrim($this->fakeRoot, '/')) {
173
+            return '/';
174
+        }
175
+
176
+        // missing slashes can cause wrong matches!
177
+        $root = rtrim($this->fakeRoot, '/') . '/';
178
+
179
+        if (strpos($path, $root) !== 0) {
180
+            return null;
181
+        } else {
182
+            $path = substr($path, strlen($this->fakeRoot));
183
+            if (strlen($path) === 0) {
184
+                return '/';
185
+            } else {
186
+                return $path;
187
+            }
188
+        }
189
+    }
190
+
191
+    /**
192
+     * get the mountpoint of the storage object for a path
193
+     * ( note: because a storage is not always mounted inside the fakeroot, the
194
+     * returned mountpoint is relative to the absolute root of the filesystem
195
+     * and does not take the chroot into account )
196
+     *
197
+     * @param string $path
198
+     * @return string
199
+     */
200
+    public function getMountPoint($path) {
201
+        return Filesystem::getMountPoint($this->getAbsolutePath($path));
202
+    }
203
+
204
+    /**
205
+     * get the mountpoint of the storage object for a path
206
+     * ( note: because a storage is not always mounted inside the fakeroot, the
207
+     * returned mountpoint is relative to the absolute root of the filesystem
208
+     * and does not take the chroot into account )
209
+     *
210
+     * @param string $path
211
+     * @return \OCP\Files\Mount\IMountPoint
212
+     */
213
+    public function getMount($path) {
214
+        return Filesystem::getMountManager()->find($this->getAbsolutePath($path));
215
+    }
216
+
217
+    /**
218
+     * resolve a path to a storage and internal path
219
+     *
220
+     * @param string $path
221
+     * @return array an array consisting of the storage and the internal path
222
+     */
223
+    public function resolvePath($path) {
224
+        $a = $this->getAbsolutePath($path);
225
+        $p = Filesystem::normalizePath($a);
226
+        return Filesystem::resolvePath($p);
227
+    }
228
+
229
+    /**
230
+     * return the path to a local version of the file
231
+     * we need this because we can't know if a file is stored local or not from
232
+     * outside the filestorage and for some purposes a local file is needed
233
+     *
234
+     * @param string $path
235
+     * @return string
236
+     */
237
+    public function getLocalFile($path) {
238
+        $parent = substr($path, 0, strrpos($path, '/'));
239
+        $path = $this->getAbsolutePath($path);
240
+        list($storage, $internalPath) = Filesystem::resolvePath($path);
241
+        if (Filesystem::isValidPath($parent) and $storage) {
242
+            return $storage->getLocalFile($internalPath);
243
+        } else {
244
+            return null;
245
+        }
246
+    }
247
+
248
+    /**
249
+     * @param string $path
250
+     * @return string
251
+     */
252
+    public function getLocalFolder($path) {
253
+        $parent = substr($path, 0, strrpos($path, '/'));
254
+        $path = $this->getAbsolutePath($path);
255
+        list($storage, $internalPath) = Filesystem::resolvePath($path);
256
+        if (Filesystem::isValidPath($parent) and $storage) {
257
+            return $storage->getLocalFolder($internalPath);
258
+        } else {
259
+            return null;
260
+        }
261
+    }
262
+
263
+    /**
264
+     * the following functions operate with arguments and return values identical
265
+     * to those of their PHP built-in equivalents. Mostly they are merely wrappers
266
+     * for \OC\Files\Storage\Storage via basicOperation().
267
+     */
268
+    public function mkdir($path) {
269
+        return $this->basicOperation('mkdir', $path, array('create', 'write'));
270
+    }
271
+
272
+    /**
273
+     * remove mount point
274
+     *
275
+     * @param \OC\Files\Mount\MoveableMount $mount
276
+     * @param string $path relative to data/
277
+     * @return boolean
278
+     */
279
+    protected function removeMount($mount, $path) {
280
+        if ($mount instanceof MoveableMount) {
281
+            // cut of /user/files to get the relative path to data/user/files
282
+            $pathParts = explode('/', $path, 4);
283
+            $relPath = '/' . $pathParts[3];
284
+            $this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true);
285
+            \OC_Hook::emit(
286
+                Filesystem::CLASSNAME, "umount",
287
+                array(Filesystem::signal_param_path => $relPath)
288
+            );
289
+            $this->changeLock($relPath, ILockingProvider::LOCK_EXCLUSIVE, true);
290
+            $result = $mount->removeMount();
291
+            $this->changeLock($relPath, ILockingProvider::LOCK_SHARED, true);
292
+            if ($result) {
293
+                \OC_Hook::emit(
294
+                    Filesystem::CLASSNAME, "post_umount",
295
+                    array(Filesystem::signal_param_path => $relPath)
296
+                );
297
+            }
298
+            $this->unlockFile($relPath, ILockingProvider::LOCK_SHARED, true);
299
+            return $result;
300
+        } else {
301
+            // do not allow deleting the storage's root / the mount point
302
+            // because for some storages it might delete the whole contents
303
+            // but isn't supposed to work that way
304
+            return false;
305
+        }
306
+    }
307
+
308
+    public function disableCacheUpdate() {
309
+        $this->updaterEnabled = false;
310
+    }
311
+
312
+    public function enableCacheUpdate() {
313
+        $this->updaterEnabled = true;
314
+    }
315
+
316
+    protected function writeUpdate(Storage $storage, $internalPath, $time = null) {
317
+        if ($this->updaterEnabled) {
318
+            if (is_null($time)) {
319
+                $time = time();
320
+            }
321
+            $storage->getUpdater()->update($internalPath, $time);
322
+        }
323
+    }
324
+
325
+    protected function removeUpdate(Storage $storage, $internalPath) {
326
+        if ($this->updaterEnabled) {
327
+            $storage->getUpdater()->remove($internalPath);
328
+        }
329
+    }
330
+
331
+    protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) {
332
+        if ($this->updaterEnabled) {
333
+            $targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
334
+        }
335
+    }
336
+
337
+    /**
338
+     * @param string $path
339
+     * @return bool|mixed
340
+     */
341
+    public function rmdir($path) {
342
+        $absolutePath = $this->getAbsolutePath($path);
343
+        $mount = Filesystem::getMountManager()->find($absolutePath);
344
+        if ($mount->getInternalPath($absolutePath) === '') {
345
+            return $this->removeMount($mount, $absolutePath);
346
+        }
347
+        if ($this->is_dir($path)) {
348
+            $result = $this->basicOperation('rmdir', $path, array('delete'));
349
+        } else {
350
+            $result = false;
351
+        }
352
+
353
+        if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
354
+            $storage = $mount->getStorage();
355
+            $internalPath = $mount->getInternalPath($absolutePath);
356
+            $storage->getUpdater()->remove($internalPath);
357
+        }
358
+        return $result;
359
+    }
360
+
361
+    /**
362
+     * @param string $path
363
+     * @return resource
364
+     */
365
+    public function opendir($path) {
366
+        return $this->basicOperation('opendir', $path, array('read'));
367
+    }
368
+
369
+    /**
370
+     * @param $handle
371
+     * @return mixed
372
+     */
373
+    public function readdir($handle) {
374
+        $fsLocal = new Storage\Local(array('datadir' => '/'));
375
+        return $fsLocal->readdir($handle);
376
+    }
377
+
378
+    /**
379
+     * @param string $path
380
+     * @return bool|mixed
381
+     */
382
+    public function is_dir($path) {
383
+        if ($path == '/') {
384
+            return true;
385
+        }
386
+        return $this->basicOperation('is_dir', $path);
387
+    }
388
+
389
+    /**
390
+     * @param string $path
391
+     * @return bool|mixed
392
+     */
393
+    public function is_file($path) {
394
+        if ($path == '/') {
395
+            return false;
396
+        }
397
+        return $this->basicOperation('is_file', $path);
398
+    }
399
+
400
+    /**
401
+     * @param string $path
402
+     * @return mixed
403
+     */
404
+    public function stat($path) {
405
+        return $this->basicOperation('stat', $path);
406
+    }
407
+
408
+    /**
409
+     * @param string $path
410
+     * @return mixed
411
+     */
412
+    public function filetype($path) {
413
+        return $this->basicOperation('filetype', $path);
414
+    }
415
+
416
+    /**
417
+     * @param string $path
418
+     * @return mixed
419
+     */
420
+    public function filesize($path) {
421
+        return $this->basicOperation('filesize', $path);
422
+    }
423
+
424
+    /**
425
+     * @param string $path
426
+     * @return bool|mixed
427
+     * @throws \OCP\Files\InvalidPathException
428
+     */
429
+    public function readfile($path) {
430
+        $this->assertPathLength($path);
431
+        @ob_end_clean();
432
+        $handle = $this->fopen($path, 'rb');
433
+        if ($handle) {
434
+            $chunkSize = 8192; // 8 kB chunks
435
+            while (!feof($handle)) {
436
+                echo fread($handle, $chunkSize);
437
+                flush();
438
+            }
439
+            fclose($handle);
440
+            $size = $this->filesize($path);
441
+            return $size;
442
+        }
443
+        return false;
444
+    }
445
+
446
+    /**
447
+     * @param string $path
448
+     * @param int $from
449
+     * @param int $to
450
+     * @return bool|mixed
451
+     * @throws \OCP\Files\InvalidPathException
452
+     * @throws \OCP\Files\UnseekableException
453
+     */
454
+    public function readfilePart($path, $from, $to) {
455
+        $this->assertPathLength($path);
456
+        @ob_end_clean();
457
+        $handle = $this->fopen($path, 'rb');
458
+        if ($handle) {
459
+            if (fseek($handle, $from) === 0) {
460
+                $chunkSize = 8192; // 8 kB chunks
461
+                $end = $to + 1;
462
+                while (!feof($handle) && ftell($handle) < $end) {
463
+                    $len = $end - ftell($handle);
464
+                    if ($len > $chunkSize) {
465
+                        $len = $chunkSize;
466
+                    }
467
+                    echo fread($handle, $len);
468
+                    flush();
469
+                }
470
+                $size = ftell($handle) - $from;
471
+                return $size;
472
+            }
473
+
474
+            throw new \OCP\Files\UnseekableException('fseek error');
475
+        }
476
+        return false;
477
+    }
478
+
479
+    /**
480
+     * @param string $path
481
+     * @return mixed
482
+     */
483
+    public function isCreatable($path) {
484
+        return $this->basicOperation('isCreatable', $path);
485
+    }
486
+
487
+    /**
488
+     * @param string $path
489
+     * @return mixed
490
+     */
491
+    public function isReadable($path) {
492
+        return $this->basicOperation('isReadable', $path);
493
+    }
494
+
495
+    /**
496
+     * @param string $path
497
+     * @return mixed
498
+     */
499
+    public function isUpdatable($path) {
500
+        return $this->basicOperation('isUpdatable', $path);
501
+    }
502
+
503
+    /**
504
+     * @param string $path
505
+     * @return bool|mixed
506
+     */
507
+    public function isDeletable($path) {
508
+        $absolutePath = $this->getAbsolutePath($path);
509
+        $mount = Filesystem::getMountManager()->find($absolutePath);
510
+        if ($mount->getInternalPath($absolutePath) === '') {
511
+            return $mount instanceof MoveableMount;
512
+        }
513
+        return $this->basicOperation('isDeletable', $path);
514
+    }
515
+
516
+    /**
517
+     * @param string $path
518
+     * @return mixed
519
+     */
520
+    public function isSharable($path) {
521
+        return $this->basicOperation('isSharable', $path);
522
+    }
523
+
524
+    /**
525
+     * @param string $path
526
+     * @return bool|mixed
527
+     */
528
+    public function file_exists($path) {
529
+        if ($path == '/') {
530
+            return true;
531
+        }
532
+        return $this->basicOperation('file_exists', $path);
533
+    }
534
+
535
+    /**
536
+     * @param string $path
537
+     * @return mixed
538
+     */
539
+    public function filemtime($path) {
540
+        return $this->basicOperation('filemtime', $path);
541
+    }
542
+
543
+    /**
544
+     * @param string $path
545
+     * @param int|string $mtime
546
+     * @return bool
547
+     */
548
+    public function touch($path, $mtime = null) {
549
+        if (!is_null($mtime) and !is_numeric($mtime)) {
550
+            $mtime = strtotime($mtime);
551
+        }
552
+
553
+        $hooks = array('touch');
554
+
555
+        if (!$this->file_exists($path)) {
556
+            $hooks[] = 'create';
557
+            $hooks[] = 'write';
558
+        }
559
+        $result = $this->basicOperation('touch', $path, $hooks, $mtime);
560
+        if (!$result) {
561
+            // If create file fails because of permissions on external storage like SMB folders,
562
+            // check file exists and return false if not.
563
+            if (!$this->file_exists($path)) {
564
+                return false;
565
+            }
566
+            if (is_null($mtime)) {
567
+                $mtime = time();
568
+            }
569
+            //if native touch fails, we emulate it by changing the mtime in the cache
570
+            $this->putFileInfo($path, array('mtime' => floor($mtime)));
571
+        }
572
+        return true;
573
+    }
574
+
575
+    /**
576
+     * @param string $path
577
+     * @return mixed
578
+     */
579
+    public function file_get_contents($path) {
580
+        return $this->basicOperation('file_get_contents', $path, array('read'));
581
+    }
582
+
583
+    /**
584
+     * @param bool $exists
585
+     * @param string $path
586
+     * @param bool $run
587
+     */
588
+    protected function emit_file_hooks_pre($exists, $path, &$run) {
589
+        if (!$exists) {
590
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array(
591
+                Filesystem::signal_param_path => $this->getHookPath($path),
592
+                Filesystem::signal_param_run => &$run,
593
+            ));
594
+        } else {
595
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array(
596
+                Filesystem::signal_param_path => $this->getHookPath($path),
597
+                Filesystem::signal_param_run => &$run,
598
+            ));
599
+        }
600
+        \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array(
601
+            Filesystem::signal_param_path => $this->getHookPath($path),
602
+            Filesystem::signal_param_run => &$run,
603
+        ));
604
+    }
605
+
606
+    /**
607
+     * @param bool $exists
608
+     * @param string $path
609
+     */
610
+    protected function emit_file_hooks_post($exists, $path) {
611
+        if (!$exists) {
612
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array(
613
+                Filesystem::signal_param_path => $this->getHookPath($path),
614
+            ));
615
+        } else {
616
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array(
617
+                Filesystem::signal_param_path => $this->getHookPath($path),
618
+            ));
619
+        }
620
+        \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array(
621
+            Filesystem::signal_param_path => $this->getHookPath($path),
622
+        ));
623
+    }
624
+
625
+    /**
626
+     * @param string $path
627
+     * @param mixed $data
628
+     * @return bool|mixed
629
+     * @throws \Exception
630
+     */
631
+    public function file_put_contents($path, $data) {
632
+        if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier
633
+            $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
634
+            if (Filesystem::isValidPath($path)
635
+                and !Filesystem::isFileBlacklisted($path)
636
+            ) {
637
+                $path = $this->getRelativePath($absolutePath);
638
+
639
+                $this->lockFile($path, ILockingProvider::LOCK_SHARED);
640
+
641
+                $exists = $this->file_exists($path);
642
+                $run = true;
643
+                if ($this->shouldEmitHooks($path)) {
644
+                    $this->emit_file_hooks_pre($exists, $path, $run);
645
+                }
646
+                if (!$run) {
647
+                    $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
648
+                    return false;
649
+                }
650
+
651
+                $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
652
+
653
+                /** @var \OC\Files\Storage\Storage $storage */
654
+                list($storage, $internalPath) = $this->resolvePath($path);
655
+                $target = $storage->fopen($internalPath, 'w');
656
+                if ($target) {
657
+                    list (, $result) = \OC_Helper::streamCopy($data, $target);
658
+                    fclose($target);
659
+                    fclose($data);
660
+
661
+                    $this->writeUpdate($storage, $internalPath);
662
+
663
+                    $this->changeLock($path, ILockingProvider::LOCK_SHARED);
664
+
665
+                    if ($this->shouldEmitHooks($path) && $result !== false) {
666
+                        $this->emit_file_hooks_post($exists, $path);
667
+                    }
668
+                    $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
669
+                    return $result;
670
+                } else {
671
+                    $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
672
+                    return false;
673
+                }
674
+            } else {
675
+                return false;
676
+            }
677
+        } else {
678
+            $hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write');
679
+            return $this->basicOperation('file_put_contents', $path, $hooks, $data);
680
+        }
681
+    }
682
+
683
+    /**
684
+     * @param string $path
685
+     * @return bool|mixed
686
+     */
687
+    public function unlink($path) {
688
+        if ($path === '' || $path === '/') {
689
+            // do not allow deleting the root
690
+            return false;
691
+        }
692
+        $postFix = (substr($path, -1, 1) === '/') ? '/' : '';
693
+        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
694
+        $mount = Filesystem::getMountManager()->find($absolutePath . $postFix);
695
+        if ($mount and $mount->getInternalPath($absolutePath) === '') {
696
+            return $this->removeMount($mount, $absolutePath);
697
+        }
698
+        if ($this->is_dir($path)) {
699
+            $result = $this->basicOperation('rmdir', $path, ['delete']);
700
+        } else {
701
+            $result = $this->basicOperation('unlink', $path, ['delete']);
702
+        }
703
+        if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
704
+            $storage = $mount->getStorage();
705
+            $internalPath = $mount->getInternalPath($absolutePath);
706
+            $storage->getUpdater()->remove($internalPath);
707
+            return true;
708
+        } else {
709
+            return $result;
710
+        }
711
+    }
712
+
713
+    /**
714
+     * @param string $directory
715
+     * @return bool|mixed
716
+     */
717
+    public function deleteAll($directory) {
718
+        return $this->rmdir($directory);
719
+    }
720
+
721
+    /**
722
+     * Rename/move a file or folder from the source path to target path.
723
+     *
724
+     * @param string $path1 source path
725
+     * @param string $path2 target path
726
+     *
727
+     * @return bool|mixed
728
+     */
729
+    public function rename($path1, $path2) {
730
+        $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
731
+        $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
732
+        $result = false;
733
+        if (
734
+            Filesystem::isValidPath($path2)
735
+            and Filesystem::isValidPath($path1)
736
+            and !Filesystem::isFileBlacklisted($path2)
737
+        ) {
738
+            $path1 = $this->getRelativePath($absolutePath1);
739
+            $path2 = $this->getRelativePath($absolutePath2);
740
+            $exists = $this->file_exists($path2);
741
+
742
+            if ($path1 == null or $path2 == null) {
743
+                return false;
744
+            }
745
+
746
+            $this->lockFile($path1, ILockingProvider::LOCK_SHARED, true);
747
+            try {
748
+                $this->lockFile($path2, ILockingProvider::LOCK_SHARED, true);
749
+            } catch (LockedException $e) {
750
+                $this->unlockFile($path1, ILockingProvider::LOCK_SHARED);
751
+                throw $e;
752
+            }
753
+
754
+            $run = true;
755
+            if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) {
756
+                // if it was a rename from a part file to a regular file it was a write and not a rename operation
757
+                $this->emit_file_hooks_pre($exists, $path2, $run);
758
+            } elseif ($this->shouldEmitHooks($path1)) {
759
+                \OC_Hook::emit(
760
+                    Filesystem::CLASSNAME, Filesystem::signal_rename,
761
+                    array(
762
+                        Filesystem::signal_param_oldpath => $this->getHookPath($path1),
763
+                        Filesystem::signal_param_newpath => $this->getHookPath($path2),
764
+                        Filesystem::signal_param_run => &$run
765
+                    )
766
+                );
767
+            }
768
+            if ($run) {
769
+                $this->verifyPath(dirname($path2), basename($path2));
770
+
771
+                $manager = Filesystem::getMountManager();
772
+                $mount1 = $this->getMount($path1);
773
+                $mount2 = $this->getMount($path2);
774
+                $storage1 = $mount1->getStorage();
775
+                $storage2 = $mount2->getStorage();
776
+                $internalPath1 = $mount1->getInternalPath($absolutePath1);
777
+                $internalPath2 = $mount2->getInternalPath($absolutePath2);
778
+
779
+                $this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true);
780
+                $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true);
781
+
782
+                if ($internalPath1 === '' and $mount1 instanceof MoveableMount) {
783
+                    if ($this->isTargetAllowed($absolutePath2)) {
784
+                        /**
785
+                         * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
786
+                         */
787
+                        $sourceMountPoint = $mount1->getMountPoint();
788
+                        $result = $mount1->moveMount($absolutePath2);
789
+                        $manager->moveMount($sourceMountPoint, $mount1->getMountPoint());
790
+                    } else {
791
+                        $result = false;
792
+                    }
793
+                    // moving a file/folder within the same mount point
794
+                } elseif ($storage1 === $storage2) {
795
+                    if ($storage1) {
796
+                        $result = $storage1->rename($internalPath1, $internalPath2);
797
+                    } else {
798
+                        $result = false;
799
+                    }
800
+                    // moving a file/folder between storages (from $storage1 to $storage2)
801
+                } else {
802
+                    $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
803
+                }
804
+
805
+                if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
806
+                    // if it was a rename from a part file to a regular file it was a write and not a rename operation
807
+
808
+                    $this->writeUpdate($storage2, $internalPath2);
809
+                } else if ($result) {
810
+                    if ($internalPath1 !== '') { // don't do a cache update for moved mounts
811
+                        $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2);
812
+                    }
813
+                }
814
+
815
+                $this->changeLock($path1, ILockingProvider::LOCK_SHARED, true);
816
+                $this->changeLock($path2, ILockingProvider::LOCK_SHARED, true);
817
+
818
+                if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
819
+                    if ($this->shouldEmitHooks()) {
820
+                        $this->emit_file_hooks_post($exists, $path2);
821
+                    }
822
+                } elseif ($result) {
823
+                    if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) {
824
+                        \OC_Hook::emit(
825
+                            Filesystem::CLASSNAME,
826
+                            Filesystem::signal_post_rename,
827
+                            array(
828
+                                Filesystem::signal_param_oldpath => $this->getHookPath($path1),
829
+                                Filesystem::signal_param_newpath => $this->getHookPath($path2)
830
+                            )
831
+                        );
832
+                    }
833
+                }
834
+            }
835
+            $this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true);
836
+            $this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true);
837
+        }
838
+        return $result;
839
+    }
840
+
841
+    /**
842
+     * Copy a file/folder from the source path to target path
843
+     *
844
+     * @param string $path1 source path
845
+     * @param string $path2 target path
846
+     * @param bool $preserveMtime whether to preserve mtime on the copy
847
+     *
848
+     * @return bool|mixed
849
+     */
850
+    public function copy($path1, $path2, $preserveMtime = false) {
851
+        $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
852
+        $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
853
+        $result = false;
854
+        if (
855
+            Filesystem::isValidPath($path2)
856
+            and Filesystem::isValidPath($path1)
857
+            and !Filesystem::isFileBlacklisted($path2)
858
+        ) {
859
+            $path1 = $this->getRelativePath($absolutePath1);
860
+            $path2 = $this->getRelativePath($absolutePath2);
861
+
862
+            if ($path1 == null or $path2 == null) {
863
+                return false;
864
+            }
865
+            $run = true;
866
+
867
+            $this->lockFile($path2, ILockingProvider::LOCK_SHARED);
868
+            $this->lockFile($path1, ILockingProvider::LOCK_SHARED);
869
+            $lockTypePath1 = ILockingProvider::LOCK_SHARED;
870
+            $lockTypePath2 = ILockingProvider::LOCK_SHARED;
871
+
872
+            try {
873
+
874
+                $exists = $this->file_exists($path2);
875
+                if ($this->shouldEmitHooks()) {
876
+                    \OC_Hook::emit(
877
+                        Filesystem::CLASSNAME,
878
+                        Filesystem::signal_copy,
879
+                        array(
880
+                            Filesystem::signal_param_oldpath => $this->getHookPath($path1),
881
+                            Filesystem::signal_param_newpath => $this->getHookPath($path2),
882
+                            Filesystem::signal_param_run => &$run
883
+                        )
884
+                    );
885
+                    $this->emit_file_hooks_pre($exists, $path2, $run);
886
+                }
887
+                if ($run) {
888
+                    $mount1 = $this->getMount($path1);
889
+                    $mount2 = $this->getMount($path2);
890
+                    $storage1 = $mount1->getStorage();
891
+                    $internalPath1 = $mount1->getInternalPath($absolutePath1);
892
+                    $storage2 = $mount2->getStorage();
893
+                    $internalPath2 = $mount2->getInternalPath($absolutePath2);
894
+
895
+                    $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE);
896
+                    $lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE;
897
+
898
+                    if ($mount1->getMountPoint() == $mount2->getMountPoint()) {
899
+                        if ($storage1) {
900
+                            $result = $storage1->copy($internalPath1, $internalPath2);
901
+                        } else {
902
+                            $result = false;
903
+                        }
904
+                    } else {
905
+                        $result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2);
906
+                    }
907
+
908
+                    $this->writeUpdate($storage2, $internalPath2);
909
+
910
+                    $this->changeLock($path2, ILockingProvider::LOCK_SHARED);
911
+                    $lockTypePath2 = ILockingProvider::LOCK_SHARED;
912
+
913
+                    if ($this->shouldEmitHooks() && $result !== false) {
914
+                        \OC_Hook::emit(
915
+                            Filesystem::CLASSNAME,
916
+                            Filesystem::signal_post_copy,
917
+                            array(
918
+                                Filesystem::signal_param_oldpath => $this->getHookPath($path1),
919
+                                Filesystem::signal_param_newpath => $this->getHookPath($path2)
920
+                            )
921
+                        );
922
+                        $this->emit_file_hooks_post($exists, $path2);
923
+                    }
924
+
925
+                }
926
+            } catch (\Exception $e) {
927
+                $this->unlockFile($path2, $lockTypePath2);
928
+                $this->unlockFile($path1, $lockTypePath1);
929
+                throw $e;
930
+            }
931
+
932
+            $this->unlockFile($path2, $lockTypePath2);
933
+            $this->unlockFile($path1, $lockTypePath1);
934
+
935
+        }
936
+        return $result;
937
+    }
938
+
939
+    /**
940
+     * @param string $path
941
+     * @param string $mode 'r' or 'w'
942
+     * @return resource
943
+     */
944
+    public function fopen($path, $mode) {
945
+        $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support
946
+        $hooks = array();
947
+        switch ($mode) {
948
+            case 'r':
949
+                $hooks[] = 'read';
950
+                break;
951
+            case 'r+':
952
+            case 'w+':
953
+            case 'x+':
954
+            case 'a+':
955
+                $hooks[] = 'read';
956
+                $hooks[] = 'write';
957
+                break;
958
+            case 'w':
959
+            case 'x':
960
+            case 'a':
961
+                $hooks[] = 'write';
962
+                break;
963
+            default:
964
+                \OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR);
965
+        }
966
+
967
+        if ($mode !== 'r' && $mode !== 'w') {
968
+            \OC::$server->getLogger()->info('Trying to open a file with a mode other than "r" or "w" can cause severe performance issues with some backends');
969
+        }
970
+
971
+        return $this->basicOperation('fopen', $path, $hooks, $mode);
972
+    }
973
+
974
+    /**
975
+     * @param string $path
976
+     * @return bool|string
977
+     * @throws \OCP\Files\InvalidPathException
978
+     */
979
+    public function toTmpFile($path) {
980
+        $this->assertPathLength($path);
981
+        if (Filesystem::isValidPath($path)) {
982
+            $source = $this->fopen($path, 'r');
983
+            if ($source) {
984
+                $extension = pathinfo($path, PATHINFO_EXTENSION);
985
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
986
+                file_put_contents($tmpFile, $source);
987
+                return $tmpFile;
988
+            } else {
989
+                return false;
990
+            }
991
+        } else {
992
+            return false;
993
+        }
994
+    }
995
+
996
+    /**
997
+     * @param string $tmpFile
998
+     * @param string $path
999
+     * @return bool|mixed
1000
+     * @throws \OCP\Files\InvalidPathException
1001
+     */
1002
+    public function fromTmpFile($tmpFile, $path) {
1003
+        $this->assertPathLength($path);
1004
+        if (Filesystem::isValidPath($path)) {
1005
+
1006
+            // Get directory that the file is going into
1007
+            $filePath = dirname($path);
1008
+
1009
+            // Create the directories if any
1010
+            if (!$this->file_exists($filePath)) {
1011
+                $result = $this->createParentDirectories($filePath);
1012
+                if ($result === false) {
1013
+                    return false;
1014
+                }
1015
+            }
1016
+
1017
+            $source = fopen($tmpFile, 'r');
1018
+            if ($source) {
1019
+                $result = $this->file_put_contents($path, $source);
1020
+                // $this->file_put_contents() might have already closed
1021
+                // the resource, so we check it, before trying to close it
1022
+                // to avoid messages in the error log.
1023
+                if (is_resource($source)) {
1024
+                    fclose($source);
1025
+                }
1026
+                unlink($tmpFile);
1027
+                return $result;
1028
+            } else {
1029
+                return false;
1030
+            }
1031
+        } else {
1032
+            return false;
1033
+        }
1034
+    }
1035
+
1036
+
1037
+    /**
1038
+     * @param string $path
1039
+     * @return mixed
1040
+     * @throws \OCP\Files\InvalidPathException
1041
+     */
1042
+    public function getMimeType($path) {
1043
+        $this->assertPathLength($path);
1044
+        return $this->basicOperation('getMimeType', $path);
1045
+    }
1046
+
1047
+    /**
1048
+     * @param string $type
1049
+     * @param string $path
1050
+     * @param bool $raw
1051
+     * @return bool|null|string
1052
+     */
1053
+    public function hash($type, $path, $raw = false) {
1054
+        $postFix = (substr($path, -1, 1) === '/') ? '/' : '';
1055
+        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1056
+        if (Filesystem::isValidPath($path)) {
1057
+            $path = $this->getRelativePath($absolutePath);
1058
+            if ($path == null) {
1059
+                return false;
1060
+            }
1061
+            if ($this->shouldEmitHooks($path)) {
1062
+                \OC_Hook::emit(
1063
+                    Filesystem::CLASSNAME,
1064
+                    Filesystem::signal_read,
1065
+                    array(Filesystem::signal_param_path => $this->getHookPath($path))
1066
+                );
1067
+            }
1068
+            list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1069
+            if ($storage) {
1070
+                $result = $storage->hash($type, $internalPath, $raw);
1071
+                return $result;
1072
+            }
1073
+        }
1074
+        return null;
1075
+    }
1076
+
1077
+    /**
1078
+     * @param string $path
1079
+     * @return mixed
1080
+     * @throws \OCP\Files\InvalidPathException
1081
+     */
1082
+    public function free_space($path = '/') {
1083
+        $this->assertPathLength($path);
1084
+        $result = $this->basicOperation('free_space', $path);
1085
+        if ($result === null) {
1086
+            throw new InvalidPathException();
1087
+        }
1088
+        return $result;
1089
+    }
1090
+
1091
+    /**
1092
+     * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage
1093
+     *
1094
+     * @param string $operation
1095
+     * @param string $path
1096
+     * @param array $hooks (optional)
1097
+     * @param mixed $extraParam (optional)
1098
+     * @return mixed
1099
+     * @throws \Exception
1100
+     *
1101
+     * This method takes requests for basic filesystem functions (e.g. reading & writing
1102
+     * files), processes hooks and proxies, sanitises paths, and finally passes them on to
1103
+     * \OC\Files\Storage\Storage for delegation to a storage backend for execution
1104
+     */
1105
+    private function basicOperation($operation, $path, $hooks = [], $extraParam = null) {
1106
+        $postFix = (substr($path, -1, 1) === '/') ? '/' : '';
1107
+        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1108
+        if (Filesystem::isValidPath($path)
1109
+            and !Filesystem::isFileBlacklisted($path)
1110
+        ) {
1111
+            $path = $this->getRelativePath($absolutePath);
1112
+            if ($path == null) {
1113
+                return false;
1114
+            }
1115
+
1116
+            if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) {
1117
+                // always a shared lock during pre-hooks so the hook can read the file
1118
+                $this->lockFile($path, ILockingProvider::LOCK_SHARED);
1119
+            }
1120
+
1121
+            $run = $this->runHooks($hooks, $path);
1122
+            /** @var \OC\Files\Storage\Storage $storage */
1123
+            list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1124
+            if ($run and $storage) {
1125
+                if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1126
+                    $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
1127
+                }
1128
+                try {
1129
+                    if (!is_null($extraParam)) {
1130
+                        $result = $storage->$operation($internalPath, $extraParam);
1131
+                    } else {
1132
+                        $result = $storage->$operation($internalPath);
1133
+                    }
1134
+                } catch (\Exception $e) {
1135
+                    if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1136
+                        $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1137
+                    } else if (in_array('read', $hooks)) {
1138
+                        $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1139
+                    }
1140
+                    throw $e;
1141
+                }
1142
+
1143
+                if ($result && in_array('delete', $hooks) and $result) {
1144
+                    $this->removeUpdate($storage, $internalPath);
1145
+                }
1146
+                if ($result && in_array('write', $hooks) and $operation !== 'fopen') {
1147
+                    $this->writeUpdate($storage, $internalPath);
1148
+                }
1149
+                if ($result && in_array('touch', $hooks)) {
1150
+                    $this->writeUpdate($storage, $internalPath, $extraParam);
1151
+                }
1152
+
1153
+                if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) {
1154
+                    $this->changeLock($path, ILockingProvider::LOCK_SHARED);
1155
+                }
1156
+
1157
+                $unlockLater = false;
1158
+                if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) {
1159
+                    $unlockLater = true;
1160
+                    // make sure our unlocking callback will still be called if connection is aborted
1161
+                    ignore_user_abort(true);
1162
+                    $result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
1163
+                        if (in_array('write', $hooks)) {
1164
+                            $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1165
+                        } else if (in_array('read', $hooks)) {
1166
+                            $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1167
+                        }
1168
+                    });
1169
+                }
1170
+
1171
+                if ($this->shouldEmitHooks($path) && $result !== false) {
1172
+                    if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open
1173
+                        $this->runHooks($hooks, $path, true);
1174
+                    }
1175
+                }
1176
+
1177
+                if (!$unlockLater
1178
+                    && (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks))
1179
+                ) {
1180
+                    $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1181
+                }
1182
+                return $result;
1183
+            } else {
1184
+                $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1185
+            }
1186
+        }
1187
+        return null;
1188
+    }
1189
+
1190
+    /**
1191
+     * get the path relative to the default root for hook usage
1192
+     *
1193
+     * @param string $path
1194
+     * @return string
1195
+     */
1196
+    private function getHookPath($path) {
1197
+        if (!Filesystem::getView()) {
1198
+            return $path;
1199
+        }
1200
+        return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path));
1201
+    }
1202
+
1203
+    private function shouldEmitHooks($path = '') {
1204
+        if ($path && Cache\Scanner::isPartialFile($path)) {
1205
+            return false;
1206
+        }
1207
+        if (!Filesystem::$loaded) {
1208
+            return false;
1209
+        }
1210
+        $defaultRoot = Filesystem::getRoot();
1211
+        if ($defaultRoot === null) {
1212
+            return false;
1213
+        }
1214
+        if ($this->fakeRoot === $defaultRoot) {
1215
+            return true;
1216
+        }
1217
+        $fullPath = $this->getAbsolutePath($path);
1218
+
1219
+        if ($fullPath === $defaultRoot) {
1220
+            return true;
1221
+        }
1222
+
1223
+        return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
1224
+    }
1225
+
1226
+    /**
1227
+     * @param string[] $hooks
1228
+     * @param string $path
1229
+     * @param bool $post
1230
+     * @return bool
1231
+     */
1232
+    private function runHooks($hooks, $path, $post = false) {
1233
+        $relativePath = $path;
1234
+        $path = $this->getHookPath($path);
1235
+        $prefix = ($post) ? 'post_' : '';
1236
+        $run = true;
1237
+        if ($this->shouldEmitHooks($relativePath)) {
1238
+            foreach ($hooks as $hook) {
1239
+                if ($hook != 'read') {
1240
+                    \OC_Hook::emit(
1241
+                        Filesystem::CLASSNAME,
1242
+                        $prefix . $hook,
1243
+                        array(
1244
+                            Filesystem::signal_param_run => &$run,
1245
+                            Filesystem::signal_param_path => $path
1246
+                        )
1247
+                    );
1248
+                } elseif (!$post) {
1249
+                    \OC_Hook::emit(
1250
+                        Filesystem::CLASSNAME,
1251
+                        $prefix . $hook,
1252
+                        array(
1253
+                            Filesystem::signal_param_path => $path
1254
+                        )
1255
+                    );
1256
+                }
1257
+            }
1258
+        }
1259
+        return $run;
1260
+    }
1261
+
1262
+    /**
1263
+     * check if a file or folder has been updated since $time
1264
+     *
1265
+     * @param string $path
1266
+     * @param int $time
1267
+     * @return bool
1268
+     */
1269
+    public function hasUpdated($path, $time) {
1270
+        return $this->basicOperation('hasUpdated', $path, array(), $time);
1271
+    }
1272
+
1273
+    /**
1274
+     * @param string $ownerId
1275
+     * @return \OC\User\User
1276
+     */
1277
+    private function getUserObjectForOwner($ownerId) {
1278
+        $owner = $this->userManager->get($ownerId);
1279
+        if ($owner instanceof IUser) {
1280
+            return $owner;
1281
+        } else {
1282
+            return new User($ownerId, null);
1283
+        }
1284
+    }
1285
+
1286
+    /**
1287
+     * Get file info from cache
1288
+     *
1289
+     * If the file is not in cached it will be scanned
1290
+     * If the file has changed on storage the cache will be updated
1291
+     *
1292
+     * @param \OC\Files\Storage\Storage $storage
1293
+     * @param string $internalPath
1294
+     * @param string $relativePath
1295
+     * @return ICacheEntry|bool
1296
+     */
1297
+    private function getCacheEntry($storage, $internalPath, $relativePath) {
1298
+        $cache = $storage->getCache($internalPath);
1299
+        $data = $cache->get($internalPath);
1300
+        $watcher = $storage->getWatcher($internalPath);
1301
+
1302
+        try {
1303
+            // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data
1304
+            if (!$data || $data['size'] === -1) {
1305
+                $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1306
+                if (!$storage->file_exists($internalPath)) {
1307
+                    $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1308
+                    return false;
1309
+                }
1310
+                $scanner = $storage->getScanner($internalPath);
1311
+                $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1312
+                $data = $cache->get($internalPath);
1313
+                $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1314
+            } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) {
1315
+                $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1316
+                $watcher->update($internalPath, $data);
1317
+                $storage->getPropagator()->propagateChange($internalPath, time());
1318
+                $data = $cache->get($internalPath);
1319
+                $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1320
+            }
1321
+        } catch (LockedException $e) {
1322
+            // if the file is locked we just use the old cache info
1323
+        }
1324
+
1325
+        return $data;
1326
+    }
1327
+
1328
+    /**
1329
+     * get the filesystem info
1330
+     *
1331
+     * @param string $path
1332
+     * @param boolean|string $includeMountPoints true to add mountpoint sizes,
1333
+     * 'ext' to add only ext storage mount point sizes. Defaults to true.
1334
+     * defaults to true
1335
+     * @return \OC\Files\FileInfo|false False if file does not exist
1336
+     */
1337
+    public function getFileInfo($path, $includeMountPoints = true) {
1338
+        $this->assertPathLength($path);
1339
+        if (!Filesystem::isValidPath($path)) {
1340
+            return false;
1341
+        }
1342
+        if (Cache\Scanner::isPartialFile($path)) {
1343
+            return $this->getPartFileInfo($path);
1344
+        }
1345
+        $relativePath = $path;
1346
+        $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1347
+
1348
+        $mount = Filesystem::getMountManager()->find($path);
1349
+        $storage = $mount->getStorage();
1350
+        $internalPath = $mount->getInternalPath($path);
1351
+        if ($storage) {
1352
+            $data = $this->getCacheEntry($storage, $internalPath, $relativePath);
1353
+
1354
+            if (!$data instanceof ICacheEntry) {
1355
+                return false;
1356
+            }
1357
+
1358
+            if ($mount instanceof MoveableMount && $internalPath === '') {
1359
+                $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE;
1360
+            }
1361
+
1362
+            $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath));
1363
+            $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner);
1364
+
1365
+            if ($data and isset($data['fileid'])) {
1366
+                if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') {
1367
+                    //add the sizes of other mount points to the folder
1368
+                    $extOnly = ($includeMountPoints === 'ext');
1369
+                    $mounts = Filesystem::getMountManager()->findIn($path);
1370
+                    $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
1371
+                        $subStorage = $mount->getStorage();
1372
+                        return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
1373
+                    }));
1374
+                }
1375
+            }
1376
+
1377
+            return $info;
1378
+        }
1379
+
1380
+        return false;
1381
+    }
1382
+
1383
+    /**
1384
+     * get the content of a directory
1385
+     *
1386
+     * @param string $directory path under datadirectory
1387
+     * @param string $mimetype_filter limit returned content to this mimetype or mimepart
1388
+     * @return FileInfo[]
1389
+     */
1390
+    public function getDirectoryContent($directory, $mimetype_filter = '') {
1391
+        $this->assertPathLength($directory);
1392
+        if (!Filesystem::isValidPath($directory)) {
1393
+            return [];
1394
+        }
1395
+        $path = $this->getAbsolutePath($directory);
1396
+        $path = Filesystem::normalizePath($path);
1397
+        $mount = $this->getMount($directory);
1398
+        $storage = $mount->getStorage();
1399
+        $internalPath = $mount->getInternalPath($path);
1400
+        if ($storage) {
1401
+            $cache = $storage->getCache($internalPath);
1402
+            $user = \OC_User::getUser();
1403
+
1404
+            $data = $this->getCacheEntry($storage, $internalPath, $directory);
1405
+
1406
+            if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) {
1407
+                return [];
1408
+            }
1409
+
1410
+            $folderId = $data['fileid'];
1411
+            $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter
1412
+
1413
+            $sharingDisabled = \OCP\Util::isSharingDisabledForUser();
1414
+            /**
1415
+             * @var \OC\Files\FileInfo[] $files
1416
+             */
1417
+            $files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1418
+                if ($sharingDisabled) {
1419
+                    $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1420
+                }
1421
+                $owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
1422
+                return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
1423
+            }, $contents);
1424
+
1425
+            //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
1426
+            $mounts = Filesystem::getMountManager()->findIn($path);
1427
+            $dirLength = strlen($path);
1428
+            foreach ($mounts as $mount) {
1429
+                $mountPoint = $mount->getMountPoint();
1430
+                $subStorage = $mount->getStorage();
1431
+                if ($subStorage) {
1432
+                    $subCache = $subStorage->getCache('');
1433
+
1434
+                    $rootEntry = $subCache->get('');
1435
+                    if (!$rootEntry) {
1436
+                        $subScanner = $subStorage->getScanner('');
1437
+                        try {
1438
+                            $subScanner->scanFile('');
1439
+                        } catch (\OCP\Files\StorageNotAvailableException $e) {
1440
+                            continue;
1441
+                        } catch (\OCP\Files\StorageInvalidException $e) {
1442
+                            continue;
1443
+                        } catch (\Exception $e) {
1444
+                            // sometimes when the storage is not available it can be any exception
1445
+                            \OCP\Util::writeLog(
1446
+                                'core',
1447
+                                'Exception while scanning storage "' . $subStorage->getId() . '": ' .
1448
+                                get_class($e) . ': ' . $e->getMessage(),
1449
+                                \OCP\Util::ERROR
1450
+                            );
1451
+                            continue;
1452
+                        }
1453
+                        $rootEntry = $subCache->get('');
1454
+                    }
1455
+
1456
+                    if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) {
1457
+                        $relativePath = trim(substr($mountPoint, $dirLength), '/');
1458
+                        if ($pos = strpos($relativePath, '/')) {
1459
+                            //mountpoint inside subfolder add size to the correct folder
1460
+                            $entryName = substr($relativePath, 0, $pos);
1461
+                            foreach ($files as &$entry) {
1462
+                                if ($entry->getName() === $entryName) {
1463
+                                    $entry->addSubEntry($rootEntry, $mountPoint);
1464
+                                }
1465
+                            }
1466
+                        } else { //mountpoint in this folder, add an entry for it
1467
+                            $rootEntry['name'] = $relativePath;
1468
+                            $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
1469
+                            $permissions = $rootEntry['permissions'];
1470
+                            // do not allow renaming/deleting the mount point if they are not shared files/folders
1471
+                            // for shared files/folders we use the permissions given by the owner
1472
+                            if ($mount instanceof MoveableMount) {
1473
+                                $rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
1474
+                            } else {
1475
+                                $rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
1476
+                            }
1477
+
1478
+                            //remove any existing entry with the same name
1479
+                            foreach ($files as $i => $file) {
1480
+                                if ($file['name'] === $rootEntry['name']) {
1481
+                                    unset($files[$i]);
1482
+                                    break;
1483
+                                }
1484
+                            }
1485
+                            $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
1486
+
1487
+                            // if sharing was disabled for the user we remove the share permissions
1488
+                            if (\OCP\Util::isSharingDisabledForUser()) {
1489
+                                $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1490
+                            }
1491
+
1492
+                            $owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
1493
+                            $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1494
+                        }
1495
+                    }
1496
+                }
1497
+            }
1498
+
1499
+            if ($mimetype_filter) {
1500
+                $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
1501
+                    if (strpos($mimetype_filter, '/')) {
1502
+                        return $file->getMimetype() === $mimetype_filter;
1503
+                    } else {
1504
+                        return $file->getMimePart() === $mimetype_filter;
1505
+                    }
1506
+                });
1507
+            }
1508
+
1509
+            return $files;
1510
+        } else {
1511
+            return [];
1512
+        }
1513
+    }
1514
+
1515
+    /**
1516
+     * change file metadata
1517
+     *
1518
+     * @param string $path
1519
+     * @param array|\OCP\Files\FileInfo $data
1520
+     * @return int
1521
+     *
1522
+     * returns the fileid of the updated file
1523
+     */
1524
+    public function putFileInfo($path, $data) {
1525
+        $this->assertPathLength($path);
1526
+        if ($data instanceof FileInfo) {
1527
+            $data = $data->getData();
1528
+        }
1529
+        $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1530
+        /**
1531
+         * @var \OC\Files\Storage\Storage $storage
1532
+         * @var string $internalPath
1533
+         */
1534
+        list($storage, $internalPath) = Filesystem::resolvePath($path);
1535
+        if ($storage) {
1536
+            $cache = $storage->getCache($path);
1537
+
1538
+            if (!$cache->inCache($internalPath)) {
1539
+                $scanner = $storage->getScanner($internalPath);
1540
+                $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1541
+            }
1542
+
1543
+            return $cache->put($internalPath, $data);
1544
+        } else {
1545
+            return -1;
1546
+        }
1547
+    }
1548
+
1549
+    /**
1550
+     * search for files with the name matching $query
1551
+     *
1552
+     * @param string $query
1553
+     * @return FileInfo[]
1554
+     */
1555
+    public function search($query) {
1556
+        return $this->searchCommon('search', array('%' . $query . '%'));
1557
+    }
1558
+
1559
+    /**
1560
+     * search for files with the name matching $query
1561
+     *
1562
+     * @param string $query
1563
+     * @return FileInfo[]
1564
+     */
1565
+    public function searchRaw($query) {
1566
+        return $this->searchCommon('search', array($query));
1567
+    }
1568
+
1569
+    /**
1570
+     * search for files by mimetype
1571
+     *
1572
+     * @param string $mimetype
1573
+     * @return FileInfo[]
1574
+     */
1575
+    public function searchByMime($mimetype) {
1576
+        return $this->searchCommon('searchByMime', array($mimetype));
1577
+    }
1578
+
1579
+    /**
1580
+     * search for files by tag
1581
+     *
1582
+     * @param string|int $tag name or tag id
1583
+     * @param string $userId owner of the tags
1584
+     * @return FileInfo[]
1585
+     */
1586
+    public function searchByTag($tag, $userId) {
1587
+        return $this->searchCommon('searchByTag', array($tag, $userId));
1588
+    }
1589
+
1590
+    /**
1591
+     * @param string $method cache method
1592
+     * @param array $args
1593
+     * @return FileInfo[]
1594
+     */
1595
+    private function searchCommon($method, $args) {
1596
+        $files = array();
1597
+        $rootLength = strlen($this->fakeRoot);
1598
+
1599
+        $mount = $this->getMount('');
1600
+        $mountPoint = $mount->getMountPoint();
1601
+        $storage = $mount->getStorage();
1602
+        if ($storage) {
1603
+            $cache = $storage->getCache('');
1604
+
1605
+            $results = call_user_func_array(array($cache, $method), $args);
1606
+            foreach ($results as $result) {
1607
+                if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
1608
+                    $internalPath = $result['path'];
1609
+                    $path = $mountPoint . $result['path'];
1610
+                    $result['path'] = substr($mountPoint . $result['path'], $rootLength);
1611
+                    $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1612
+                    $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1613
+                }
1614
+            }
1615
+
1616
+            $mounts = Filesystem::getMountManager()->findIn($this->fakeRoot);
1617
+            foreach ($mounts as $mount) {
1618
+                $mountPoint = $mount->getMountPoint();
1619
+                $storage = $mount->getStorage();
1620
+                if ($storage) {
1621
+                    $cache = $storage->getCache('');
1622
+
1623
+                    $relativeMountPoint = substr($mountPoint, $rootLength);
1624
+                    $results = call_user_func_array(array($cache, $method), $args);
1625
+                    if ($results) {
1626
+                        foreach ($results as $result) {
1627
+                            $internalPath = $result['path'];
1628
+                            $result['path'] = rtrim($relativeMountPoint . $result['path'], '/');
1629
+                            $path = rtrim($mountPoint . $internalPath, '/');
1630
+                            $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1631
+                            $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1632
+                        }
1633
+                    }
1634
+                }
1635
+            }
1636
+        }
1637
+        return $files;
1638
+    }
1639
+
1640
+    /**
1641
+     * Get the owner for a file or folder
1642
+     *
1643
+     * @param string $path
1644
+     * @return string the user id of the owner
1645
+     * @throws NotFoundException
1646
+     */
1647
+    public function getOwner($path) {
1648
+        $info = $this->getFileInfo($path);
1649
+        if (!$info) {
1650
+            throw new NotFoundException($path . ' not found while trying to get owner');
1651
+        }
1652
+        return $info->getOwner()->getUID();
1653
+    }
1654
+
1655
+    /**
1656
+     * get the ETag for a file or folder
1657
+     *
1658
+     * @param string $path
1659
+     * @return string
1660
+     */
1661
+    public function getETag($path) {
1662
+        /**
1663
+         * @var Storage\Storage $storage
1664
+         * @var string $internalPath
1665
+         */
1666
+        list($storage, $internalPath) = $this->resolvePath($path);
1667
+        if ($storage) {
1668
+            return $storage->getETag($internalPath);
1669
+        } else {
1670
+            return null;
1671
+        }
1672
+    }
1673
+
1674
+    /**
1675
+     * Get the path of a file by id, relative to the view
1676
+     *
1677
+     * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
1678
+     *
1679
+     * @param int $id
1680
+     * @throws NotFoundException
1681
+     * @return string
1682
+     */
1683
+    public function getPath($id) {
1684
+        $id = (int)$id;
1685
+        $manager = Filesystem::getMountManager();
1686
+        $mounts = $manager->findIn($this->fakeRoot);
1687
+        $mounts[] = $manager->find($this->fakeRoot);
1688
+        // reverse the array so we start with the storage this view is in
1689
+        // which is the most likely to contain the file we're looking for
1690
+        $mounts = array_reverse($mounts);
1691
+        foreach ($mounts as $mount) {
1692
+            /**
1693
+             * @var \OC\Files\Mount\MountPoint $mount
1694
+             */
1695
+            if ($mount->getStorage()) {
1696
+                $cache = $mount->getStorage()->getCache();
1697
+                $internalPath = $cache->getPathById($id);
1698
+                if (is_string($internalPath)) {
1699
+                    $fullPath = $mount->getMountPoint() . $internalPath;
1700
+                    if (!is_null($path = $this->getRelativePath($fullPath))) {
1701
+                        return $path;
1702
+                    }
1703
+                }
1704
+            }
1705
+        }
1706
+        throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id));
1707
+    }
1708
+
1709
+    /**
1710
+     * @param string $path
1711
+     * @throws InvalidPathException
1712
+     */
1713
+    private function assertPathLength($path) {
1714
+        $maxLen = min(PHP_MAXPATHLEN, 4000);
1715
+        // Check for the string length - performed using isset() instead of strlen()
1716
+        // because isset() is about 5x-40x faster.
1717
+        if (isset($path[$maxLen])) {
1718
+            $pathLen = strlen($path);
1719
+            throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path");
1720
+        }
1721
+    }
1722
+
1723
+    /**
1724
+     * check if it is allowed to move a mount point to a given target.
1725
+     * It is not allowed to move a mount point into a different mount point or
1726
+     * into an already shared folder
1727
+     *
1728
+     * @param string $target path
1729
+     * @return boolean
1730
+     */
1731
+    private function isTargetAllowed($target) {
1732
+
1733
+        list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target);
1734
+        if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
1735
+            \OCP\Util::writeLog('files',
1736
+                'It is not allowed to move one mount point into another one',
1737
+                \OCP\Util::DEBUG);
1738
+            return false;
1739
+        }
1740
+
1741
+        // note: cannot use the view because the target is already locked
1742
+        $fileId = (int)$targetStorage->getCache()->getId($targetInternalPath);
1743
+        if ($fileId === -1) {
1744
+            // target might not exist, need to check parent instead
1745
+            $fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath));
1746
+        }
1747
+
1748
+        // check if any of the parents were shared by the current owner (include collections)
1749
+        $shares = \OCP\Share::getItemShared(
1750
+            'folder',
1751
+            $fileId,
1752
+            \OCP\Share::FORMAT_NONE,
1753
+            null,
1754
+            true
1755
+        );
1756
+
1757
+        if (count($shares) > 0) {
1758
+            \OCP\Util::writeLog('files',
1759
+                'It is not allowed to move one mount point into a shared folder',
1760
+                \OCP\Util::DEBUG);
1761
+            return false;
1762
+        }
1763
+
1764
+        return true;
1765
+    }
1766
+
1767
+    /**
1768
+     * Get a fileinfo object for files that are ignored in the cache (part files)
1769
+     *
1770
+     * @param string $path
1771
+     * @return \OCP\Files\FileInfo
1772
+     */
1773
+    private function getPartFileInfo($path) {
1774
+        $mount = $this->getMount($path);
1775
+        $storage = $mount->getStorage();
1776
+        $internalPath = $mount->getInternalPath($this->getAbsolutePath($path));
1777
+        $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1778
+        return new FileInfo(
1779
+            $this->getAbsolutePath($path),
1780
+            $storage,
1781
+            $internalPath,
1782
+            [
1783
+                'fileid' => null,
1784
+                'mimetype' => $storage->getMimeType($internalPath),
1785
+                'name' => basename($path),
1786
+                'etag' => null,
1787
+                'size' => $storage->filesize($internalPath),
1788
+                'mtime' => $storage->filemtime($internalPath),
1789
+                'encrypted' => false,
1790
+                'permissions' => \OCP\Constants::PERMISSION_ALL
1791
+            ],
1792
+            $mount,
1793
+            $owner
1794
+        );
1795
+    }
1796
+
1797
+    /**
1798
+     * @param string $path
1799
+     * @param string $fileName
1800
+     * @throws InvalidPathException
1801
+     */
1802
+    public function verifyPath($path, $fileName) {
1803
+        try {
1804
+            /** @type \OCP\Files\Storage $storage */
1805
+            list($storage, $internalPath) = $this->resolvePath($path);
1806
+            $storage->verifyPath($internalPath, $fileName);
1807
+        } catch (ReservedWordException $ex) {
1808
+            $l = \OC::$server->getL10N('lib');
1809
+            throw new InvalidPathException($l->t('File name is a reserved word'));
1810
+        } catch (InvalidCharacterInPathException $ex) {
1811
+            $l = \OC::$server->getL10N('lib');
1812
+            throw new InvalidPathException($l->t('File name contains at least one invalid character'));
1813
+        } catch (FileNameTooLongException $ex) {
1814
+            $l = \OC::$server->getL10N('lib');
1815
+            throw new InvalidPathException($l->t('File name is too long'));
1816
+        } catch (InvalidDirectoryException $ex) {
1817
+            $l = \OC::$server->getL10N('lib');
1818
+            throw new InvalidPathException($l->t('Dot files are not allowed'));
1819
+        } catch (EmptyFileNameException $ex) {
1820
+            $l = \OC::$server->getL10N('lib');
1821
+            throw new InvalidPathException($l->t('Empty filename is not allowed'));
1822
+        }
1823
+    }
1824
+
1825
+    /**
1826
+     * get all parent folders of $path
1827
+     *
1828
+     * @param string $path
1829
+     * @return string[]
1830
+     */
1831
+    private function getParents($path) {
1832
+        $path = trim($path, '/');
1833
+        if (!$path) {
1834
+            return [];
1835
+        }
1836
+
1837
+        $parts = explode('/', $path);
1838
+
1839
+        // remove the single file
1840
+        array_pop($parts);
1841
+        $result = array('/');
1842
+        $resultPath = '';
1843
+        foreach ($parts as $part) {
1844
+            if ($part) {
1845
+                $resultPath .= '/' . $part;
1846
+                $result[] = $resultPath;
1847
+            }
1848
+        }
1849
+        return $result;
1850
+    }
1851
+
1852
+    /**
1853
+     * Returns the mount point for which to lock
1854
+     *
1855
+     * @param string $absolutePath absolute path
1856
+     * @param bool $useParentMount true to return parent mount instead of whatever
1857
+     * is mounted directly on the given path, false otherwise
1858
+     * @return \OC\Files\Mount\MountPoint mount point for which to apply locks
1859
+     */
1860
+    private function getMountForLock($absolutePath, $useParentMount = false) {
1861
+        $results = [];
1862
+        $mount = Filesystem::getMountManager()->find($absolutePath);
1863
+        if (!$mount) {
1864
+            return $results;
1865
+        }
1866
+
1867
+        if ($useParentMount) {
1868
+            // find out if something is mounted directly on the path
1869
+            $internalPath = $mount->getInternalPath($absolutePath);
1870
+            if ($internalPath === '') {
1871
+                // resolve the parent mount instead
1872
+                $mount = Filesystem::getMountManager()->find(dirname($absolutePath));
1873
+            }
1874
+        }
1875
+
1876
+        return $mount;
1877
+    }
1878
+
1879
+    /**
1880
+     * Lock the given path
1881
+     *
1882
+     * @param string $path the path of the file to lock, relative to the view
1883
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1884
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1885
+     *
1886
+     * @return bool False if the path is excluded from locking, true otherwise
1887
+     * @throws \OCP\Lock\LockedException if the path is already locked
1888
+     */
1889
+    private function lockPath($path, $type, $lockMountPoint = false) {
1890
+        $absolutePath = $this->getAbsolutePath($path);
1891
+        $absolutePath = Filesystem::normalizePath($absolutePath);
1892
+        if (!$this->shouldLockFile($absolutePath)) {
1893
+            return false;
1894
+        }
1895
+
1896
+        $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1897
+        if ($mount) {
1898
+            try {
1899
+                $storage = $mount->getStorage();
1900
+                if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1901
+                    $storage->acquireLock(
1902
+                        $mount->getInternalPath($absolutePath),
1903
+                        $type,
1904
+                        $this->lockingProvider
1905
+                    );
1906
+                }
1907
+            } catch (\OCP\Lock\LockedException $e) {
1908
+                // rethrow with the a human-readable path
1909
+                throw new \OCP\Lock\LockedException(
1910
+                    $this->getPathRelativeToFiles($absolutePath),
1911
+                    $e
1912
+                );
1913
+            }
1914
+        }
1915
+
1916
+        return true;
1917
+    }
1918
+
1919
+    /**
1920
+     * Change the lock type
1921
+     *
1922
+     * @param string $path the path of the file to lock, relative to the view
1923
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1924
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1925
+     *
1926
+     * @return bool False if the path is excluded from locking, true otherwise
1927
+     * @throws \OCP\Lock\LockedException if the path is already locked
1928
+     */
1929
+    public function changeLock($path, $type, $lockMountPoint = false) {
1930
+        $path = Filesystem::normalizePath($path);
1931
+        $absolutePath = $this->getAbsolutePath($path);
1932
+        $absolutePath = Filesystem::normalizePath($absolutePath);
1933
+        if (!$this->shouldLockFile($absolutePath)) {
1934
+            return false;
1935
+        }
1936
+
1937
+        $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1938
+        if ($mount) {
1939
+            try {
1940
+                $storage = $mount->getStorage();
1941
+                if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1942
+                    $storage->changeLock(
1943
+                        $mount->getInternalPath($absolutePath),
1944
+                        $type,
1945
+                        $this->lockingProvider
1946
+                    );
1947
+                }
1948
+            } catch (\OCP\Lock\LockedException $e) {
1949
+                // rethrow with the a human-readable path
1950
+                throw new \OCP\Lock\LockedException(
1951
+                    $this->getPathRelativeToFiles($absolutePath),
1952
+                    $e
1953
+                );
1954
+            }
1955
+        }
1956
+
1957
+        return true;
1958
+    }
1959
+
1960
+    /**
1961
+     * Unlock the given path
1962
+     *
1963
+     * @param string $path the path of the file to unlock, relative to the view
1964
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1965
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1966
+     *
1967
+     * @return bool False if the path is excluded from locking, true otherwise
1968
+     */
1969
+    private function unlockPath($path, $type, $lockMountPoint = false) {
1970
+        $absolutePath = $this->getAbsolutePath($path);
1971
+        $absolutePath = Filesystem::normalizePath($absolutePath);
1972
+        if (!$this->shouldLockFile($absolutePath)) {
1973
+            return false;
1974
+        }
1975
+
1976
+        $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1977
+        if ($mount) {
1978
+            $storage = $mount->getStorage();
1979
+            if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1980
+                $storage->releaseLock(
1981
+                    $mount->getInternalPath($absolutePath),
1982
+                    $type,
1983
+                    $this->lockingProvider
1984
+                );
1985
+            }
1986
+        }
1987
+
1988
+        return true;
1989
+    }
1990
+
1991
+    /**
1992
+     * Lock a path and all its parents up to the root of the view
1993
+     *
1994
+     * @param string $path the path of the file to lock relative to the view
1995
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1996
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1997
+     *
1998
+     * @return bool False if the path is excluded from locking, true otherwise
1999
+     */
2000
+    public function lockFile($path, $type, $lockMountPoint = false) {
2001
+        $absolutePath = $this->getAbsolutePath($path);
2002
+        $absolutePath = Filesystem::normalizePath($absolutePath);
2003
+        if (!$this->shouldLockFile($absolutePath)) {
2004
+            return false;
2005
+        }
2006
+
2007
+        $this->lockPath($path, $type, $lockMountPoint);
2008
+
2009
+        $parents = $this->getParents($path);
2010
+        foreach ($parents as $parent) {
2011
+            $this->lockPath($parent, ILockingProvider::LOCK_SHARED);
2012
+        }
2013
+
2014
+        return true;
2015
+    }
2016
+
2017
+    /**
2018
+     * Unlock a path and all its parents up to the root of the view
2019
+     *
2020
+     * @param string $path the path of the file to lock relative to the view
2021
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2022
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2023
+     *
2024
+     * @return bool False if the path is excluded from locking, true otherwise
2025
+     */
2026
+    public function unlockFile($path, $type, $lockMountPoint = false) {
2027
+        $absolutePath = $this->getAbsolutePath($path);
2028
+        $absolutePath = Filesystem::normalizePath($absolutePath);
2029
+        if (!$this->shouldLockFile($absolutePath)) {
2030
+            return false;
2031
+        }
2032
+
2033
+        $this->unlockPath($path, $type, $lockMountPoint);
2034
+
2035
+        $parents = $this->getParents($path);
2036
+        foreach ($parents as $parent) {
2037
+            $this->unlockPath($parent, ILockingProvider::LOCK_SHARED);
2038
+        }
2039
+
2040
+        return true;
2041
+    }
2042
+
2043
+    /**
2044
+     * Only lock files in data/user/files/
2045
+     *
2046
+     * @param string $path Absolute path to the file/folder we try to (un)lock
2047
+     * @return bool
2048
+     */
2049
+    protected function shouldLockFile($path) {
2050
+        $path = Filesystem::normalizePath($path);
2051
+
2052
+        $pathSegments = explode('/', $path);
2053
+        if (isset($pathSegments[2])) {
2054
+            // E.g.: /username/files/path-to-file
2055
+            return ($pathSegments[2] === 'files') && (count($pathSegments) > 3);
2056
+        }
2057
+
2058
+        return true;
2059
+    }
2060
+
2061
+    /**
2062
+     * Shortens the given absolute path to be relative to
2063
+     * "$user/files".
2064
+     *
2065
+     * @param string $absolutePath absolute path which is under "files"
2066
+     *
2067
+     * @return string path relative to "files" with trimmed slashes or null
2068
+     * if the path was NOT relative to files
2069
+     *
2070
+     * @throws \InvalidArgumentException if the given path was not under "files"
2071
+     * @since 8.1.0
2072
+     */
2073
+    public function getPathRelativeToFiles($absolutePath) {
2074
+        $path = Filesystem::normalizePath($absolutePath);
2075
+        $parts = explode('/', trim($path, '/'), 3);
2076
+        // "$user", "files", "path/to/dir"
2077
+        if (!isset($parts[1]) || $parts[1] !== 'files') {
2078
+            $this->logger->error(
2079
+                '$absolutePath must be relative to "files", value is "%s"',
2080
+                [
2081
+                    $absolutePath
2082
+                ]
2083
+            );
2084
+            throw new \InvalidArgumentException('$absolutePath must be relative to "files"');
2085
+        }
2086
+        if (isset($parts[2])) {
2087
+            return $parts[2];
2088
+        }
2089
+        return '';
2090
+    }
2091
+
2092
+    /**
2093
+     * @param string $filename
2094
+     * @return array
2095
+     * @throws \OC\User\NoUserException
2096
+     * @throws NotFoundException
2097
+     */
2098
+    public function getUidAndFilename($filename) {
2099
+        $info = $this->getFileInfo($filename);
2100
+        if (!$info instanceof \OCP\Files\FileInfo) {
2101
+            throw new NotFoundException($this->getAbsolutePath($filename) . ' not found');
2102
+        }
2103
+        $uid = $info->getOwner()->getUID();
2104
+        if ($uid != \OCP\User::getUser()) {
2105
+            Filesystem::initMountPoints($uid);
2106
+            $ownerView = new View('/' . $uid . '/files');
2107
+            try {
2108
+                $filename = $ownerView->getPath($info['fileid']);
2109
+            } catch (NotFoundException $e) {
2110
+                throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid);
2111
+            }
2112
+        }
2113
+        return [$uid, $filename];
2114
+    }
2115
+
2116
+    /**
2117
+     * Creates parent non-existing folders
2118
+     *
2119
+     * @param string $filePath
2120
+     * @return bool
2121
+     */
2122
+    private function createParentDirectories($filePath) {
2123
+        $directoryParts = explode('/', $filePath);
2124
+        $directoryParts = array_filter($directoryParts);
2125
+        foreach ($directoryParts as $key => $part) {
2126
+            $currentPathElements = array_slice($directoryParts, 0, $key);
2127
+            $currentPath = '/' . implode('/', $currentPathElements);
2128
+            if ($this->is_file($currentPath)) {
2129
+                return false;
2130
+            }
2131
+            if (!$this->file_exists($currentPath)) {
2132
+                $this->mkdir($currentPath);
2133
+            }
2134
+        }
2135
+
2136
+        return true;
2137
+    }
2138 2138
 }
Please login to merge, or discard this patch.
lib/private/Share20/Manager.php 1 patch
Indentation   +1364 added lines, -1364 removed lines patch added patch discarded remove patch
@@ -56,1392 +56,1392 @@
 block discarded – undo
56 56
  */
57 57
 class Manager implements IManager {
58 58
 
59
-	/** @var IProviderFactory */
60
-	private $factory;
61
-	/** @var ILogger */
62
-	private $logger;
63
-	/** @var IConfig */
64
-	private $config;
65
-	/** @var ISecureRandom */
66
-	private $secureRandom;
67
-	/** @var IHasher */
68
-	private $hasher;
69
-	/** @var IMountManager */
70
-	private $mountManager;
71
-	/** @var IGroupManager */
72
-	private $groupManager;
73
-	/** @var IL10N */
74
-	private $l;
75
-	/** @var IUserManager */
76
-	private $userManager;
77
-	/** @var IRootFolder */
78
-	private $rootFolder;
79
-	/** @var CappedMemoryCache */
80
-	private $sharingDisabledForUsersCache;
81
-	/** @var EventDispatcher */
82
-	private $eventDispatcher;
83
-	/** @var LegacyHooks */
84
-	private $legacyHooks;
85
-
86
-
87
-	/**
88
-	 * Manager constructor.
89
-	 *
90
-	 * @param ILogger $logger
91
-	 * @param IConfig $config
92
-	 * @param ISecureRandom $secureRandom
93
-	 * @param IHasher $hasher
94
-	 * @param IMountManager $mountManager
95
-	 * @param IGroupManager $groupManager
96
-	 * @param IL10N $l
97
-	 * @param IProviderFactory $factory
98
-	 * @param IUserManager $userManager
99
-	 * @param IRootFolder $rootFolder
100
-	 * @param EventDispatcher $eventDispatcher
101
-	 */
102
-	public function __construct(
103
-			ILogger $logger,
104
-			IConfig $config,
105
-			ISecureRandom $secureRandom,
106
-			IHasher $hasher,
107
-			IMountManager $mountManager,
108
-			IGroupManager $groupManager,
109
-			IL10N $l,
110
-			IProviderFactory $factory,
111
-			IUserManager $userManager,
112
-			IRootFolder $rootFolder,
113
-			EventDispatcher $eventDispatcher
114
-	) {
115
-		$this->logger = $logger;
116
-		$this->config = $config;
117
-		$this->secureRandom = $secureRandom;
118
-		$this->hasher = $hasher;
119
-		$this->mountManager = $mountManager;
120
-		$this->groupManager = $groupManager;
121
-		$this->l = $l;
122
-		$this->factory = $factory;
123
-		$this->userManager = $userManager;
124
-		$this->rootFolder = $rootFolder;
125
-		$this->eventDispatcher = $eventDispatcher;
126
-		$this->sharingDisabledForUsersCache = new CappedMemoryCache();
127
-		$this->legacyHooks = new LegacyHooks($this->eventDispatcher);
128
-	}
129
-
130
-	/**
131
-	 * Convert from a full share id to a tuple (providerId, shareId)
132
-	 *
133
-	 * @param string $id
134
-	 * @return string[]
135
-	 */
136
-	private function splitFullId($id) {
137
-		return explode(':', $id, 2);
138
-	}
139
-
140
-	/**
141
-	 * Verify if a password meets all requirements
142
-	 *
143
-	 * @param string $password
144
-	 * @throws \Exception
145
-	 */
146
-	protected function verifyPassword($password) {
147
-		if ($password === null) {
148
-			// No password is set, check if this is allowed.
149
-			if ($this->shareApiLinkEnforcePassword()) {
150
-				throw new \InvalidArgumentException('Passwords are enforced for link shares');
151
-			}
152
-
153
-			return;
154
-		}
155
-
156
-		// Let others verify the password
157
-		try {
158
-			$event = new GenericEvent($password);
159
-			$this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
160
-		} catch (HintException $e) {
161
-			throw new \Exception($e->getHint());
162
-		}
163
-	}
164
-
165
-	/**
166
-	 * Check for generic requirements before creating a share
167
-	 *
168
-	 * @param \OCP\Share\IShare $share
169
-	 * @throws \InvalidArgumentException
170
-	 * @throws GenericShareException
171
-	 *
172
-	 * @suppress PhanUndeclaredClassMethod
173
-	 */
174
-	protected function generalCreateChecks(\OCP\Share\IShare $share) {
175
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
176
-			// We expect a valid user as sharedWith for user shares
177
-			if (!$this->userManager->userExists($share->getSharedWith())) {
178
-				throw new \InvalidArgumentException('SharedWith is not a valid user');
179
-			}
180
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
181
-			// We expect a valid group as sharedWith for group shares
182
-			if (!$this->groupManager->groupExists($share->getSharedWith())) {
183
-				throw new \InvalidArgumentException('SharedWith is not a valid group');
184
-			}
185
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
186
-			if ($share->getSharedWith() !== null) {
187
-				throw new \InvalidArgumentException('SharedWith should be empty');
188
-			}
189
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
190
-			if ($share->getSharedWith() === null) {
191
-				throw new \InvalidArgumentException('SharedWith should not be empty');
192
-			}
193
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
194
-			if ($share->getSharedWith() === null) {
195
-				throw new \InvalidArgumentException('SharedWith should not be empty');
196
-			}
197
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
198
-			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
199
-			if ($circle === null) {
200
-				throw new \InvalidArgumentException('SharedWith is not a valid circle');
201
-			}
202
-		} else {
203
-			// We can't handle other types yet
204
-			throw new \InvalidArgumentException('unknown share type');
205
-		}
206
-
207
-		// Verify the initiator of the share is set
208
-		if ($share->getSharedBy() === null) {
209
-			throw new \InvalidArgumentException('SharedBy should be set');
210
-		}
211
-
212
-		// Cannot share with yourself
213
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
214
-			$share->getSharedWith() === $share->getSharedBy()) {
215
-			throw new \InvalidArgumentException('Can’t share with yourself');
216
-		}
217
-
218
-		// The path should be set
219
-		if ($share->getNode() === null) {
220
-			throw new \InvalidArgumentException('Path should be set');
221
-		}
222
-
223
-		// And it should be a file or a folder
224
-		if (!($share->getNode() instanceof \OCP\Files\File) &&
225
-				!($share->getNode() instanceof \OCP\Files\Folder)) {
226
-			throw new \InvalidArgumentException('Path should be either a file or a folder');
227
-		}
228
-
229
-		// And you can't share your rootfolder
230
-		if ($this->userManager->userExists($share->getSharedBy())) {
231
-			$sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath();
232
-		} else {
233
-			$sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath();
234
-		}
235
-		if ($sharedPath === $share->getNode()->getPath()) {
236
-			throw new \InvalidArgumentException('You can’t share your root folder');
237
-		}
238
-
239
-		// Check if we actually have share permissions
240
-		if (!$share->getNode()->isShareable()) {
241
-			$message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]);
242
-			throw new GenericShareException($message_t, $message_t, 404);
243
-		}
244
-
245
-		// Permissions should be set
246
-		if ($share->getPermissions() === null) {
247
-			throw new \InvalidArgumentException('A share requires permissions');
248
-		}
249
-
250
-		/*
59
+    /** @var IProviderFactory */
60
+    private $factory;
61
+    /** @var ILogger */
62
+    private $logger;
63
+    /** @var IConfig */
64
+    private $config;
65
+    /** @var ISecureRandom */
66
+    private $secureRandom;
67
+    /** @var IHasher */
68
+    private $hasher;
69
+    /** @var IMountManager */
70
+    private $mountManager;
71
+    /** @var IGroupManager */
72
+    private $groupManager;
73
+    /** @var IL10N */
74
+    private $l;
75
+    /** @var IUserManager */
76
+    private $userManager;
77
+    /** @var IRootFolder */
78
+    private $rootFolder;
79
+    /** @var CappedMemoryCache */
80
+    private $sharingDisabledForUsersCache;
81
+    /** @var EventDispatcher */
82
+    private $eventDispatcher;
83
+    /** @var LegacyHooks */
84
+    private $legacyHooks;
85
+
86
+
87
+    /**
88
+     * Manager constructor.
89
+     *
90
+     * @param ILogger $logger
91
+     * @param IConfig $config
92
+     * @param ISecureRandom $secureRandom
93
+     * @param IHasher $hasher
94
+     * @param IMountManager $mountManager
95
+     * @param IGroupManager $groupManager
96
+     * @param IL10N $l
97
+     * @param IProviderFactory $factory
98
+     * @param IUserManager $userManager
99
+     * @param IRootFolder $rootFolder
100
+     * @param EventDispatcher $eventDispatcher
101
+     */
102
+    public function __construct(
103
+            ILogger $logger,
104
+            IConfig $config,
105
+            ISecureRandom $secureRandom,
106
+            IHasher $hasher,
107
+            IMountManager $mountManager,
108
+            IGroupManager $groupManager,
109
+            IL10N $l,
110
+            IProviderFactory $factory,
111
+            IUserManager $userManager,
112
+            IRootFolder $rootFolder,
113
+            EventDispatcher $eventDispatcher
114
+    ) {
115
+        $this->logger = $logger;
116
+        $this->config = $config;
117
+        $this->secureRandom = $secureRandom;
118
+        $this->hasher = $hasher;
119
+        $this->mountManager = $mountManager;
120
+        $this->groupManager = $groupManager;
121
+        $this->l = $l;
122
+        $this->factory = $factory;
123
+        $this->userManager = $userManager;
124
+        $this->rootFolder = $rootFolder;
125
+        $this->eventDispatcher = $eventDispatcher;
126
+        $this->sharingDisabledForUsersCache = new CappedMemoryCache();
127
+        $this->legacyHooks = new LegacyHooks($this->eventDispatcher);
128
+    }
129
+
130
+    /**
131
+     * Convert from a full share id to a tuple (providerId, shareId)
132
+     *
133
+     * @param string $id
134
+     * @return string[]
135
+     */
136
+    private function splitFullId($id) {
137
+        return explode(':', $id, 2);
138
+    }
139
+
140
+    /**
141
+     * Verify if a password meets all requirements
142
+     *
143
+     * @param string $password
144
+     * @throws \Exception
145
+     */
146
+    protected function verifyPassword($password) {
147
+        if ($password === null) {
148
+            // No password is set, check if this is allowed.
149
+            if ($this->shareApiLinkEnforcePassword()) {
150
+                throw new \InvalidArgumentException('Passwords are enforced for link shares');
151
+            }
152
+
153
+            return;
154
+        }
155
+
156
+        // Let others verify the password
157
+        try {
158
+            $event = new GenericEvent($password);
159
+            $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
160
+        } catch (HintException $e) {
161
+            throw new \Exception($e->getHint());
162
+        }
163
+    }
164
+
165
+    /**
166
+     * Check for generic requirements before creating a share
167
+     *
168
+     * @param \OCP\Share\IShare $share
169
+     * @throws \InvalidArgumentException
170
+     * @throws GenericShareException
171
+     *
172
+     * @suppress PhanUndeclaredClassMethod
173
+     */
174
+    protected function generalCreateChecks(\OCP\Share\IShare $share) {
175
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
176
+            // We expect a valid user as sharedWith for user shares
177
+            if (!$this->userManager->userExists($share->getSharedWith())) {
178
+                throw new \InvalidArgumentException('SharedWith is not a valid user');
179
+            }
180
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
181
+            // We expect a valid group as sharedWith for group shares
182
+            if (!$this->groupManager->groupExists($share->getSharedWith())) {
183
+                throw new \InvalidArgumentException('SharedWith is not a valid group');
184
+            }
185
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
186
+            if ($share->getSharedWith() !== null) {
187
+                throw new \InvalidArgumentException('SharedWith should be empty');
188
+            }
189
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
190
+            if ($share->getSharedWith() === null) {
191
+                throw new \InvalidArgumentException('SharedWith should not be empty');
192
+            }
193
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
194
+            if ($share->getSharedWith() === null) {
195
+                throw new \InvalidArgumentException('SharedWith should not be empty');
196
+            }
197
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
198
+            $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
199
+            if ($circle === null) {
200
+                throw new \InvalidArgumentException('SharedWith is not a valid circle');
201
+            }
202
+        } else {
203
+            // We can't handle other types yet
204
+            throw new \InvalidArgumentException('unknown share type');
205
+        }
206
+
207
+        // Verify the initiator of the share is set
208
+        if ($share->getSharedBy() === null) {
209
+            throw new \InvalidArgumentException('SharedBy should be set');
210
+        }
211
+
212
+        // Cannot share with yourself
213
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
214
+            $share->getSharedWith() === $share->getSharedBy()) {
215
+            throw new \InvalidArgumentException('Can’t share with yourself');
216
+        }
217
+
218
+        // The path should be set
219
+        if ($share->getNode() === null) {
220
+            throw new \InvalidArgumentException('Path should be set');
221
+        }
222
+
223
+        // And it should be a file or a folder
224
+        if (!($share->getNode() instanceof \OCP\Files\File) &&
225
+                !($share->getNode() instanceof \OCP\Files\Folder)) {
226
+            throw new \InvalidArgumentException('Path should be either a file or a folder');
227
+        }
228
+
229
+        // And you can't share your rootfolder
230
+        if ($this->userManager->userExists($share->getSharedBy())) {
231
+            $sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath();
232
+        } else {
233
+            $sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath();
234
+        }
235
+        if ($sharedPath === $share->getNode()->getPath()) {
236
+            throw new \InvalidArgumentException('You can’t share your root folder');
237
+        }
238
+
239
+        // Check if we actually have share permissions
240
+        if (!$share->getNode()->isShareable()) {
241
+            $message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]);
242
+            throw new GenericShareException($message_t, $message_t, 404);
243
+        }
244
+
245
+        // Permissions should be set
246
+        if ($share->getPermissions() === null) {
247
+            throw new \InvalidArgumentException('A share requires permissions');
248
+        }
249
+
250
+        /*
251 251
 		 * Quick fix for #23536
252 252
 		 * Non moveable mount points do not have update and delete permissions
253 253
 		 * while we 'most likely' do have that on the storage.
254 254
 		 */
255
-		$permissions = $share->getNode()->getPermissions();
256
-		$mount = $share->getNode()->getMountPoint();
257
-		if (!($mount instanceof MoveableMount)) {
258
-			$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
259
-		}
260
-
261
-		// Check that we do not share with more permissions than we have
262
-		if ($share->getPermissions() & ~$permissions) {
263
-			$message_t = $this->l->t('Can’t increase permissions of %s', [$share->getNode()->getPath()]);
264
-			throw new GenericShareException($message_t, $message_t, 404);
265
-		}
266
-
267
-
268
-		// Check that read permissions are always set
269
-		// Link shares are allowed to have no read permissions to allow upload to hidden folders
270
-		$noReadPermissionRequired = $share->getShareType() === \OCP\Share::SHARE_TYPE_LINK
271
-			|| $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL;
272
-		if (!$noReadPermissionRequired &&
273
-			($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
274
-			throw new \InvalidArgumentException('Shares need at least read permissions');
275
-		}
276
-
277
-		if ($share->getNode() instanceof \OCP\Files\File) {
278
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
279
-				$message_t = $this->l->t('Files can’t be shared with delete permissions');
280
-				throw new GenericShareException($message_t);
281
-			}
282
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
283
-				$message_t = $this->l->t('Files can’t be shared with create permissions');
284
-				throw new GenericShareException($message_t);
285
-			}
286
-		}
287
-	}
288
-
289
-	/**
290
-	 * Validate if the expiration date fits the system settings
291
-	 *
292
-	 * @param \OCP\Share\IShare $share The share to validate the expiration date of
293
-	 * @return \OCP\Share\IShare The modified share object
294
-	 * @throws GenericShareException
295
-	 * @throws \InvalidArgumentException
296
-	 * @throws \Exception
297
-	 */
298
-	protected function validateExpirationDate(\OCP\Share\IShare $share) {
299
-
300
-		$expirationDate = $share->getExpirationDate();
301
-
302
-		if ($expirationDate !== null) {
303
-			//Make sure the expiration date is a date
304
-			$expirationDate->setTime(0, 0, 0);
305
-
306
-			$date = new \DateTime();
307
-			$date->setTime(0, 0, 0);
308
-			if ($date >= $expirationDate) {
309
-				$message = $this->l->t('Expiration date is in the past');
310
-				throw new GenericShareException($message, $message, 404);
311
-			}
312
-		}
313
-
314
-		// If expiredate is empty set a default one if there is a default
315
-		$fullId = null;
316
-		try {
317
-			$fullId = $share->getFullId();
318
-		} catch (\UnexpectedValueException $e) {
319
-			// This is a new share
320
-		}
321
-
322
-		if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
323
-			$expirationDate = new \DateTime();
324
-			$expirationDate->setTime(0,0,0);
325
-			$expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
326
-		}
327
-
328
-		// If we enforce the expiration date check that is does not exceed
329
-		if ($this->shareApiLinkDefaultExpireDateEnforced()) {
330
-			if ($expirationDate === null) {
331
-				throw new \InvalidArgumentException('Expiration date is enforced');
332
-			}
333
-
334
-			$date = new \DateTime();
335
-			$date->setTime(0, 0, 0);
336
-			$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
337
-			if ($date < $expirationDate) {
338
-				$message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
339
-				throw new GenericShareException($message, $message, 404);
340
-			}
341
-		}
342
-
343
-		$accepted = true;
344
-		$message = '';
345
-		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
346
-			'expirationDate' => &$expirationDate,
347
-			'accepted' => &$accepted,
348
-			'message' => &$message,
349
-			'passwordSet' => $share->getPassword() !== null,
350
-		]);
351
-
352
-		if (!$accepted) {
353
-			throw new \Exception($message);
354
-		}
355
-
356
-		$share->setExpirationDate($expirationDate);
357
-
358
-		return $share;
359
-	}
360
-
361
-	/**
362
-	 * Check for pre share requirements for user shares
363
-	 *
364
-	 * @param \OCP\Share\IShare $share
365
-	 * @throws \Exception
366
-	 */
367
-	protected function userCreateChecks(\OCP\Share\IShare $share) {
368
-		// Check if we can share with group members only
369
-		if ($this->shareWithGroupMembersOnly()) {
370
-			$sharedBy = $this->userManager->get($share->getSharedBy());
371
-			$sharedWith = $this->userManager->get($share->getSharedWith());
372
-			// Verify we can share with this user
373
-			$groups = array_intersect(
374
-					$this->groupManager->getUserGroupIds($sharedBy),
375
-					$this->groupManager->getUserGroupIds($sharedWith)
376
-			);
377
-			if (empty($groups)) {
378
-				throw new \Exception('Sharing is only allowed with group members');
379
-			}
380
-		}
381
-
382
-		/*
255
+        $permissions = $share->getNode()->getPermissions();
256
+        $mount = $share->getNode()->getMountPoint();
257
+        if (!($mount instanceof MoveableMount)) {
258
+            $permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
259
+        }
260
+
261
+        // Check that we do not share with more permissions than we have
262
+        if ($share->getPermissions() & ~$permissions) {
263
+            $message_t = $this->l->t('Can’t increase permissions of %s', [$share->getNode()->getPath()]);
264
+            throw new GenericShareException($message_t, $message_t, 404);
265
+        }
266
+
267
+
268
+        // Check that read permissions are always set
269
+        // Link shares are allowed to have no read permissions to allow upload to hidden folders
270
+        $noReadPermissionRequired = $share->getShareType() === \OCP\Share::SHARE_TYPE_LINK
271
+            || $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL;
272
+        if (!$noReadPermissionRequired &&
273
+            ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
274
+            throw new \InvalidArgumentException('Shares need at least read permissions');
275
+        }
276
+
277
+        if ($share->getNode() instanceof \OCP\Files\File) {
278
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
279
+                $message_t = $this->l->t('Files can’t be shared with delete permissions');
280
+                throw new GenericShareException($message_t);
281
+            }
282
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
283
+                $message_t = $this->l->t('Files can’t be shared with create permissions');
284
+                throw new GenericShareException($message_t);
285
+            }
286
+        }
287
+    }
288
+
289
+    /**
290
+     * Validate if the expiration date fits the system settings
291
+     *
292
+     * @param \OCP\Share\IShare $share The share to validate the expiration date of
293
+     * @return \OCP\Share\IShare The modified share object
294
+     * @throws GenericShareException
295
+     * @throws \InvalidArgumentException
296
+     * @throws \Exception
297
+     */
298
+    protected function validateExpirationDate(\OCP\Share\IShare $share) {
299
+
300
+        $expirationDate = $share->getExpirationDate();
301
+
302
+        if ($expirationDate !== null) {
303
+            //Make sure the expiration date is a date
304
+            $expirationDate->setTime(0, 0, 0);
305
+
306
+            $date = new \DateTime();
307
+            $date->setTime(0, 0, 0);
308
+            if ($date >= $expirationDate) {
309
+                $message = $this->l->t('Expiration date is in the past');
310
+                throw new GenericShareException($message, $message, 404);
311
+            }
312
+        }
313
+
314
+        // If expiredate is empty set a default one if there is a default
315
+        $fullId = null;
316
+        try {
317
+            $fullId = $share->getFullId();
318
+        } catch (\UnexpectedValueException $e) {
319
+            // This is a new share
320
+        }
321
+
322
+        if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
323
+            $expirationDate = new \DateTime();
324
+            $expirationDate->setTime(0,0,0);
325
+            $expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
326
+        }
327
+
328
+        // If we enforce the expiration date check that is does not exceed
329
+        if ($this->shareApiLinkDefaultExpireDateEnforced()) {
330
+            if ($expirationDate === null) {
331
+                throw new \InvalidArgumentException('Expiration date is enforced');
332
+            }
333
+
334
+            $date = new \DateTime();
335
+            $date->setTime(0, 0, 0);
336
+            $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
337
+            if ($date < $expirationDate) {
338
+                $message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
339
+                throw new GenericShareException($message, $message, 404);
340
+            }
341
+        }
342
+
343
+        $accepted = true;
344
+        $message = '';
345
+        \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
346
+            'expirationDate' => &$expirationDate,
347
+            'accepted' => &$accepted,
348
+            'message' => &$message,
349
+            'passwordSet' => $share->getPassword() !== null,
350
+        ]);
351
+
352
+        if (!$accepted) {
353
+            throw new \Exception($message);
354
+        }
355
+
356
+        $share->setExpirationDate($expirationDate);
357
+
358
+        return $share;
359
+    }
360
+
361
+    /**
362
+     * Check for pre share requirements for user shares
363
+     *
364
+     * @param \OCP\Share\IShare $share
365
+     * @throws \Exception
366
+     */
367
+    protected function userCreateChecks(\OCP\Share\IShare $share) {
368
+        // Check if we can share with group members only
369
+        if ($this->shareWithGroupMembersOnly()) {
370
+            $sharedBy = $this->userManager->get($share->getSharedBy());
371
+            $sharedWith = $this->userManager->get($share->getSharedWith());
372
+            // Verify we can share with this user
373
+            $groups = array_intersect(
374
+                    $this->groupManager->getUserGroupIds($sharedBy),
375
+                    $this->groupManager->getUserGroupIds($sharedWith)
376
+            );
377
+            if (empty($groups)) {
378
+                throw new \Exception('Sharing is only allowed with group members');
379
+            }
380
+        }
381
+
382
+        /*
383 383
 		 * TODO: Could be costly, fix
384 384
 		 *
385 385
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
386 386
 		 */
387
-		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
388
-		$existingShares = $provider->getSharesByPath($share->getNode());
389
-		foreach($existingShares as $existingShare) {
390
-			// Ignore if it is the same share
391
-			try {
392
-				if ($existingShare->getFullId() === $share->getFullId()) {
393
-					continue;
394
-				}
395
-			} catch (\UnexpectedValueException $e) {
396
-				//Shares are not identical
397
-			}
398
-
399
-			// Identical share already existst
400
-			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
401
-				throw new \Exception('Path is already shared with this user');
402
-			}
403
-
404
-			// The share is already shared with this user via a group share
405
-			if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
406
-				$group = $this->groupManager->get($existingShare->getSharedWith());
407
-				if (!is_null($group)) {
408
-					$user = $this->userManager->get($share->getSharedWith());
409
-
410
-					if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
411
-						throw new \Exception('Path is already shared with this user');
412
-					}
413
-				}
414
-			}
415
-		}
416
-	}
417
-
418
-	/**
419
-	 * Check for pre share requirements for group shares
420
-	 *
421
-	 * @param \OCP\Share\IShare $share
422
-	 * @throws \Exception
423
-	 */
424
-	protected function groupCreateChecks(\OCP\Share\IShare $share) {
425
-		// Verify group shares are allowed
426
-		if (!$this->allowGroupSharing()) {
427
-			throw new \Exception('Group sharing is now allowed');
428
-		}
429
-
430
-		// Verify if the user can share with this group
431
-		if ($this->shareWithGroupMembersOnly()) {
432
-			$sharedBy = $this->userManager->get($share->getSharedBy());
433
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
434
-			if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
435
-				throw new \Exception('Sharing is only allowed within your own groups');
436
-			}
437
-		}
438
-
439
-		/*
387
+        $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
388
+        $existingShares = $provider->getSharesByPath($share->getNode());
389
+        foreach($existingShares as $existingShare) {
390
+            // Ignore if it is the same share
391
+            try {
392
+                if ($existingShare->getFullId() === $share->getFullId()) {
393
+                    continue;
394
+                }
395
+            } catch (\UnexpectedValueException $e) {
396
+                //Shares are not identical
397
+            }
398
+
399
+            // Identical share already existst
400
+            if ($existingShare->getSharedWith() === $share->getSharedWith()) {
401
+                throw new \Exception('Path is already shared with this user');
402
+            }
403
+
404
+            // The share is already shared with this user via a group share
405
+            if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
406
+                $group = $this->groupManager->get($existingShare->getSharedWith());
407
+                if (!is_null($group)) {
408
+                    $user = $this->userManager->get($share->getSharedWith());
409
+
410
+                    if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
411
+                        throw new \Exception('Path is already shared with this user');
412
+                    }
413
+                }
414
+            }
415
+        }
416
+    }
417
+
418
+    /**
419
+     * Check for pre share requirements for group shares
420
+     *
421
+     * @param \OCP\Share\IShare $share
422
+     * @throws \Exception
423
+     */
424
+    protected function groupCreateChecks(\OCP\Share\IShare $share) {
425
+        // Verify group shares are allowed
426
+        if (!$this->allowGroupSharing()) {
427
+            throw new \Exception('Group sharing is now allowed');
428
+        }
429
+
430
+        // Verify if the user can share with this group
431
+        if ($this->shareWithGroupMembersOnly()) {
432
+            $sharedBy = $this->userManager->get($share->getSharedBy());
433
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
434
+            if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
435
+                throw new \Exception('Sharing is only allowed within your own groups');
436
+            }
437
+        }
438
+
439
+        /*
440 440
 		 * TODO: Could be costly, fix
441 441
 		 *
442 442
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
443 443
 		 */
444
-		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
445
-		$existingShares = $provider->getSharesByPath($share->getNode());
446
-		foreach($existingShares as $existingShare) {
447
-			try {
448
-				if ($existingShare->getFullId() === $share->getFullId()) {
449
-					continue;
450
-				}
451
-			} catch (\UnexpectedValueException $e) {
452
-				//It is a new share so just continue
453
-			}
454
-
455
-			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
456
-				throw new \Exception('Path is already shared with this group');
457
-			}
458
-		}
459
-	}
460
-
461
-	/**
462
-	 * Check for pre share requirements for link shares
463
-	 *
464
-	 * @param \OCP\Share\IShare $share
465
-	 * @throws \Exception
466
-	 */
467
-	protected function linkCreateChecks(\OCP\Share\IShare $share) {
468
-		// Are link shares allowed?
469
-		if (!$this->shareApiAllowLinks()) {
470
-			throw new \Exception('Link sharing is not allowed');
471
-		}
472
-
473
-		// Link shares by definition can't have share permissions
474
-		if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
475
-			throw new \InvalidArgumentException('Link shares can’t have reshare permissions');
476
-		}
477
-
478
-		// Check if public upload is allowed
479
-		if (!$this->shareApiLinkAllowPublicUpload() &&
480
-			($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
481
-			throw new \InvalidArgumentException('Public upload is not allowed');
482
-		}
483
-	}
484
-
485
-	/**
486
-	 * To make sure we don't get invisible link shares we set the parent
487
-	 * of a link if it is a reshare. This is a quick word around
488
-	 * until we can properly display multiple link shares in the UI
489
-	 *
490
-	 * See: https://github.com/owncloud/core/issues/22295
491
-	 *
492
-	 * FIXME: Remove once multiple link shares can be properly displayed
493
-	 *
494
-	 * @param \OCP\Share\IShare $share
495
-	 */
496
-	protected function setLinkParent(\OCP\Share\IShare $share) {
497
-
498
-		// No sense in checking if the method is not there.
499
-		if (method_exists($share, 'setParent')) {
500
-			$storage = $share->getNode()->getStorage();
501
-			if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
502
-				/** @var \OCA\Files_Sharing\SharedStorage $storage */
503
-				$share->setParent($storage->getShareId());
504
-			}
505
-		};
506
-	}
507
-
508
-	/**
509
-	 * @param File|Folder $path
510
-	 */
511
-	protected function pathCreateChecks($path) {
512
-		// Make sure that we do not share a path that contains a shared mountpoint
513
-		if ($path instanceof \OCP\Files\Folder) {
514
-			$mounts = $this->mountManager->findIn($path->getPath());
515
-			foreach($mounts as $mount) {
516
-				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
517
-					throw new \InvalidArgumentException('Path contains files shared with you');
518
-				}
519
-			}
520
-		}
521
-	}
522
-
523
-	/**
524
-	 * Check if the user that is sharing can actually share
525
-	 *
526
-	 * @param \OCP\Share\IShare $share
527
-	 * @throws \Exception
528
-	 */
529
-	protected function canShare(\OCP\Share\IShare $share) {
530
-		if (!$this->shareApiEnabled()) {
531
-			throw new \Exception('Sharing is disabled');
532
-		}
533
-
534
-		if ($this->sharingDisabledForUser($share->getSharedBy())) {
535
-			throw new \Exception('Sharing is disabled for you');
536
-		}
537
-	}
538
-
539
-	/**
540
-	 * Share a path
541
-	 *
542
-	 * @param \OCP\Share\IShare $share
543
-	 * @return Share The share object
544
-	 * @throws \Exception
545
-	 *
546
-	 * TODO: handle link share permissions or check them
547
-	 */
548
-	public function createShare(\OCP\Share\IShare $share) {
549
-		$this->canShare($share);
550
-
551
-		$this->generalCreateChecks($share);
552
-
553
-		// Verify if there are any issues with the path
554
-		$this->pathCreateChecks($share->getNode());
555
-
556
-		/*
444
+        $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
445
+        $existingShares = $provider->getSharesByPath($share->getNode());
446
+        foreach($existingShares as $existingShare) {
447
+            try {
448
+                if ($existingShare->getFullId() === $share->getFullId()) {
449
+                    continue;
450
+                }
451
+            } catch (\UnexpectedValueException $e) {
452
+                //It is a new share so just continue
453
+            }
454
+
455
+            if ($existingShare->getSharedWith() === $share->getSharedWith()) {
456
+                throw new \Exception('Path is already shared with this group');
457
+            }
458
+        }
459
+    }
460
+
461
+    /**
462
+     * Check for pre share requirements for link shares
463
+     *
464
+     * @param \OCP\Share\IShare $share
465
+     * @throws \Exception
466
+     */
467
+    protected function linkCreateChecks(\OCP\Share\IShare $share) {
468
+        // Are link shares allowed?
469
+        if (!$this->shareApiAllowLinks()) {
470
+            throw new \Exception('Link sharing is not allowed');
471
+        }
472
+
473
+        // Link shares by definition can't have share permissions
474
+        if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
475
+            throw new \InvalidArgumentException('Link shares can’t have reshare permissions');
476
+        }
477
+
478
+        // Check if public upload is allowed
479
+        if (!$this->shareApiLinkAllowPublicUpload() &&
480
+            ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
481
+            throw new \InvalidArgumentException('Public upload is not allowed');
482
+        }
483
+    }
484
+
485
+    /**
486
+     * To make sure we don't get invisible link shares we set the parent
487
+     * of a link if it is a reshare. This is a quick word around
488
+     * until we can properly display multiple link shares in the UI
489
+     *
490
+     * See: https://github.com/owncloud/core/issues/22295
491
+     *
492
+     * FIXME: Remove once multiple link shares can be properly displayed
493
+     *
494
+     * @param \OCP\Share\IShare $share
495
+     */
496
+    protected function setLinkParent(\OCP\Share\IShare $share) {
497
+
498
+        // No sense in checking if the method is not there.
499
+        if (method_exists($share, 'setParent')) {
500
+            $storage = $share->getNode()->getStorage();
501
+            if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
502
+                /** @var \OCA\Files_Sharing\SharedStorage $storage */
503
+                $share->setParent($storage->getShareId());
504
+            }
505
+        };
506
+    }
507
+
508
+    /**
509
+     * @param File|Folder $path
510
+     */
511
+    protected function pathCreateChecks($path) {
512
+        // Make sure that we do not share a path that contains a shared mountpoint
513
+        if ($path instanceof \OCP\Files\Folder) {
514
+            $mounts = $this->mountManager->findIn($path->getPath());
515
+            foreach($mounts as $mount) {
516
+                if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
517
+                    throw new \InvalidArgumentException('Path contains files shared with you');
518
+                }
519
+            }
520
+        }
521
+    }
522
+
523
+    /**
524
+     * Check if the user that is sharing can actually share
525
+     *
526
+     * @param \OCP\Share\IShare $share
527
+     * @throws \Exception
528
+     */
529
+    protected function canShare(\OCP\Share\IShare $share) {
530
+        if (!$this->shareApiEnabled()) {
531
+            throw new \Exception('Sharing is disabled');
532
+        }
533
+
534
+        if ($this->sharingDisabledForUser($share->getSharedBy())) {
535
+            throw new \Exception('Sharing is disabled for you');
536
+        }
537
+    }
538
+
539
+    /**
540
+     * Share a path
541
+     *
542
+     * @param \OCP\Share\IShare $share
543
+     * @return Share The share object
544
+     * @throws \Exception
545
+     *
546
+     * TODO: handle link share permissions or check them
547
+     */
548
+    public function createShare(\OCP\Share\IShare $share) {
549
+        $this->canShare($share);
550
+
551
+        $this->generalCreateChecks($share);
552
+
553
+        // Verify if there are any issues with the path
554
+        $this->pathCreateChecks($share->getNode());
555
+
556
+        /*
557 557
 		 * On creation of a share the owner is always the owner of the path
558 558
 		 * Except for mounted federated shares.
559 559
 		 */
560
-		$storage = $share->getNode()->getStorage();
561
-		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
562
-			$parent = $share->getNode()->getParent();
563
-			while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
564
-				$parent = $parent->getParent();
565
-			}
566
-			$share->setShareOwner($parent->getOwner()->getUID());
567
-		} else {
568
-			$share->setShareOwner($share->getNode()->getOwner()->getUID());
569
-		}
570
-
571
-		//Verify share type
572
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
573
-			$this->userCreateChecks($share);
574
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
575
-			$this->groupCreateChecks($share);
576
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
577
-			$this->linkCreateChecks($share);
578
-			$this->setLinkParent($share);
579
-
580
-			/*
560
+        $storage = $share->getNode()->getStorage();
561
+        if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
562
+            $parent = $share->getNode()->getParent();
563
+            while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
564
+                $parent = $parent->getParent();
565
+            }
566
+            $share->setShareOwner($parent->getOwner()->getUID());
567
+        } else {
568
+            $share->setShareOwner($share->getNode()->getOwner()->getUID());
569
+        }
570
+
571
+        //Verify share type
572
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
573
+            $this->userCreateChecks($share);
574
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
575
+            $this->groupCreateChecks($share);
576
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
577
+            $this->linkCreateChecks($share);
578
+            $this->setLinkParent($share);
579
+
580
+            /*
581 581
 			 * For now ignore a set token.
582 582
 			 */
583
-			$share->setToken(
584
-				$this->secureRandom->generate(
585
-					\OC\Share\Constants::TOKEN_LENGTH,
586
-					\OCP\Security\ISecureRandom::CHAR_LOWER.
587
-					\OCP\Security\ISecureRandom::CHAR_UPPER.
588
-					\OCP\Security\ISecureRandom::CHAR_DIGITS
589
-				)
590
-			);
591
-
592
-			//Verify the expiration date
593
-			$this->validateExpirationDate($share);
594
-
595
-			//Verify the password
596
-			$this->verifyPassword($share->getPassword());
597
-
598
-			// If a password is set. Hash it!
599
-			if ($share->getPassword() !== null) {
600
-				$share->setPassword($this->hasher->hash($share->getPassword()));
601
-			}
602
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
603
-			$share->setToken(
604
-				$this->secureRandom->generate(
605
-					\OC\Share\Constants::TOKEN_LENGTH,
606
-					\OCP\Security\ISecureRandom::CHAR_LOWER.
607
-					\OCP\Security\ISecureRandom::CHAR_UPPER.
608
-					\OCP\Security\ISecureRandom::CHAR_DIGITS
609
-				)
610
-			);
611
-		}
612
-
613
-		// Cannot share with the owner
614
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
615
-			$share->getSharedWith() === $share->getShareOwner()) {
616
-			throw new \InvalidArgumentException('Can’t share with the share owner');
617
-		}
618
-
619
-		// Generate the target
620
-		$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
621
-		$target = \OC\Files\Filesystem::normalizePath($target);
622
-		$share->setTarget($target);
623
-
624
-		// Pre share hook
625
-		$run = true;
626
-		$error = '';
627
-		$preHookData = [
628
-			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
629
-			'itemSource' => $share->getNode()->getId(),
630
-			'shareType' => $share->getShareType(),
631
-			'uidOwner' => $share->getSharedBy(),
632
-			'permissions' => $share->getPermissions(),
633
-			'fileSource' => $share->getNode()->getId(),
634
-			'expiration' => $share->getExpirationDate(),
635
-			'token' => $share->getToken(),
636
-			'itemTarget' => $share->getTarget(),
637
-			'shareWith' => $share->getSharedWith(),
638
-			'run' => &$run,
639
-			'error' => &$error,
640
-		];
641
-		\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
642
-
643
-		if ($run === false) {
644
-			throw new \Exception($error);
645
-		}
646
-
647
-		$oldShare = $share;
648
-		$provider = $this->factory->getProviderForType($share->getShareType());
649
-		$share = $provider->create($share);
650
-		//reuse the node we already have
651
-		$share->setNode($oldShare->getNode());
652
-
653
-		// Post share hook
654
-		$postHookData = [
655
-			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
656
-			'itemSource' => $share->getNode()->getId(),
657
-			'shareType' => $share->getShareType(),
658
-			'uidOwner' => $share->getSharedBy(),
659
-			'permissions' => $share->getPermissions(),
660
-			'fileSource' => $share->getNode()->getId(),
661
-			'expiration' => $share->getExpirationDate(),
662
-			'token' => $share->getToken(),
663
-			'id' => $share->getId(),
664
-			'shareWith' => $share->getSharedWith(),
665
-			'itemTarget' => $share->getTarget(),
666
-			'fileTarget' => $share->getTarget(),
667
-		];
668
-
669
-		\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
670
-
671
-		return $share;
672
-	}
673
-
674
-	/**
675
-	 * Update a share
676
-	 *
677
-	 * @param \OCP\Share\IShare $share
678
-	 * @return \OCP\Share\IShare The share object
679
-	 * @throws \InvalidArgumentException
680
-	 */
681
-	public function updateShare(\OCP\Share\IShare $share) {
682
-		$expirationDateUpdated = false;
683
-
684
-		$this->canShare($share);
685
-
686
-		try {
687
-			$originalShare = $this->getShareById($share->getFullId());
688
-		} catch (\UnexpectedValueException $e) {
689
-			throw new \InvalidArgumentException('Share does not have a full id');
690
-		}
691
-
692
-		// We can't change the share type!
693
-		if ($share->getShareType() !== $originalShare->getShareType()) {
694
-			throw new \InvalidArgumentException('Can’t change share type');
695
-		}
696
-
697
-		// We can only change the recipient on user shares
698
-		if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
699
-		    $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
700
-			throw new \InvalidArgumentException('Can only update recipient on user shares');
701
-		}
702
-
703
-		// Cannot share with the owner
704
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
705
-			$share->getSharedWith() === $share->getShareOwner()) {
706
-			throw new \InvalidArgumentException('Can’t share with the share owner');
707
-		}
708
-
709
-		$this->generalCreateChecks($share);
710
-
711
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
712
-			$this->userCreateChecks($share);
713
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
714
-			$this->groupCreateChecks($share);
715
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
716
-			$this->linkCreateChecks($share);
717
-
718
-			$this->updateSharePasswordIfNeeded($share, $originalShare);
719
-
720
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
721
-				//Verify the expiration date
722
-				$this->validateExpirationDate($share);
723
-				$expirationDateUpdated = true;
724
-			}
725
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
726
-			$plainTextPassword = $share->getPassword();
727
-			if (!$this->updateSharePasswordIfNeeded($share, $originalShare)) {
728
-				$plainTextPassword = null;
729
-			}
730
-		}
731
-
732
-		$this->pathCreateChecks($share->getNode());
733
-
734
-		// Now update the share!
735
-		$provider = $this->factory->getProviderForType($share->getShareType());
736
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
737
-			$share = $provider->update($share, $plainTextPassword);
738
-		} else {
739
-			$share = $provider->update($share);
740
-		}
741
-
742
-		if ($expirationDateUpdated === true) {
743
-			\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [
744
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
745
-				'itemSource' => $share->getNode()->getId(),
746
-				'date' => $share->getExpirationDate(),
747
-				'uidOwner' => $share->getSharedBy(),
748
-			]);
749
-		}
750
-
751
-		if ($share->getPassword() !== $originalShare->getPassword()) {
752
-			\OC_Hook::emit('OCP\Share', 'post_update_password', [
753
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
754
-				'itemSource' => $share->getNode()->getId(),
755
-				'uidOwner' => $share->getSharedBy(),
756
-				'token' => $share->getToken(),
757
-				'disabled' => is_null($share->getPassword()),
758
-			]);
759
-		}
760
-
761
-		if ($share->getPermissions() !== $originalShare->getPermissions()) {
762
-			if ($this->userManager->userExists($share->getShareOwner())) {
763
-				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
764
-			} else {
765
-				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
766
-			}
767
-			\OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
768
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
769
-				'itemSource' => $share->getNode()->getId(),
770
-				'shareType' => $share->getShareType(),
771
-				'shareWith' => $share->getSharedWith(),
772
-				'uidOwner' => $share->getSharedBy(),
773
-				'permissions' => $share->getPermissions(),
774
-				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
775
-			));
776
-		}
777
-
778
-		return $share;
779
-	}
780
-
781
-	/**
782
-	 * Updates the password of the given share if it is not the same as the
783
-	 * password of the original share.
784
-	 *
785
-	 * @param \OCP\Share\IShare $share the share to update its password.
786
-	 * @param \OCP\Share\IShare $originalShare the original share to compare its
787
-	 *        password with.
788
-	 * @return boolean whether the password was updated or not.
789
-	 */
790
-	private function updateSharePasswordIfNeeded(\OCP\Share\IShare $share, \OCP\Share\IShare $originalShare) {
791
-		// Password updated.
792
-		if ($share->getPassword() !== $originalShare->getPassword()) {
793
-			//Verify the password
794
-			$this->verifyPassword($share->getPassword());
795
-
796
-			// If a password is set. Hash it!
797
-			if ($share->getPassword() !== null) {
798
-				$share->setPassword($this->hasher->hash($share->getPassword()));
799
-
800
-				return true;
801
-			}
802
-		}
803
-
804
-		return false;
805
-	}
806
-
807
-	/**
808
-	 * Delete all the children of this share
809
-	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
810
-	 *
811
-	 * @param \OCP\Share\IShare $share
812
-	 * @return \OCP\Share\IShare[] List of deleted shares
813
-	 */
814
-	protected function deleteChildren(\OCP\Share\IShare $share) {
815
-		$deletedShares = [];
816
-
817
-		$provider = $this->factory->getProviderForType($share->getShareType());
818
-
819
-		foreach ($provider->getChildren($share) as $child) {
820
-			$deletedChildren = $this->deleteChildren($child);
821
-			$deletedShares = array_merge($deletedShares, $deletedChildren);
822
-
823
-			$provider->delete($child);
824
-			$deletedShares[] = $child;
825
-		}
826
-
827
-		return $deletedShares;
828
-	}
829
-
830
-	/**
831
-	 * Delete a share
832
-	 *
833
-	 * @param \OCP\Share\IShare $share
834
-	 * @throws ShareNotFound
835
-	 * @throws \InvalidArgumentException
836
-	 */
837
-	public function deleteShare(\OCP\Share\IShare $share) {
838
-
839
-		try {
840
-			$share->getFullId();
841
-		} catch (\UnexpectedValueException $e) {
842
-			throw new \InvalidArgumentException('Share does not have a full id');
843
-		}
844
-
845
-		$event = new GenericEvent($share);
846
-		$this->eventDispatcher->dispatch('OCP\Share::preUnshare', $event);
847
-
848
-		// Get all children and delete them as well
849
-		$deletedShares = $this->deleteChildren($share);
850
-
851
-		// Do the actual delete
852
-		$provider = $this->factory->getProviderForType($share->getShareType());
853
-		$provider->delete($share);
854
-
855
-		// All the deleted shares caused by this delete
856
-		$deletedShares[] = $share;
857
-
858
-		// Emit post hook
859
-		$event->setArgument('deletedShares', $deletedShares);
860
-		$this->eventDispatcher->dispatch('OCP\Share::postUnshare', $event);
861
-	}
862
-
863
-
864
-	/**
865
-	 * Unshare a file as the recipient.
866
-	 * This can be different from a regular delete for example when one of
867
-	 * the users in a groups deletes that share. But the provider should
868
-	 * handle this.
869
-	 *
870
-	 * @param \OCP\Share\IShare $share
871
-	 * @param string $recipientId
872
-	 */
873
-	public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
874
-		list($providerId, ) = $this->splitFullId($share->getFullId());
875
-		$provider = $this->factory->getProvider($providerId);
876
-
877
-		$provider->deleteFromSelf($share, $recipientId);
878
-	}
879
-
880
-	/**
881
-	 * @inheritdoc
882
-	 */
883
-	public function moveShare(\OCP\Share\IShare $share, $recipientId) {
884
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
885
-			throw new \InvalidArgumentException('Can’t change target of link share');
886
-		}
887
-
888
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {
889
-			throw new \InvalidArgumentException('Invalid recipient');
890
-		}
891
-
892
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
893
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
894
-			if (is_null($sharedWith)) {
895
-				throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
896
-			}
897
-			$recipient = $this->userManager->get($recipientId);
898
-			if (!$sharedWith->inGroup($recipient)) {
899
-				throw new \InvalidArgumentException('Invalid recipient');
900
-			}
901
-		}
902
-
903
-		list($providerId, ) = $this->splitFullId($share->getFullId());
904
-		$provider = $this->factory->getProvider($providerId);
905
-
906
-		$provider->move($share, $recipientId);
907
-	}
908
-
909
-	public function getSharesInFolder($userId, Folder $node, $reshares = false) {
910
-		$providers = $this->factory->getAllProviders();
911
-
912
-		return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
913
-			$newShares = $provider->getSharesInFolder($userId, $node, $reshares);
914
-			foreach ($newShares as $fid => $data) {
915
-				if (!isset($shares[$fid])) {
916
-					$shares[$fid] = [];
917
-				}
918
-
919
-				$shares[$fid] = array_merge($shares[$fid], $data);
920
-			}
921
-			return $shares;
922
-		}, []);
923
-	}
924
-
925
-	/**
926
-	 * @inheritdoc
927
-	 */
928
-	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
929
-		if ($path !== null &&
930
-				!($path instanceof \OCP\Files\File) &&
931
-				!($path instanceof \OCP\Files\Folder)) {
932
-			throw new \InvalidArgumentException('invalid path');
933
-		}
934
-
935
-		try {
936
-			$provider = $this->factory->getProviderForType($shareType);
937
-		} catch (ProviderException $e) {
938
-			return [];
939
-		}
940
-
941
-		$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
942
-
943
-		/*
583
+            $share->setToken(
584
+                $this->secureRandom->generate(
585
+                    \OC\Share\Constants::TOKEN_LENGTH,
586
+                    \OCP\Security\ISecureRandom::CHAR_LOWER.
587
+                    \OCP\Security\ISecureRandom::CHAR_UPPER.
588
+                    \OCP\Security\ISecureRandom::CHAR_DIGITS
589
+                )
590
+            );
591
+
592
+            //Verify the expiration date
593
+            $this->validateExpirationDate($share);
594
+
595
+            //Verify the password
596
+            $this->verifyPassword($share->getPassword());
597
+
598
+            // If a password is set. Hash it!
599
+            if ($share->getPassword() !== null) {
600
+                $share->setPassword($this->hasher->hash($share->getPassword()));
601
+            }
602
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
603
+            $share->setToken(
604
+                $this->secureRandom->generate(
605
+                    \OC\Share\Constants::TOKEN_LENGTH,
606
+                    \OCP\Security\ISecureRandom::CHAR_LOWER.
607
+                    \OCP\Security\ISecureRandom::CHAR_UPPER.
608
+                    \OCP\Security\ISecureRandom::CHAR_DIGITS
609
+                )
610
+            );
611
+        }
612
+
613
+        // Cannot share with the owner
614
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
615
+            $share->getSharedWith() === $share->getShareOwner()) {
616
+            throw new \InvalidArgumentException('Can’t share with the share owner');
617
+        }
618
+
619
+        // Generate the target
620
+        $target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
621
+        $target = \OC\Files\Filesystem::normalizePath($target);
622
+        $share->setTarget($target);
623
+
624
+        // Pre share hook
625
+        $run = true;
626
+        $error = '';
627
+        $preHookData = [
628
+            'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
629
+            'itemSource' => $share->getNode()->getId(),
630
+            'shareType' => $share->getShareType(),
631
+            'uidOwner' => $share->getSharedBy(),
632
+            'permissions' => $share->getPermissions(),
633
+            'fileSource' => $share->getNode()->getId(),
634
+            'expiration' => $share->getExpirationDate(),
635
+            'token' => $share->getToken(),
636
+            'itemTarget' => $share->getTarget(),
637
+            'shareWith' => $share->getSharedWith(),
638
+            'run' => &$run,
639
+            'error' => &$error,
640
+        ];
641
+        \OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
642
+
643
+        if ($run === false) {
644
+            throw new \Exception($error);
645
+        }
646
+
647
+        $oldShare = $share;
648
+        $provider = $this->factory->getProviderForType($share->getShareType());
649
+        $share = $provider->create($share);
650
+        //reuse the node we already have
651
+        $share->setNode($oldShare->getNode());
652
+
653
+        // Post share hook
654
+        $postHookData = [
655
+            'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
656
+            'itemSource' => $share->getNode()->getId(),
657
+            'shareType' => $share->getShareType(),
658
+            'uidOwner' => $share->getSharedBy(),
659
+            'permissions' => $share->getPermissions(),
660
+            'fileSource' => $share->getNode()->getId(),
661
+            'expiration' => $share->getExpirationDate(),
662
+            'token' => $share->getToken(),
663
+            'id' => $share->getId(),
664
+            'shareWith' => $share->getSharedWith(),
665
+            'itemTarget' => $share->getTarget(),
666
+            'fileTarget' => $share->getTarget(),
667
+        ];
668
+
669
+        \OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
670
+
671
+        return $share;
672
+    }
673
+
674
+    /**
675
+     * Update a share
676
+     *
677
+     * @param \OCP\Share\IShare $share
678
+     * @return \OCP\Share\IShare The share object
679
+     * @throws \InvalidArgumentException
680
+     */
681
+    public function updateShare(\OCP\Share\IShare $share) {
682
+        $expirationDateUpdated = false;
683
+
684
+        $this->canShare($share);
685
+
686
+        try {
687
+            $originalShare = $this->getShareById($share->getFullId());
688
+        } catch (\UnexpectedValueException $e) {
689
+            throw new \InvalidArgumentException('Share does not have a full id');
690
+        }
691
+
692
+        // We can't change the share type!
693
+        if ($share->getShareType() !== $originalShare->getShareType()) {
694
+            throw new \InvalidArgumentException('Can’t change share type');
695
+        }
696
+
697
+        // We can only change the recipient on user shares
698
+        if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
699
+            $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
700
+            throw new \InvalidArgumentException('Can only update recipient on user shares');
701
+        }
702
+
703
+        // Cannot share with the owner
704
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
705
+            $share->getSharedWith() === $share->getShareOwner()) {
706
+            throw new \InvalidArgumentException('Can’t share with the share owner');
707
+        }
708
+
709
+        $this->generalCreateChecks($share);
710
+
711
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
712
+            $this->userCreateChecks($share);
713
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
714
+            $this->groupCreateChecks($share);
715
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
716
+            $this->linkCreateChecks($share);
717
+
718
+            $this->updateSharePasswordIfNeeded($share, $originalShare);
719
+
720
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
721
+                //Verify the expiration date
722
+                $this->validateExpirationDate($share);
723
+                $expirationDateUpdated = true;
724
+            }
725
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
726
+            $plainTextPassword = $share->getPassword();
727
+            if (!$this->updateSharePasswordIfNeeded($share, $originalShare)) {
728
+                $plainTextPassword = null;
729
+            }
730
+        }
731
+
732
+        $this->pathCreateChecks($share->getNode());
733
+
734
+        // Now update the share!
735
+        $provider = $this->factory->getProviderForType($share->getShareType());
736
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
737
+            $share = $provider->update($share, $plainTextPassword);
738
+        } else {
739
+            $share = $provider->update($share);
740
+        }
741
+
742
+        if ($expirationDateUpdated === true) {
743
+            \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [
744
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
745
+                'itemSource' => $share->getNode()->getId(),
746
+                'date' => $share->getExpirationDate(),
747
+                'uidOwner' => $share->getSharedBy(),
748
+            ]);
749
+        }
750
+
751
+        if ($share->getPassword() !== $originalShare->getPassword()) {
752
+            \OC_Hook::emit('OCP\Share', 'post_update_password', [
753
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
754
+                'itemSource' => $share->getNode()->getId(),
755
+                'uidOwner' => $share->getSharedBy(),
756
+                'token' => $share->getToken(),
757
+                'disabled' => is_null($share->getPassword()),
758
+            ]);
759
+        }
760
+
761
+        if ($share->getPermissions() !== $originalShare->getPermissions()) {
762
+            if ($this->userManager->userExists($share->getShareOwner())) {
763
+                $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
764
+            } else {
765
+                $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
766
+            }
767
+            \OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
768
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
769
+                'itemSource' => $share->getNode()->getId(),
770
+                'shareType' => $share->getShareType(),
771
+                'shareWith' => $share->getSharedWith(),
772
+                'uidOwner' => $share->getSharedBy(),
773
+                'permissions' => $share->getPermissions(),
774
+                'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
775
+            ));
776
+        }
777
+
778
+        return $share;
779
+    }
780
+
781
+    /**
782
+     * Updates the password of the given share if it is not the same as the
783
+     * password of the original share.
784
+     *
785
+     * @param \OCP\Share\IShare $share the share to update its password.
786
+     * @param \OCP\Share\IShare $originalShare the original share to compare its
787
+     *        password with.
788
+     * @return boolean whether the password was updated or not.
789
+     */
790
+    private function updateSharePasswordIfNeeded(\OCP\Share\IShare $share, \OCP\Share\IShare $originalShare) {
791
+        // Password updated.
792
+        if ($share->getPassword() !== $originalShare->getPassword()) {
793
+            //Verify the password
794
+            $this->verifyPassword($share->getPassword());
795
+
796
+            // If a password is set. Hash it!
797
+            if ($share->getPassword() !== null) {
798
+                $share->setPassword($this->hasher->hash($share->getPassword()));
799
+
800
+                return true;
801
+            }
802
+        }
803
+
804
+        return false;
805
+    }
806
+
807
+    /**
808
+     * Delete all the children of this share
809
+     * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
810
+     *
811
+     * @param \OCP\Share\IShare $share
812
+     * @return \OCP\Share\IShare[] List of deleted shares
813
+     */
814
+    protected function deleteChildren(\OCP\Share\IShare $share) {
815
+        $deletedShares = [];
816
+
817
+        $provider = $this->factory->getProviderForType($share->getShareType());
818
+
819
+        foreach ($provider->getChildren($share) as $child) {
820
+            $deletedChildren = $this->deleteChildren($child);
821
+            $deletedShares = array_merge($deletedShares, $deletedChildren);
822
+
823
+            $provider->delete($child);
824
+            $deletedShares[] = $child;
825
+        }
826
+
827
+        return $deletedShares;
828
+    }
829
+
830
+    /**
831
+     * Delete a share
832
+     *
833
+     * @param \OCP\Share\IShare $share
834
+     * @throws ShareNotFound
835
+     * @throws \InvalidArgumentException
836
+     */
837
+    public function deleteShare(\OCP\Share\IShare $share) {
838
+
839
+        try {
840
+            $share->getFullId();
841
+        } catch (\UnexpectedValueException $e) {
842
+            throw new \InvalidArgumentException('Share does not have a full id');
843
+        }
844
+
845
+        $event = new GenericEvent($share);
846
+        $this->eventDispatcher->dispatch('OCP\Share::preUnshare', $event);
847
+
848
+        // Get all children and delete them as well
849
+        $deletedShares = $this->deleteChildren($share);
850
+
851
+        // Do the actual delete
852
+        $provider = $this->factory->getProviderForType($share->getShareType());
853
+        $provider->delete($share);
854
+
855
+        // All the deleted shares caused by this delete
856
+        $deletedShares[] = $share;
857
+
858
+        // Emit post hook
859
+        $event->setArgument('deletedShares', $deletedShares);
860
+        $this->eventDispatcher->dispatch('OCP\Share::postUnshare', $event);
861
+    }
862
+
863
+
864
+    /**
865
+     * Unshare a file as the recipient.
866
+     * This can be different from a regular delete for example when one of
867
+     * the users in a groups deletes that share. But the provider should
868
+     * handle this.
869
+     *
870
+     * @param \OCP\Share\IShare $share
871
+     * @param string $recipientId
872
+     */
873
+    public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
874
+        list($providerId, ) = $this->splitFullId($share->getFullId());
875
+        $provider = $this->factory->getProvider($providerId);
876
+
877
+        $provider->deleteFromSelf($share, $recipientId);
878
+    }
879
+
880
+    /**
881
+     * @inheritdoc
882
+     */
883
+    public function moveShare(\OCP\Share\IShare $share, $recipientId) {
884
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
885
+            throw new \InvalidArgumentException('Can’t change target of link share');
886
+        }
887
+
888
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {
889
+            throw new \InvalidArgumentException('Invalid recipient');
890
+        }
891
+
892
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
893
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
894
+            if (is_null($sharedWith)) {
895
+                throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
896
+            }
897
+            $recipient = $this->userManager->get($recipientId);
898
+            if (!$sharedWith->inGroup($recipient)) {
899
+                throw new \InvalidArgumentException('Invalid recipient');
900
+            }
901
+        }
902
+
903
+        list($providerId, ) = $this->splitFullId($share->getFullId());
904
+        $provider = $this->factory->getProvider($providerId);
905
+
906
+        $provider->move($share, $recipientId);
907
+    }
908
+
909
+    public function getSharesInFolder($userId, Folder $node, $reshares = false) {
910
+        $providers = $this->factory->getAllProviders();
911
+
912
+        return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
913
+            $newShares = $provider->getSharesInFolder($userId, $node, $reshares);
914
+            foreach ($newShares as $fid => $data) {
915
+                if (!isset($shares[$fid])) {
916
+                    $shares[$fid] = [];
917
+                }
918
+
919
+                $shares[$fid] = array_merge($shares[$fid], $data);
920
+            }
921
+            return $shares;
922
+        }, []);
923
+    }
924
+
925
+    /**
926
+     * @inheritdoc
927
+     */
928
+    public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
929
+        if ($path !== null &&
930
+                !($path instanceof \OCP\Files\File) &&
931
+                !($path instanceof \OCP\Files\Folder)) {
932
+            throw new \InvalidArgumentException('invalid path');
933
+        }
934
+
935
+        try {
936
+            $provider = $this->factory->getProviderForType($shareType);
937
+        } catch (ProviderException $e) {
938
+            return [];
939
+        }
940
+
941
+        $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
942
+
943
+        /*
944 944
 		 * Work around so we don't return expired shares but still follow
945 945
 		 * proper pagination.
946 946
 		 */
947 947
 
948
-		$shares2 = [];
949
-
950
-		while(true) {
951
-			$added = 0;
952
-			foreach ($shares as $share) {
953
-
954
-				try {
955
-					$this->checkExpireDate($share);
956
-				} catch (ShareNotFound $e) {
957
-					//Ignore since this basically means the share is deleted
958
-					continue;
959
-				}
960
-
961
-				$added++;
962
-				$shares2[] = $share;
963
-
964
-				if (count($shares2) === $limit) {
965
-					break;
966
-				}
967
-			}
968
-
969
-			if (count($shares2) === $limit) {
970
-				break;
971
-			}
972
-
973
-			// If there was no limit on the select we are done
974
-			if ($limit === -1) {
975
-				break;
976
-			}
977
-
978
-			$offset += $added;
979
-
980
-			// Fetch again $limit shares
981
-			$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
982
-
983
-			// No more shares means we are done
984
-			if (empty($shares)) {
985
-				break;
986
-			}
987
-		}
988
-
989
-		$shares = $shares2;
990
-
991
-		return $shares;
992
-	}
993
-
994
-	/**
995
-	 * @inheritdoc
996
-	 */
997
-	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
998
-		try {
999
-			$provider = $this->factory->getProviderForType($shareType);
1000
-		} catch (ProviderException $e) {
1001
-			return [];
1002
-		}
1003
-
1004
-		$shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1005
-
1006
-		// remove all shares which are already expired
1007
-		foreach ($shares as $key => $share) {
1008
-			try {
1009
-				$this->checkExpireDate($share);
1010
-			} catch (ShareNotFound $e) {
1011
-				unset($shares[$key]);
1012
-			}
1013
-		}
1014
-
1015
-		return $shares;
1016
-	}
1017
-
1018
-	/**
1019
-	 * @inheritdoc
1020
-	 */
1021
-	public function getShareById($id, $recipient = null) {
1022
-		if ($id === null) {
1023
-			throw new ShareNotFound();
1024
-		}
1025
-
1026
-		list($providerId, $id) = $this->splitFullId($id);
1027
-
1028
-		try {
1029
-			$provider = $this->factory->getProvider($providerId);
1030
-		} catch (ProviderException $e) {
1031
-			throw new ShareNotFound();
1032
-		}
1033
-
1034
-		$share = $provider->getShareById($id, $recipient);
1035
-
1036
-		$this->checkExpireDate($share);
1037
-
1038
-		return $share;
1039
-	}
1040
-
1041
-	/**
1042
-	 * Get all the shares for a given path
1043
-	 *
1044
-	 * @param \OCP\Files\Node $path
1045
-	 * @param int $page
1046
-	 * @param int $perPage
1047
-	 *
1048
-	 * @return Share[]
1049
-	 */
1050
-	public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) {
1051
-		return [];
1052
-	}
1053
-
1054
-	/**
1055
-	 * Get the share by token possible with password
1056
-	 *
1057
-	 * @param string $token
1058
-	 * @return Share
1059
-	 *
1060
-	 * @throws ShareNotFound
1061
-	 */
1062
-	public function getShareByToken($token) {
1063
-		$share = null;
1064
-		try {
1065
-			if($this->shareApiAllowLinks()) {
1066
-				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
1067
-				$share = $provider->getShareByToken($token);
1068
-			}
1069
-		} catch (ProviderException $e) {
1070
-		} catch (ShareNotFound $e) {
1071
-		}
1072
-
1073
-
1074
-		// If it is not a link share try to fetch a federated share by token
1075
-		if ($share === null) {
1076
-			try {
1077
-				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE);
1078
-				$share = $provider->getShareByToken($token);
1079
-			} catch (ProviderException $e) {
1080
-			} catch (ShareNotFound $e) {
1081
-			}
1082
-		}
1083
-
1084
-		// If it is not a link share try to fetch a mail share by token
1085
-		if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
1086
-			try {
1087
-				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL);
1088
-				$share = $provider->getShareByToken($token);
1089
-			} catch (ProviderException $e) {
1090
-			} catch (ShareNotFound $e) {
1091
-			}
1092
-		}
1093
-
1094
-		if ($share === null) {
1095
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1096
-		}
1097
-
1098
-		$this->checkExpireDate($share);
1099
-
1100
-		/*
948
+        $shares2 = [];
949
+
950
+        while(true) {
951
+            $added = 0;
952
+            foreach ($shares as $share) {
953
+
954
+                try {
955
+                    $this->checkExpireDate($share);
956
+                } catch (ShareNotFound $e) {
957
+                    //Ignore since this basically means the share is deleted
958
+                    continue;
959
+                }
960
+
961
+                $added++;
962
+                $shares2[] = $share;
963
+
964
+                if (count($shares2) === $limit) {
965
+                    break;
966
+                }
967
+            }
968
+
969
+            if (count($shares2) === $limit) {
970
+                break;
971
+            }
972
+
973
+            // If there was no limit on the select we are done
974
+            if ($limit === -1) {
975
+                break;
976
+            }
977
+
978
+            $offset += $added;
979
+
980
+            // Fetch again $limit shares
981
+            $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
982
+
983
+            // No more shares means we are done
984
+            if (empty($shares)) {
985
+                break;
986
+            }
987
+        }
988
+
989
+        $shares = $shares2;
990
+
991
+        return $shares;
992
+    }
993
+
994
+    /**
995
+     * @inheritdoc
996
+     */
997
+    public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
998
+        try {
999
+            $provider = $this->factory->getProviderForType($shareType);
1000
+        } catch (ProviderException $e) {
1001
+            return [];
1002
+        }
1003
+
1004
+        $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1005
+
1006
+        // remove all shares which are already expired
1007
+        foreach ($shares as $key => $share) {
1008
+            try {
1009
+                $this->checkExpireDate($share);
1010
+            } catch (ShareNotFound $e) {
1011
+                unset($shares[$key]);
1012
+            }
1013
+        }
1014
+
1015
+        return $shares;
1016
+    }
1017
+
1018
+    /**
1019
+     * @inheritdoc
1020
+     */
1021
+    public function getShareById($id, $recipient = null) {
1022
+        if ($id === null) {
1023
+            throw new ShareNotFound();
1024
+        }
1025
+
1026
+        list($providerId, $id) = $this->splitFullId($id);
1027
+
1028
+        try {
1029
+            $provider = $this->factory->getProvider($providerId);
1030
+        } catch (ProviderException $e) {
1031
+            throw new ShareNotFound();
1032
+        }
1033
+
1034
+        $share = $provider->getShareById($id, $recipient);
1035
+
1036
+        $this->checkExpireDate($share);
1037
+
1038
+        return $share;
1039
+    }
1040
+
1041
+    /**
1042
+     * Get all the shares for a given path
1043
+     *
1044
+     * @param \OCP\Files\Node $path
1045
+     * @param int $page
1046
+     * @param int $perPage
1047
+     *
1048
+     * @return Share[]
1049
+     */
1050
+    public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) {
1051
+        return [];
1052
+    }
1053
+
1054
+    /**
1055
+     * Get the share by token possible with password
1056
+     *
1057
+     * @param string $token
1058
+     * @return Share
1059
+     *
1060
+     * @throws ShareNotFound
1061
+     */
1062
+    public function getShareByToken($token) {
1063
+        $share = null;
1064
+        try {
1065
+            if($this->shareApiAllowLinks()) {
1066
+                $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
1067
+                $share = $provider->getShareByToken($token);
1068
+            }
1069
+        } catch (ProviderException $e) {
1070
+        } catch (ShareNotFound $e) {
1071
+        }
1072
+
1073
+
1074
+        // If it is not a link share try to fetch a federated share by token
1075
+        if ($share === null) {
1076
+            try {
1077
+                $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE);
1078
+                $share = $provider->getShareByToken($token);
1079
+            } catch (ProviderException $e) {
1080
+            } catch (ShareNotFound $e) {
1081
+            }
1082
+        }
1083
+
1084
+        // If it is not a link share try to fetch a mail share by token
1085
+        if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
1086
+            try {
1087
+                $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL);
1088
+                $share = $provider->getShareByToken($token);
1089
+            } catch (ProviderException $e) {
1090
+            } catch (ShareNotFound $e) {
1091
+            }
1092
+        }
1093
+
1094
+        if ($share === null) {
1095
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1096
+        }
1097
+
1098
+        $this->checkExpireDate($share);
1099
+
1100
+        /*
1101 1101
 		 * Reduce the permissions for link shares if public upload is not enabled
1102 1102
 		 */
1103
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
1104
-			!$this->shareApiLinkAllowPublicUpload()) {
1105
-			$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1106
-		}
1107
-
1108
-		return $share;
1109
-	}
1110
-
1111
-	protected function checkExpireDate($share) {
1112
-		if ($share->getExpirationDate() !== null &&
1113
-			$share->getExpirationDate() <= new \DateTime()) {
1114
-			$this->deleteShare($share);
1115
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1116
-		}
1117
-
1118
-	}
1119
-
1120
-	/**
1121
-	 * Verify the password of a public share
1122
-	 *
1123
-	 * @param \OCP\Share\IShare $share
1124
-	 * @param string $password
1125
-	 * @return bool
1126
-	 */
1127
-	public function checkPassword(\OCP\Share\IShare $share, $password) {
1128
-		$passwordProtected = $share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK
1129
-			|| $share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL;
1130
-		if (!$passwordProtected) {
1131
-			//TODO maybe exception?
1132
-			return false;
1133
-		}
1134
-
1135
-		if ($password === null || $share->getPassword() === null) {
1136
-			return false;
1137
-		}
1138
-
1139
-		$newHash = '';
1140
-		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1141
-			return false;
1142
-		}
1143
-
1144
-		if (!empty($newHash)) {
1145
-			$share->setPassword($newHash);
1146
-			$provider = $this->factory->getProviderForType($share->getShareType());
1147
-			$provider->update($share);
1148
-		}
1149
-
1150
-		return true;
1151
-	}
1152
-
1153
-	/**
1154
-	 * @inheritdoc
1155
-	 */
1156
-	public function userDeleted($uid) {
1157
-		$types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL];
1158
-
1159
-		foreach ($types as $type) {
1160
-			try {
1161
-				$provider = $this->factory->getProviderForType($type);
1162
-			} catch (ProviderException $e) {
1163
-				continue;
1164
-			}
1165
-			$provider->userDeleted($uid, $type);
1166
-		}
1167
-	}
1168
-
1169
-	/**
1170
-	 * @inheritdoc
1171
-	 */
1172
-	public function groupDeleted($gid) {
1173
-		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1174
-		$provider->groupDeleted($gid);
1175
-	}
1176
-
1177
-	/**
1178
-	 * @inheritdoc
1179
-	 */
1180
-	public function userDeletedFromGroup($uid, $gid) {
1181
-		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1182
-		$provider->userDeletedFromGroup($uid, $gid);
1183
-	}
1184
-
1185
-	/**
1186
-	 * Get access list to a path. This means
1187
-	 * all the users that can access a given path.
1188
-	 *
1189
-	 * Consider:
1190
-	 * -root
1191
-	 * |-folder1 (23)
1192
-	 *  |-folder2 (32)
1193
-	 *   |-fileA (42)
1194
-	 *
1195
-	 * fileA is shared with user1 and user1@server1
1196
-	 * folder2 is shared with group2 (user4 is a member of group2)
1197
-	 * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1198
-	 *
1199
-	 * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1200
-	 * [
1201
-	 *  users  => [
1202
-	 *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1203
-	 *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1204
-	 *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1205
-	 *  ],
1206
-	 *  remote => [
1207
-	 *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1208
-	 *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1209
-	 *  ],
1210
-	 *  public => bool
1211
-	 *  mail => bool
1212
-	 * ]
1213
-	 *
1214
-	 * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1215
-	 * [
1216
-	 *  users  => ['user1', 'user2', 'user4'],
1217
-	 *  remote => bool,
1218
-	 *  public => bool
1219
-	 *  mail => bool
1220
-	 * ]
1221
-	 *
1222
-	 * This is required for encryption/activity
1223
-	 *
1224
-	 * @param \OCP\Files\Node $path
1225
-	 * @param bool $recursive Should we check all parent folders as well
1226
-	 * @param bool $currentAccess Should the user have currently access to the file
1227
-	 * @return array
1228
-	 */
1229
-	public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1230
-		$owner = $path->getOwner()->getUID();
1231
-
1232
-		if ($currentAccess) {
1233
-			$al = ['users' => [], 'remote' => [], 'public' => false];
1234
-		} else {
1235
-			$al = ['users' => [], 'remote' => false, 'public' => false];
1236
-		}
1237
-		if (!$this->userManager->userExists($owner)) {
1238
-			return $al;
1239
-		}
1240
-
1241
-		//Get node for the owner
1242
-		$userFolder = $this->rootFolder->getUserFolder($owner);
1243
-		if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1244
-			$path = $userFolder->getById($path->getId())[0];
1245
-		}
1246
-
1247
-		$providers = $this->factory->getAllProviders();
1248
-
1249
-		/** @var Node[] $nodes */
1250
-		$nodes = [];
1251
-
1252
-
1253
-		if ($currentAccess) {
1254
-			$ownerPath = $path->getPath();
1255
-			$ownerPath = explode('/', $ownerPath, 4);
1256
-			if (count($ownerPath) < 4) {
1257
-				$ownerPath = '';
1258
-			} else {
1259
-				$ownerPath = $ownerPath[3];
1260
-			}
1261
-			$al['users'][$owner] = [
1262
-				'node_id' => $path->getId(),
1263
-				'node_path' => '/' . $ownerPath,
1264
-			];
1265
-		} else {
1266
-			$al['users'][] = $owner;
1267
-		}
1268
-
1269
-		// Collect all the shares
1270
-		while ($path->getPath() !== $userFolder->getPath()) {
1271
-			$nodes[] = $path;
1272
-			if (!$recursive) {
1273
-				break;
1274
-			}
1275
-			$path = $path->getParent();
1276
-		}
1277
-
1278
-		foreach ($providers as $provider) {
1279
-			$tmp = $provider->getAccessList($nodes, $currentAccess);
1280
-
1281
-			foreach ($tmp as $k => $v) {
1282
-				if (isset($al[$k])) {
1283
-					if (is_array($al[$k])) {
1284
-						$al[$k] = array_merge($al[$k], $v);
1285
-					} else {
1286
-						$al[$k] = $al[$k] || $v;
1287
-					}
1288
-				} else {
1289
-					$al[$k] = $v;
1290
-				}
1291
-			}
1292
-		}
1293
-
1294
-		return $al;
1295
-	}
1296
-
1297
-	/**
1298
-	 * Create a new share
1299
-	 * @return \OCP\Share\IShare;
1300
-	 */
1301
-	public function newShare() {
1302
-		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1303
-	}
1304
-
1305
-	/**
1306
-	 * Is the share API enabled
1307
-	 *
1308
-	 * @return bool
1309
-	 */
1310
-	public function shareApiEnabled() {
1311
-		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1312
-	}
1313
-
1314
-	/**
1315
-	 * Is public link sharing enabled
1316
-	 *
1317
-	 * @return bool
1318
-	 */
1319
-	public function shareApiAllowLinks() {
1320
-		return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1321
-	}
1322
-
1323
-	/**
1324
-	 * Is password on public link requires
1325
-	 *
1326
-	 * @return bool
1327
-	 */
1328
-	public function shareApiLinkEnforcePassword() {
1329
-		return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1330
-	}
1331
-
1332
-	/**
1333
-	 * Is default expire date enabled
1334
-	 *
1335
-	 * @return bool
1336
-	 */
1337
-	public function shareApiLinkDefaultExpireDate() {
1338
-		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1339
-	}
1340
-
1341
-	/**
1342
-	 * Is default expire date enforced
1343
-	 *`
1344
-	 * @return bool
1345
-	 */
1346
-	public function shareApiLinkDefaultExpireDateEnforced() {
1347
-		return $this->shareApiLinkDefaultExpireDate() &&
1348
-			$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1349
-	}
1350
-
1351
-	/**
1352
-	 * Number of default expire days
1353
-	 *shareApiLinkAllowPublicUpload
1354
-	 * @return int
1355
-	 */
1356
-	public function shareApiLinkDefaultExpireDays() {
1357
-		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1358
-	}
1359
-
1360
-	/**
1361
-	 * Allow public upload on link shares
1362
-	 *
1363
-	 * @return bool
1364
-	 */
1365
-	public function shareApiLinkAllowPublicUpload() {
1366
-		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1367
-	}
1368
-
1369
-	/**
1370
-	 * check if user can only share with group members
1371
-	 * @return bool
1372
-	 */
1373
-	public function shareWithGroupMembersOnly() {
1374
-		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1375
-	}
1376
-
1377
-	/**
1378
-	 * Check if users can share with groups
1379
-	 * @return bool
1380
-	 */
1381
-	public function allowGroupSharing() {
1382
-		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1383
-	}
1384
-
1385
-	/**
1386
-	 * Copied from \OC_Util::isSharingDisabledForUser
1387
-	 *
1388
-	 * TODO: Deprecate fuction from OC_Util
1389
-	 *
1390
-	 * @param string $userId
1391
-	 * @return bool
1392
-	 */
1393
-	public function sharingDisabledForUser($userId) {
1394
-		if ($userId === null) {
1395
-			return false;
1396
-		}
1397
-
1398
-		if (isset($this->sharingDisabledForUsersCache[$userId])) {
1399
-			return $this->sharingDisabledForUsersCache[$userId];
1400
-		}
1401
-
1402
-		if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1403
-			$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1404
-			$excludedGroups = json_decode($groupsList);
1405
-			if (is_null($excludedGroups)) {
1406
-				$excludedGroups = explode(',', $groupsList);
1407
-				$newValue = json_encode($excludedGroups);
1408
-				$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1409
-			}
1410
-			$user = $this->userManager->get($userId);
1411
-			$usersGroups = $this->groupManager->getUserGroupIds($user);
1412
-			if (!empty($usersGroups)) {
1413
-				$remainingGroups = array_diff($usersGroups, $excludedGroups);
1414
-				// if the user is only in groups which are disabled for sharing then
1415
-				// sharing is also disabled for the user
1416
-				if (empty($remainingGroups)) {
1417
-					$this->sharingDisabledForUsersCache[$userId] = true;
1418
-					return true;
1419
-				}
1420
-			}
1421
-		}
1422
-
1423
-		$this->sharingDisabledForUsersCache[$userId] = false;
1424
-		return false;
1425
-	}
1426
-
1427
-	/**
1428
-	 * @inheritdoc
1429
-	 */
1430
-	public function outgoingServer2ServerSharesAllowed() {
1431
-		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1432
-	}
1433
-
1434
-	/**
1435
-	 * @inheritdoc
1436
-	 */
1437
-	public function shareProviderExists($shareType) {
1438
-		try {
1439
-			$this->factory->getProviderForType($shareType);
1440
-		} catch (ProviderException $e) {
1441
-			return false;
1442
-		}
1443
-
1444
-		return true;
1445
-	}
1103
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
1104
+            !$this->shareApiLinkAllowPublicUpload()) {
1105
+            $share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1106
+        }
1107
+
1108
+        return $share;
1109
+    }
1110
+
1111
+    protected function checkExpireDate($share) {
1112
+        if ($share->getExpirationDate() !== null &&
1113
+            $share->getExpirationDate() <= new \DateTime()) {
1114
+            $this->deleteShare($share);
1115
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1116
+        }
1117
+
1118
+    }
1119
+
1120
+    /**
1121
+     * Verify the password of a public share
1122
+     *
1123
+     * @param \OCP\Share\IShare $share
1124
+     * @param string $password
1125
+     * @return bool
1126
+     */
1127
+    public function checkPassword(\OCP\Share\IShare $share, $password) {
1128
+        $passwordProtected = $share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK
1129
+            || $share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL;
1130
+        if (!$passwordProtected) {
1131
+            //TODO maybe exception?
1132
+            return false;
1133
+        }
1134
+
1135
+        if ($password === null || $share->getPassword() === null) {
1136
+            return false;
1137
+        }
1138
+
1139
+        $newHash = '';
1140
+        if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1141
+            return false;
1142
+        }
1143
+
1144
+        if (!empty($newHash)) {
1145
+            $share->setPassword($newHash);
1146
+            $provider = $this->factory->getProviderForType($share->getShareType());
1147
+            $provider->update($share);
1148
+        }
1149
+
1150
+        return true;
1151
+    }
1152
+
1153
+    /**
1154
+     * @inheritdoc
1155
+     */
1156
+    public function userDeleted($uid) {
1157
+        $types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL];
1158
+
1159
+        foreach ($types as $type) {
1160
+            try {
1161
+                $provider = $this->factory->getProviderForType($type);
1162
+            } catch (ProviderException $e) {
1163
+                continue;
1164
+            }
1165
+            $provider->userDeleted($uid, $type);
1166
+        }
1167
+    }
1168
+
1169
+    /**
1170
+     * @inheritdoc
1171
+     */
1172
+    public function groupDeleted($gid) {
1173
+        $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1174
+        $provider->groupDeleted($gid);
1175
+    }
1176
+
1177
+    /**
1178
+     * @inheritdoc
1179
+     */
1180
+    public function userDeletedFromGroup($uid, $gid) {
1181
+        $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1182
+        $provider->userDeletedFromGroup($uid, $gid);
1183
+    }
1184
+
1185
+    /**
1186
+     * Get access list to a path. This means
1187
+     * all the users that can access a given path.
1188
+     *
1189
+     * Consider:
1190
+     * -root
1191
+     * |-folder1 (23)
1192
+     *  |-folder2 (32)
1193
+     *   |-fileA (42)
1194
+     *
1195
+     * fileA is shared with user1 and user1@server1
1196
+     * folder2 is shared with group2 (user4 is a member of group2)
1197
+     * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1198
+     *
1199
+     * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1200
+     * [
1201
+     *  users  => [
1202
+     *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1203
+     *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1204
+     *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1205
+     *  ],
1206
+     *  remote => [
1207
+     *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1208
+     *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1209
+     *  ],
1210
+     *  public => bool
1211
+     *  mail => bool
1212
+     * ]
1213
+     *
1214
+     * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1215
+     * [
1216
+     *  users  => ['user1', 'user2', 'user4'],
1217
+     *  remote => bool,
1218
+     *  public => bool
1219
+     *  mail => bool
1220
+     * ]
1221
+     *
1222
+     * This is required for encryption/activity
1223
+     *
1224
+     * @param \OCP\Files\Node $path
1225
+     * @param bool $recursive Should we check all parent folders as well
1226
+     * @param bool $currentAccess Should the user have currently access to the file
1227
+     * @return array
1228
+     */
1229
+    public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1230
+        $owner = $path->getOwner()->getUID();
1231
+
1232
+        if ($currentAccess) {
1233
+            $al = ['users' => [], 'remote' => [], 'public' => false];
1234
+        } else {
1235
+            $al = ['users' => [], 'remote' => false, 'public' => false];
1236
+        }
1237
+        if (!$this->userManager->userExists($owner)) {
1238
+            return $al;
1239
+        }
1240
+
1241
+        //Get node for the owner
1242
+        $userFolder = $this->rootFolder->getUserFolder($owner);
1243
+        if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1244
+            $path = $userFolder->getById($path->getId())[0];
1245
+        }
1246
+
1247
+        $providers = $this->factory->getAllProviders();
1248
+
1249
+        /** @var Node[] $nodes */
1250
+        $nodes = [];
1251
+
1252
+
1253
+        if ($currentAccess) {
1254
+            $ownerPath = $path->getPath();
1255
+            $ownerPath = explode('/', $ownerPath, 4);
1256
+            if (count($ownerPath) < 4) {
1257
+                $ownerPath = '';
1258
+            } else {
1259
+                $ownerPath = $ownerPath[3];
1260
+            }
1261
+            $al['users'][$owner] = [
1262
+                'node_id' => $path->getId(),
1263
+                'node_path' => '/' . $ownerPath,
1264
+            ];
1265
+        } else {
1266
+            $al['users'][] = $owner;
1267
+        }
1268
+
1269
+        // Collect all the shares
1270
+        while ($path->getPath() !== $userFolder->getPath()) {
1271
+            $nodes[] = $path;
1272
+            if (!$recursive) {
1273
+                break;
1274
+            }
1275
+            $path = $path->getParent();
1276
+        }
1277
+
1278
+        foreach ($providers as $provider) {
1279
+            $tmp = $provider->getAccessList($nodes, $currentAccess);
1280
+
1281
+            foreach ($tmp as $k => $v) {
1282
+                if (isset($al[$k])) {
1283
+                    if (is_array($al[$k])) {
1284
+                        $al[$k] = array_merge($al[$k], $v);
1285
+                    } else {
1286
+                        $al[$k] = $al[$k] || $v;
1287
+                    }
1288
+                } else {
1289
+                    $al[$k] = $v;
1290
+                }
1291
+            }
1292
+        }
1293
+
1294
+        return $al;
1295
+    }
1296
+
1297
+    /**
1298
+     * Create a new share
1299
+     * @return \OCP\Share\IShare;
1300
+     */
1301
+    public function newShare() {
1302
+        return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1303
+    }
1304
+
1305
+    /**
1306
+     * Is the share API enabled
1307
+     *
1308
+     * @return bool
1309
+     */
1310
+    public function shareApiEnabled() {
1311
+        return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1312
+    }
1313
+
1314
+    /**
1315
+     * Is public link sharing enabled
1316
+     *
1317
+     * @return bool
1318
+     */
1319
+    public function shareApiAllowLinks() {
1320
+        return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1321
+    }
1322
+
1323
+    /**
1324
+     * Is password on public link requires
1325
+     *
1326
+     * @return bool
1327
+     */
1328
+    public function shareApiLinkEnforcePassword() {
1329
+        return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1330
+    }
1331
+
1332
+    /**
1333
+     * Is default expire date enabled
1334
+     *
1335
+     * @return bool
1336
+     */
1337
+    public function shareApiLinkDefaultExpireDate() {
1338
+        return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1339
+    }
1340
+
1341
+    /**
1342
+     * Is default expire date enforced
1343
+     *`
1344
+     * @return bool
1345
+     */
1346
+    public function shareApiLinkDefaultExpireDateEnforced() {
1347
+        return $this->shareApiLinkDefaultExpireDate() &&
1348
+            $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1349
+    }
1350
+
1351
+    /**
1352
+     * Number of default expire days
1353
+     *shareApiLinkAllowPublicUpload
1354
+     * @return int
1355
+     */
1356
+    public function shareApiLinkDefaultExpireDays() {
1357
+        return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1358
+    }
1359
+
1360
+    /**
1361
+     * Allow public upload on link shares
1362
+     *
1363
+     * @return bool
1364
+     */
1365
+    public function shareApiLinkAllowPublicUpload() {
1366
+        return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1367
+    }
1368
+
1369
+    /**
1370
+     * check if user can only share with group members
1371
+     * @return bool
1372
+     */
1373
+    public function shareWithGroupMembersOnly() {
1374
+        return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1375
+    }
1376
+
1377
+    /**
1378
+     * Check if users can share with groups
1379
+     * @return bool
1380
+     */
1381
+    public function allowGroupSharing() {
1382
+        return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1383
+    }
1384
+
1385
+    /**
1386
+     * Copied from \OC_Util::isSharingDisabledForUser
1387
+     *
1388
+     * TODO: Deprecate fuction from OC_Util
1389
+     *
1390
+     * @param string $userId
1391
+     * @return bool
1392
+     */
1393
+    public function sharingDisabledForUser($userId) {
1394
+        if ($userId === null) {
1395
+            return false;
1396
+        }
1397
+
1398
+        if (isset($this->sharingDisabledForUsersCache[$userId])) {
1399
+            return $this->sharingDisabledForUsersCache[$userId];
1400
+        }
1401
+
1402
+        if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1403
+            $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1404
+            $excludedGroups = json_decode($groupsList);
1405
+            if (is_null($excludedGroups)) {
1406
+                $excludedGroups = explode(',', $groupsList);
1407
+                $newValue = json_encode($excludedGroups);
1408
+                $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1409
+            }
1410
+            $user = $this->userManager->get($userId);
1411
+            $usersGroups = $this->groupManager->getUserGroupIds($user);
1412
+            if (!empty($usersGroups)) {
1413
+                $remainingGroups = array_diff($usersGroups, $excludedGroups);
1414
+                // if the user is only in groups which are disabled for sharing then
1415
+                // sharing is also disabled for the user
1416
+                if (empty($remainingGroups)) {
1417
+                    $this->sharingDisabledForUsersCache[$userId] = true;
1418
+                    return true;
1419
+                }
1420
+            }
1421
+        }
1422
+
1423
+        $this->sharingDisabledForUsersCache[$userId] = false;
1424
+        return false;
1425
+    }
1426
+
1427
+    /**
1428
+     * @inheritdoc
1429
+     */
1430
+    public function outgoingServer2ServerSharesAllowed() {
1431
+        return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1432
+    }
1433
+
1434
+    /**
1435
+     * @inheritdoc
1436
+     */
1437
+    public function shareProviderExists($shareType) {
1438
+        try {
1439
+            $this->factory->getProviderForType($shareType);
1440
+        } catch (ProviderException $e) {
1441
+            return false;
1442
+        }
1443
+
1444
+        return true;
1445
+    }
1446 1446
 
1447 1447
 }
Please login to merge, or discard this patch.
lib/private/Diagnostics/QueryLogger.php 1 patch
Indentation   +55 added lines, -55 removed lines patch added patch discarded remove patch
@@ -29,68 +29,68 @@
 block discarded – undo
29 29
 use OCP\Diagnostics\IQueryLogger;
30 30
 
31 31
 class QueryLogger implements IQueryLogger {
32
-	/**
33
-	 * @var \OC\Diagnostics\Query
34
-	 */
35
-	protected $activeQuery;
32
+    /**
33
+     * @var \OC\Diagnostics\Query
34
+     */
35
+    protected $activeQuery;
36 36
 
37
-	/**
38
-	 * @var CappedMemoryCache
39
-	 */
40
-	protected $queries;
37
+    /**
38
+     * @var CappedMemoryCache
39
+     */
40
+    protected $queries;
41 41
 
42
-	/**
43
-	 * QueryLogger constructor.
44
-	 */
45
-	public function __construct() {
46
-		$this->queries = new CappedMemoryCache(1024);
47
-	}
42
+    /**
43
+     * QueryLogger constructor.
44
+     */
45
+    public function __construct() {
46
+        $this->queries = new CappedMemoryCache(1024);
47
+    }
48 48
 
49 49
 
50
-	/**
51
-	 * @var bool - Module needs to be activated by some app
52
-	 */
53
-	private $activated = false;
50
+    /**
51
+     * @var bool - Module needs to be activated by some app
52
+     */
53
+    private $activated = false;
54 54
 
55
-	/**
56
-	 * @inheritdoc
57
-	 */
58
-	public function startQuery($sql, array $params = null, array $types = null) {
59
-		if ($this->activated) {
60
-			$this->activeQuery = new Query($sql, $params, microtime(true), $this->getStack());
61
-		}
62
-	}
55
+    /**
56
+     * @inheritdoc
57
+     */
58
+    public function startQuery($sql, array $params = null, array $types = null) {
59
+        if ($this->activated) {
60
+            $this->activeQuery = new Query($sql, $params, microtime(true), $this->getStack());
61
+        }
62
+    }
63 63
 
64
-	private function getStack() {
65
-		$stack = debug_backtrace();
66
-		array_shift($stack);
67
-		array_shift($stack);
68
-		array_shift($stack);
69
-		return $stack;
70
-	}
64
+    private function getStack() {
65
+        $stack = debug_backtrace();
66
+        array_shift($stack);
67
+        array_shift($stack);
68
+        array_shift($stack);
69
+        return $stack;
70
+    }
71 71
 
72
-	/**
73
-	 * @inheritdoc
74
-	 */
75
-	public function stopQuery() {
76
-		if ($this->activated && $this->activeQuery) {
77
-			$this->activeQuery->end(microtime(true));
78
-			$this->queries[] = $this->activeQuery;
79
-			$this->activeQuery = null;
80
-		}
81
-	}
72
+    /**
73
+     * @inheritdoc
74
+     */
75
+    public function stopQuery() {
76
+        if ($this->activated && $this->activeQuery) {
77
+            $this->activeQuery->end(microtime(true));
78
+            $this->queries[] = $this->activeQuery;
79
+            $this->activeQuery = null;
80
+        }
81
+    }
82 82
 
83
-	/**
84
-	 * @inheritdoc
85
-	 */
86
-	public function getQueries() {
87
-		return $this->queries->getData();
88
-	}
83
+    /**
84
+     * @inheritdoc
85
+     */
86
+    public function getQueries() {
87
+        return $this->queries->getData();
88
+    }
89 89
 
90
-	/**
91
-	 * @inheritdoc
92
-	 */
93
-	public function activate() {
94
-		$this->activated = true;
95
-	}
90
+    /**
91
+     * @inheritdoc
92
+     */
93
+    public function activate() {
94
+        $this->activated = true;
95
+    }
96 96
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/Swift.php 2 patches
Indentation   +601 added lines, -601 removed lines patch added patch discarded remove patch
@@ -48,606 +48,606 @@
 block discarded – undo
48 48
 
49 49
 class Swift extends \OC\Files\Storage\Common {
50 50
 
51
-	/**
52
-	 * @var \OpenCloud\ObjectStore\Service
53
-	 */
54
-	private $connection;
55
-	/**
56
-	 * @var \OpenCloud\ObjectStore\Resource\Container
57
-	 */
58
-	private $container;
59
-	/**
60
-	 * @var \OpenCloud\OpenStack
61
-	 */
62
-	private $anchor;
63
-	/**
64
-	 * @var string
65
-	 */
66
-	private $bucket;
67
-	/**
68
-	 * Connection parameters
69
-	 *
70
-	 * @var array
71
-	 */
72
-	private $params;
73
-
74
-	/** @var string  */
75
-	private $id;
76
-
77
-	/**
78
-	 * @var array
79
-	 */
80
-	private static $tmpFiles = array();
81
-
82
-	/**
83
-	 * Key value cache mapping path to data object. Maps path to
84
-	 * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing
85
-	 * paths and path to false for not existing paths.
86
-	 * @var \OCP\ICache
87
-	 */
88
-	private $objectCache;
89
-
90
-	/**
91
-	 * @param string $path
92
-	 */
93
-	private function normalizePath($path) {
94
-		$path = trim($path, '/');
95
-
96
-		if (!$path) {
97
-			$path = '.';
98
-		}
99
-
100
-		$path = str_replace('#', '%23', $path);
101
-
102
-		return $path;
103
-	}
104
-
105
-	const SUBCONTAINER_FILE = '.subcontainers';
106
-
107
-	/**
108
-	 * translate directory path to container name
109
-	 *
110
-	 * @param string $path
111
-	 * @return string
112
-	 */
113
-
114
-	/**
115
-	 * Fetches an object from the API.
116
-	 * If the object is cached already or a
117
-	 * failed "doesn't exist" response was cached,
118
-	 * that one will be returned.
119
-	 *
120
-	 * @param string $path
121
-	 * @return \OpenCloud\ObjectStore\Resource\DataObject|bool object
122
-	 * or false if the object did not exist
123
-	 */
124
-	private function fetchObject($path) {
125
-		if ($this->objectCache->hasKey($path)) {
126
-			// might be "false" if object did not exist from last check
127
-			return $this->objectCache->get($path);
128
-		}
129
-		try {
130
-			$object = $this->getContainer()->getPartialObject($path);
131
-			$this->objectCache->set($path, $object);
132
-			return $object;
133
-		} catch (ClientErrorResponseException $e) {
134
-			// this exception happens when the object does not exist, which
135
-			// is expected in most cases
136
-			$this->objectCache->set($path, false);
137
-			return false;
138
-		} catch (ClientErrorResponseException $e) {
139
-			// Expected response is "404 Not Found", so only log if it isn't
140
-			if ($e->getResponse()->getStatusCode() !== 404) {
141
-				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
142
-			}
143
-			return false;
144
-		}
145
-	}
146
-
147
-	/**
148
-	 * Returns whether the given path exists.
149
-	 *
150
-	 * @param string $path
151
-	 *
152
-	 * @return bool true if the object exist, false otherwise
153
-	 */
154
-	private function doesObjectExist($path) {
155
-		return $this->fetchObject($path) !== false;
156
-	}
157
-
158
-	public function __construct($params) {
159
-		if ((empty($params['key']) and empty($params['password']))
160
-			or empty($params['user']) or empty($params['bucket'])
161
-			or empty($params['region'])
162
-		) {
163
-			throw new \Exception("API Key or password, Username, Bucket and Region have to be configured.");
164
-		}
165
-
166
-		$this->id = 'swift::' . $params['user'] . md5($params['bucket']);
167
-
168
-		$bucketUrl = Url::factory($params['bucket']);
169
-		if ($bucketUrl->isAbsolute()) {
170
-			$this->bucket = end(($bucketUrl->getPathSegments()));
171
-			$params['endpoint_url'] = $bucketUrl->addPath('..')->normalizePath();
172
-		} else {
173
-			$this->bucket = $params['bucket'];
174
-		}
175
-
176
-		if (empty($params['url'])) {
177
-			$params['url'] = 'https://identity.api.rackspacecloud.com/v2.0/';
178
-		}
179
-
180
-		if (empty($params['service_name'])) {
181
-			$params['service_name'] = 'cloudFiles';
182
-		}
183
-
184
-		$this->params = $params;
185
-		// FIXME: private class...
186
-		$this->objectCache = new \OC\Cache\CappedMemoryCache();
187
-	}
188
-
189
-	public function mkdir($path) {
190
-		$path = $this->normalizePath($path);
191
-
192
-		if ($this->is_dir($path)) {
193
-			return false;
194
-		}
195
-
196
-		if ($path !== '.') {
197
-			$path .= '/';
198
-		}
199
-
200
-		try {
201
-			$customHeaders = array('content-type' => 'httpd/unix-directory');
202
-			$metadataHeaders = DataObject::stockHeaders(array());
203
-			$allHeaders = $customHeaders + $metadataHeaders;
204
-			$this->getContainer()->uploadObject($path, '', $allHeaders);
205
-			// invalidate so that the next access gets the real object
206
-			// with all properties
207
-			$this->objectCache->remove($path);
208
-		} catch (Exceptions\CreateUpdateError $e) {
209
-			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
210
-			return false;
211
-		}
212
-
213
-		return true;
214
-	}
215
-
216
-	public function file_exists($path) {
217
-		$path = $this->normalizePath($path);
218
-
219
-		if ($path !== '.' && $this->is_dir($path)) {
220
-			$path .= '/';
221
-		}
222
-
223
-		return $this->doesObjectExist($path);
224
-	}
225
-
226
-	public function rmdir($path) {
227
-		$path = $this->normalizePath($path);
228
-
229
-		if (!$this->is_dir($path) || !$this->isDeletable($path)) {
230
-			return false;
231
-		}
232
-
233
-		$dh = $this->opendir($path);
234
-		while ($file = readdir($dh)) {
235
-			if (\OC\Files\Filesystem::isIgnoredDir($file)) {
236
-				continue;
237
-			}
238
-
239
-			if ($this->is_dir($path . '/' . $file)) {
240
-				$this->rmdir($path . '/' . $file);
241
-			} else {
242
-				$this->unlink($path . '/' . $file);
243
-			}
244
-		}
245
-
246
-		try {
247
-			$this->getContainer()->dataObject()->setName($path . '/')->delete();
248
-			$this->objectCache->remove($path . '/');
249
-		} catch (Exceptions\DeleteError $e) {
250
-			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
251
-			return false;
252
-		}
253
-
254
-		return true;
255
-	}
256
-
257
-	public function opendir($path) {
258
-		$path = $this->normalizePath($path);
259
-
260
-		if ($path === '.') {
261
-			$path = '';
262
-		} else {
263
-			$path .= '/';
264
-		}
265
-
266
-		$path = str_replace('%23', '#', $path); // the prefix is sent as a query param, so revert the encoding of #
267
-
268
-		try {
269
-			$files = array();
270
-			/** @var OpenCloud\Common\Collection $objects */
271
-			$objects = $this->getContainer()->objectList(array(
272
-				'prefix' => $path,
273
-				'delimiter' => '/'
274
-			));
275
-
276
-			/** @var OpenCloud\ObjectStore\Resource\DataObject $object */
277
-			foreach ($objects as $object) {
278
-				$file = basename($object->getName());
279
-				if ($file !== basename($path)) {
280
-					$files[] = $file;
281
-				}
282
-			}
283
-
284
-			return IteratorDirectory::wrap($files);
285
-		} catch (\Exception $e) {
286
-			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
287
-			return false;
288
-		}
289
-
290
-	}
291
-
292
-	public function stat($path) {
293
-		$path = $this->normalizePath($path);
294
-
295
-		if ($path === '.') {
296
-			$path = '';
297
-		} else if ($this->is_dir($path)) {
298
-			$path .= '/';
299
-		}
300
-
301
-		try {
302
-			/** @var DataObject $object */
303
-			$object = $this->fetchObject($path);
304
-			if (!$object) {
305
-				return false;
306
-			}
307
-		} catch (ClientErrorResponseException $e) {
308
-			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
309
-			return false;
310
-		}
311
-
312
-		$dateTime = \DateTime::createFromFormat(\DateTime::RFC1123, $object->getLastModified());
313
-		if ($dateTime !== false) {
314
-			$mtime = $dateTime->getTimestamp();
315
-		} else {
316
-			$mtime = null;
317
-		}
318
-		$objectMetadata = $object->getMetadata();
319
-		$metaTimestamp = $objectMetadata->getProperty('timestamp');
320
-		if (isset($metaTimestamp)) {
321
-			$mtime = $metaTimestamp;
322
-		}
323
-
324
-		if (!empty($mtime)) {
325
-			$mtime = floor($mtime);
326
-		}
327
-
328
-		$stat = array();
329
-		$stat['size'] = (int)$object->getContentLength();
330
-		$stat['mtime'] = $mtime;
331
-		$stat['atime'] = time();
332
-		return $stat;
333
-	}
334
-
335
-	public function filetype($path) {
336
-		$path = $this->normalizePath($path);
337
-
338
-		if ($path !== '.' && $this->doesObjectExist($path)) {
339
-			return 'file';
340
-		}
341
-
342
-		if ($path !== '.') {
343
-			$path .= '/';
344
-		}
345
-
346
-		if ($this->doesObjectExist($path)) {
347
-			return 'dir';
348
-		}
349
-	}
350
-
351
-	public function unlink($path) {
352
-		$path = $this->normalizePath($path);
353
-
354
-		if ($this->is_dir($path)) {
355
-			return $this->rmdir($path);
356
-		}
357
-
358
-		try {
359
-			$this->getContainer()->dataObject()->setName($path)->delete();
360
-			$this->objectCache->remove($path);
361
-			$this->objectCache->remove($path . '/');
362
-		} catch (ClientErrorResponseException $e) {
363
-			if ($e->getResponse()->getStatusCode() !== 404) {
364
-				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
365
-			}
366
-			return false;
367
-		}
368
-
369
-		return true;
370
-	}
371
-
372
-	public function fopen($path, $mode) {
373
-		$path = $this->normalizePath($path);
374
-
375
-		switch ($mode) {
376
-			case 'a':
377
-			case 'ab':
378
-			case 'a+':
379
-				return false;
380
-			case 'r':
381
-			case 'rb':
382
-				try {
383
-					$c = $this->getContainer();
384
-					$streamFactory = new \Guzzle\Stream\PhpStreamRequestFactory();
385
-					/** @var \OpenCloud\Common\Http\Client $client */
386
-					$client = $c->getClient();
387
-					$streamInterface = $streamFactory->fromRequest($client->get($c->getUrl($path)));
388
-					$streamInterface->rewind();
389
-					$stream = $streamInterface->getStream();
390
-					stream_context_set_option($stream, 'swift','content', $streamInterface);
391
-					if(!strrpos($streamInterface
392
-						->getMetaData('wrapper_data')[0], '404 Not Found')) {
393
-						return $stream;
394
-					}
395
-					return false;
396
-				} catch (\Guzzle\Http\Exception\BadResponseException $e) {
397
-					\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
398
-					return false;
399
-				}
400
-			case 'w':
401
-			case 'wb':
402
-			case 'r+':
403
-			case 'w+':
404
-			case 'wb+':
405
-			case 'x':
406
-			case 'x+':
407
-			case 'c':
408
-			case 'c+':
409
-				if (strrpos($path, '.') !== false) {
410
-					$ext = substr($path, strrpos($path, '.'));
411
-				} else {
412
-					$ext = '';
413
-				}
414
-				$tmpFile = \OCP\Files::tmpFile($ext);
415
-				// Fetch existing file if required
416
-				if ($mode[0] !== 'w' && $this->file_exists($path)) {
417
-					if ($mode[0] === 'x') {
418
-						// File cannot already exist
419
-						return false;
420
-					}
421
-					$source = $this->fopen($path, 'r');
422
-					file_put_contents($tmpFile, $source);
423
-				}
424
-				$handle = fopen($tmpFile, $mode);
425
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
426
-					$this->writeBack($tmpFile, $path);
427
-				});
428
-		}
429
-	}
430
-
431
-	public function touch($path, $mtime = null) {
432
-		$path = $this->normalizePath($path);
433
-		if (is_null($mtime)) {
434
-			$mtime = time();
435
-		}
436
-		$metadata = array('timestamp' => $mtime);
437
-		if ($this->file_exists($path)) {
438
-			if ($this->is_dir($path) && $path != '.') {
439
-				$path .= '/';
440
-			}
441
-
442
-			$object = $this->fetchObject($path);
443
-			if ($object->saveMetadata($metadata)) {
444
-				// invalidate target object to force repopulation on fetch
445
-				$this->objectCache->remove($path);
446
-			}
447
-			return true;
448
-		} else {
449
-			$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
450
-			$customHeaders = array('content-type' => $mimeType);
451
-			$metadataHeaders = DataObject::stockHeaders($metadata);
452
-			$allHeaders = $customHeaders + $metadataHeaders;
453
-			$this->getContainer()->uploadObject($path, '', $allHeaders);
454
-			// invalidate target object to force repopulation on fetch
455
-			$this->objectCache->remove($path);
456
-			return true;
457
-		}
458
-	}
459
-
460
-	public function copy($path1, $path2) {
461
-		$path1 = $this->normalizePath($path1);
462
-		$path2 = $this->normalizePath($path2);
463
-
464
-		$fileType = $this->filetype($path1);
465
-		if ($fileType === 'file') {
466
-
467
-			// make way
468
-			$this->unlink($path2);
469
-
470
-			try {
471
-				$source = $this->fetchObject($path1);
472
-				$source->copy($this->bucket . '/' . $path2);
473
-				// invalidate target object to force repopulation on fetch
474
-				$this->objectCache->remove($path2);
475
-				$this->objectCache->remove($path2 . '/');
476
-			} catch (ClientErrorResponseException $e) {
477
-				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
478
-				return false;
479
-			}
480
-
481
-		} else if ($fileType === 'dir') {
482
-
483
-			// make way
484
-			$this->unlink($path2);
485
-
486
-			try {
487
-				$source = $this->fetchObject($path1 . '/');
488
-				$source->copy($this->bucket . '/' . $path2 . '/');
489
-				// invalidate target object to force repopulation on fetch
490
-				$this->objectCache->remove($path2);
491
-				$this->objectCache->remove($path2 . '/');
492
-			} catch (ClientErrorResponseException $e) {
493
-				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
494
-				return false;
495
-			}
496
-
497
-			$dh = $this->opendir($path1);
498
-			while ($file = readdir($dh)) {
499
-				if (\OC\Files\Filesystem::isIgnoredDir($file)) {
500
-					continue;
501
-				}
502
-
503
-				$source = $path1 . '/' . $file;
504
-				$target = $path2 . '/' . $file;
505
-				$this->copy($source, $target);
506
-			}
507
-
508
-		} else {
509
-			//file does not exist
510
-			return false;
511
-		}
512
-
513
-		return true;
514
-	}
515
-
516
-	public function rename($path1, $path2) {
517
-		$path1 = $this->normalizePath($path1);
518
-		$path2 = $this->normalizePath($path2);
519
-
520
-		$fileType = $this->filetype($path1);
521
-
522
-		if ($fileType === 'dir' || $fileType === 'file') {
523
-			// copy
524
-			if ($this->copy($path1, $path2) === false) {
525
-				return false;
526
-			}
527
-
528
-			// cleanup
529
-			if ($this->unlink($path1) === false) {
530
-				$this->unlink($path2);
531
-				return false;
532
-			}
533
-
534
-			return true;
535
-		}
536
-
537
-		return false;
538
-	}
539
-
540
-	public function getId() {
541
-		return $this->id;
542
-	}
543
-
544
-	/**
545
-	 * Returns the connection
546
-	 *
547
-	 * @return OpenCloud\ObjectStore\Service connected client
548
-	 * @throws \Exception if connection could not be made
549
-	 */
550
-	public function getConnection() {
551
-		if (!is_null($this->connection)) {
552
-			return $this->connection;
553
-		}
554
-
555
-		$settings = array(
556
-			'username' => $this->params['user'],
557
-		);
558
-
559
-		if (!empty($this->params['password'])) {
560
-			$settings['password'] = $this->params['password'];
561
-		} else if (!empty($this->params['key'])) {
562
-			$settings['apiKey'] = $this->params['key'];
563
-		}
564
-
565
-		if (!empty($this->params['tenant'])) {
566
-			$settings['tenantName'] = $this->params['tenant'];
567
-		}
568
-
569
-		if (!empty($this->params['timeout'])) {
570
-			$settings['timeout'] = $this->params['timeout'];
571
-		}
572
-
573
-		if (isset($settings['apiKey'])) {
574
-			$this->anchor = new Rackspace($this->params['url'], $settings);
575
-		} else {
576
-			$this->anchor = new OpenStack($this->params['url'], $settings);
577
-		}
578
-
579
-		$connection = $this->anchor->objectStoreService($this->params['service_name'], $this->params['region']);
580
-
581
-		if (!empty($this->params['endpoint_url'])) {
582
-			$endpoint = $connection->getEndpoint();
583
-			$endpoint->setPublicUrl($this->params['endpoint_url']);
584
-			$endpoint->setPrivateUrl($this->params['endpoint_url']);
585
-			$connection->setEndpoint($endpoint);
586
-		}
587
-
588
-		$this->connection = $connection;
589
-
590
-		return $this->connection;
591
-	}
592
-
593
-	/**
594
-	 * Returns the initialized object store container.
595
-	 *
596
-	 * @return OpenCloud\ObjectStore\Resource\Container
597
-	 */
598
-	public function getContainer() {
599
-		if (!is_null($this->container)) {
600
-			return $this->container;
601
-		}
602
-
603
-		try {
604
-			$this->container = $this->getConnection()->getContainer($this->bucket);
605
-		} catch (ClientErrorResponseException $e) {
606
-			$this->container = $this->getConnection()->createContainer($this->bucket);
607
-		}
608
-
609
-		if (!$this->file_exists('.')) {
610
-			$this->mkdir('.');
611
-		}
612
-
613
-		return $this->container;
614
-	}
615
-
616
-	public function writeBack($tmpFile, $path) {
617
-		$fileData = fopen($tmpFile, 'r');
618
-		$this->getContainer()->uploadObject($path, $fileData);
619
-		// invalidate target object to force repopulation on fetch
620
-		$this->objectCache->remove(self::$tmpFiles[$tmpFile]);
621
-		unlink($tmpFile);
622
-	}
623
-
624
-	public function hasUpdated($path, $time) {
625
-		if ($this->is_file($path)) {
626
-			return parent::hasUpdated($path, $time);
627
-		}
628
-		$path = $this->normalizePath($path);
629
-		$dh = $this->opendir($path);
630
-		$content = array();
631
-		while (($file = readdir($dh)) !== false) {
632
-			$content[] = $file;
633
-		}
634
-		if ($path === '.') {
635
-			$path = '';
636
-		}
637
-		$cachedContent = $this->getCache()->getFolderContents($path);
638
-		$cachedNames = array_map(function ($content) {
639
-			return $content['name'];
640
-		}, $cachedContent);
641
-		sort($cachedNames);
642
-		sort($content);
643
-		return $cachedNames != $content;
644
-	}
645
-
646
-	/**
647
-	 * check if curl is installed
648
-	 */
649
-	public static function checkDependencies() {
650
-		return true;
651
-	}
51
+    /**
52
+     * @var \OpenCloud\ObjectStore\Service
53
+     */
54
+    private $connection;
55
+    /**
56
+     * @var \OpenCloud\ObjectStore\Resource\Container
57
+     */
58
+    private $container;
59
+    /**
60
+     * @var \OpenCloud\OpenStack
61
+     */
62
+    private $anchor;
63
+    /**
64
+     * @var string
65
+     */
66
+    private $bucket;
67
+    /**
68
+     * Connection parameters
69
+     *
70
+     * @var array
71
+     */
72
+    private $params;
73
+
74
+    /** @var string  */
75
+    private $id;
76
+
77
+    /**
78
+     * @var array
79
+     */
80
+    private static $tmpFiles = array();
81
+
82
+    /**
83
+     * Key value cache mapping path to data object. Maps path to
84
+     * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing
85
+     * paths and path to false for not existing paths.
86
+     * @var \OCP\ICache
87
+     */
88
+    private $objectCache;
89
+
90
+    /**
91
+     * @param string $path
92
+     */
93
+    private function normalizePath($path) {
94
+        $path = trim($path, '/');
95
+
96
+        if (!$path) {
97
+            $path = '.';
98
+        }
99
+
100
+        $path = str_replace('#', '%23', $path);
101
+
102
+        return $path;
103
+    }
104
+
105
+    const SUBCONTAINER_FILE = '.subcontainers';
106
+
107
+    /**
108
+     * translate directory path to container name
109
+     *
110
+     * @param string $path
111
+     * @return string
112
+     */
113
+
114
+    /**
115
+     * Fetches an object from the API.
116
+     * If the object is cached already or a
117
+     * failed "doesn't exist" response was cached,
118
+     * that one will be returned.
119
+     *
120
+     * @param string $path
121
+     * @return \OpenCloud\ObjectStore\Resource\DataObject|bool object
122
+     * or false if the object did not exist
123
+     */
124
+    private function fetchObject($path) {
125
+        if ($this->objectCache->hasKey($path)) {
126
+            // might be "false" if object did not exist from last check
127
+            return $this->objectCache->get($path);
128
+        }
129
+        try {
130
+            $object = $this->getContainer()->getPartialObject($path);
131
+            $this->objectCache->set($path, $object);
132
+            return $object;
133
+        } catch (ClientErrorResponseException $e) {
134
+            // this exception happens when the object does not exist, which
135
+            // is expected in most cases
136
+            $this->objectCache->set($path, false);
137
+            return false;
138
+        } catch (ClientErrorResponseException $e) {
139
+            // Expected response is "404 Not Found", so only log if it isn't
140
+            if ($e->getResponse()->getStatusCode() !== 404) {
141
+                \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
142
+            }
143
+            return false;
144
+        }
145
+    }
146
+
147
+    /**
148
+     * Returns whether the given path exists.
149
+     *
150
+     * @param string $path
151
+     *
152
+     * @return bool true if the object exist, false otherwise
153
+     */
154
+    private function doesObjectExist($path) {
155
+        return $this->fetchObject($path) !== false;
156
+    }
157
+
158
+    public function __construct($params) {
159
+        if ((empty($params['key']) and empty($params['password']))
160
+            or empty($params['user']) or empty($params['bucket'])
161
+            or empty($params['region'])
162
+        ) {
163
+            throw new \Exception("API Key or password, Username, Bucket and Region have to be configured.");
164
+        }
165
+
166
+        $this->id = 'swift::' . $params['user'] . md5($params['bucket']);
167
+
168
+        $bucketUrl = Url::factory($params['bucket']);
169
+        if ($bucketUrl->isAbsolute()) {
170
+            $this->bucket = end(($bucketUrl->getPathSegments()));
171
+            $params['endpoint_url'] = $bucketUrl->addPath('..')->normalizePath();
172
+        } else {
173
+            $this->bucket = $params['bucket'];
174
+        }
175
+
176
+        if (empty($params['url'])) {
177
+            $params['url'] = 'https://identity.api.rackspacecloud.com/v2.0/';
178
+        }
179
+
180
+        if (empty($params['service_name'])) {
181
+            $params['service_name'] = 'cloudFiles';
182
+        }
183
+
184
+        $this->params = $params;
185
+        // FIXME: private class...
186
+        $this->objectCache = new \OC\Cache\CappedMemoryCache();
187
+    }
188
+
189
+    public function mkdir($path) {
190
+        $path = $this->normalizePath($path);
191
+
192
+        if ($this->is_dir($path)) {
193
+            return false;
194
+        }
195
+
196
+        if ($path !== '.') {
197
+            $path .= '/';
198
+        }
199
+
200
+        try {
201
+            $customHeaders = array('content-type' => 'httpd/unix-directory');
202
+            $metadataHeaders = DataObject::stockHeaders(array());
203
+            $allHeaders = $customHeaders + $metadataHeaders;
204
+            $this->getContainer()->uploadObject($path, '', $allHeaders);
205
+            // invalidate so that the next access gets the real object
206
+            // with all properties
207
+            $this->objectCache->remove($path);
208
+        } catch (Exceptions\CreateUpdateError $e) {
209
+            \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
210
+            return false;
211
+        }
212
+
213
+        return true;
214
+    }
215
+
216
+    public function file_exists($path) {
217
+        $path = $this->normalizePath($path);
218
+
219
+        if ($path !== '.' && $this->is_dir($path)) {
220
+            $path .= '/';
221
+        }
222
+
223
+        return $this->doesObjectExist($path);
224
+    }
225
+
226
+    public function rmdir($path) {
227
+        $path = $this->normalizePath($path);
228
+
229
+        if (!$this->is_dir($path) || !$this->isDeletable($path)) {
230
+            return false;
231
+        }
232
+
233
+        $dh = $this->opendir($path);
234
+        while ($file = readdir($dh)) {
235
+            if (\OC\Files\Filesystem::isIgnoredDir($file)) {
236
+                continue;
237
+            }
238
+
239
+            if ($this->is_dir($path . '/' . $file)) {
240
+                $this->rmdir($path . '/' . $file);
241
+            } else {
242
+                $this->unlink($path . '/' . $file);
243
+            }
244
+        }
245
+
246
+        try {
247
+            $this->getContainer()->dataObject()->setName($path . '/')->delete();
248
+            $this->objectCache->remove($path . '/');
249
+        } catch (Exceptions\DeleteError $e) {
250
+            \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
251
+            return false;
252
+        }
253
+
254
+        return true;
255
+    }
256
+
257
+    public function opendir($path) {
258
+        $path = $this->normalizePath($path);
259
+
260
+        if ($path === '.') {
261
+            $path = '';
262
+        } else {
263
+            $path .= '/';
264
+        }
265
+
266
+        $path = str_replace('%23', '#', $path); // the prefix is sent as a query param, so revert the encoding of #
267
+
268
+        try {
269
+            $files = array();
270
+            /** @var OpenCloud\Common\Collection $objects */
271
+            $objects = $this->getContainer()->objectList(array(
272
+                'prefix' => $path,
273
+                'delimiter' => '/'
274
+            ));
275
+
276
+            /** @var OpenCloud\ObjectStore\Resource\DataObject $object */
277
+            foreach ($objects as $object) {
278
+                $file = basename($object->getName());
279
+                if ($file !== basename($path)) {
280
+                    $files[] = $file;
281
+                }
282
+            }
283
+
284
+            return IteratorDirectory::wrap($files);
285
+        } catch (\Exception $e) {
286
+            \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
287
+            return false;
288
+        }
289
+
290
+    }
291
+
292
+    public function stat($path) {
293
+        $path = $this->normalizePath($path);
294
+
295
+        if ($path === '.') {
296
+            $path = '';
297
+        } else if ($this->is_dir($path)) {
298
+            $path .= '/';
299
+        }
300
+
301
+        try {
302
+            /** @var DataObject $object */
303
+            $object = $this->fetchObject($path);
304
+            if (!$object) {
305
+                return false;
306
+            }
307
+        } catch (ClientErrorResponseException $e) {
308
+            \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
309
+            return false;
310
+        }
311
+
312
+        $dateTime = \DateTime::createFromFormat(\DateTime::RFC1123, $object->getLastModified());
313
+        if ($dateTime !== false) {
314
+            $mtime = $dateTime->getTimestamp();
315
+        } else {
316
+            $mtime = null;
317
+        }
318
+        $objectMetadata = $object->getMetadata();
319
+        $metaTimestamp = $objectMetadata->getProperty('timestamp');
320
+        if (isset($metaTimestamp)) {
321
+            $mtime = $metaTimestamp;
322
+        }
323
+
324
+        if (!empty($mtime)) {
325
+            $mtime = floor($mtime);
326
+        }
327
+
328
+        $stat = array();
329
+        $stat['size'] = (int)$object->getContentLength();
330
+        $stat['mtime'] = $mtime;
331
+        $stat['atime'] = time();
332
+        return $stat;
333
+    }
334
+
335
+    public function filetype($path) {
336
+        $path = $this->normalizePath($path);
337
+
338
+        if ($path !== '.' && $this->doesObjectExist($path)) {
339
+            return 'file';
340
+        }
341
+
342
+        if ($path !== '.') {
343
+            $path .= '/';
344
+        }
345
+
346
+        if ($this->doesObjectExist($path)) {
347
+            return 'dir';
348
+        }
349
+    }
350
+
351
+    public function unlink($path) {
352
+        $path = $this->normalizePath($path);
353
+
354
+        if ($this->is_dir($path)) {
355
+            return $this->rmdir($path);
356
+        }
357
+
358
+        try {
359
+            $this->getContainer()->dataObject()->setName($path)->delete();
360
+            $this->objectCache->remove($path);
361
+            $this->objectCache->remove($path . '/');
362
+        } catch (ClientErrorResponseException $e) {
363
+            if ($e->getResponse()->getStatusCode() !== 404) {
364
+                \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
365
+            }
366
+            return false;
367
+        }
368
+
369
+        return true;
370
+    }
371
+
372
+    public function fopen($path, $mode) {
373
+        $path = $this->normalizePath($path);
374
+
375
+        switch ($mode) {
376
+            case 'a':
377
+            case 'ab':
378
+            case 'a+':
379
+                return false;
380
+            case 'r':
381
+            case 'rb':
382
+                try {
383
+                    $c = $this->getContainer();
384
+                    $streamFactory = new \Guzzle\Stream\PhpStreamRequestFactory();
385
+                    /** @var \OpenCloud\Common\Http\Client $client */
386
+                    $client = $c->getClient();
387
+                    $streamInterface = $streamFactory->fromRequest($client->get($c->getUrl($path)));
388
+                    $streamInterface->rewind();
389
+                    $stream = $streamInterface->getStream();
390
+                    stream_context_set_option($stream, 'swift','content', $streamInterface);
391
+                    if(!strrpos($streamInterface
392
+                        ->getMetaData('wrapper_data')[0], '404 Not Found')) {
393
+                        return $stream;
394
+                    }
395
+                    return false;
396
+                } catch (\Guzzle\Http\Exception\BadResponseException $e) {
397
+                    \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
398
+                    return false;
399
+                }
400
+            case 'w':
401
+            case 'wb':
402
+            case 'r+':
403
+            case 'w+':
404
+            case 'wb+':
405
+            case 'x':
406
+            case 'x+':
407
+            case 'c':
408
+            case 'c+':
409
+                if (strrpos($path, '.') !== false) {
410
+                    $ext = substr($path, strrpos($path, '.'));
411
+                } else {
412
+                    $ext = '';
413
+                }
414
+                $tmpFile = \OCP\Files::tmpFile($ext);
415
+                // Fetch existing file if required
416
+                if ($mode[0] !== 'w' && $this->file_exists($path)) {
417
+                    if ($mode[0] === 'x') {
418
+                        // File cannot already exist
419
+                        return false;
420
+                    }
421
+                    $source = $this->fopen($path, 'r');
422
+                    file_put_contents($tmpFile, $source);
423
+                }
424
+                $handle = fopen($tmpFile, $mode);
425
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
426
+                    $this->writeBack($tmpFile, $path);
427
+                });
428
+        }
429
+    }
430
+
431
+    public function touch($path, $mtime = null) {
432
+        $path = $this->normalizePath($path);
433
+        if (is_null($mtime)) {
434
+            $mtime = time();
435
+        }
436
+        $metadata = array('timestamp' => $mtime);
437
+        if ($this->file_exists($path)) {
438
+            if ($this->is_dir($path) && $path != '.') {
439
+                $path .= '/';
440
+            }
441
+
442
+            $object = $this->fetchObject($path);
443
+            if ($object->saveMetadata($metadata)) {
444
+                // invalidate target object to force repopulation on fetch
445
+                $this->objectCache->remove($path);
446
+            }
447
+            return true;
448
+        } else {
449
+            $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
450
+            $customHeaders = array('content-type' => $mimeType);
451
+            $metadataHeaders = DataObject::stockHeaders($metadata);
452
+            $allHeaders = $customHeaders + $metadataHeaders;
453
+            $this->getContainer()->uploadObject($path, '', $allHeaders);
454
+            // invalidate target object to force repopulation on fetch
455
+            $this->objectCache->remove($path);
456
+            return true;
457
+        }
458
+    }
459
+
460
+    public function copy($path1, $path2) {
461
+        $path1 = $this->normalizePath($path1);
462
+        $path2 = $this->normalizePath($path2);
463
+
464
+        $fileType = $this->filetype($path1);
465
+        if ($fileType === 'file') {
466
+
467
+            // make way
468
+            $this->unlink($path2);
469
+
470
+            try {
471
+                $source = $this->fetchObject($path1);
472
+                $source->copy($this->bucket . '/' . $path2);
473
+                // invalidate target object to force repopulation on fetch
474
+                $this->objectCache->remove($path2);
475
+                $this->objectCache->remove($path2 . '/');
476
+            } catch (ClientErrorResponseException $e) {
477
+                \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
478
+                return false;
479
+            }
480
+
481
+        } else if ($fileType === 'dir') {
482
+
483
+            // make way
484
+            $this->unlink($path2);
485
+
486
+            try {
487
+                $source = $this->fetchObject($path1 . '/');
488
+                $source->copy($this->bucket . '/' . $path2 . '/');
489
+                // invalidate target object to force repopulation on fetch
490
+                $this->objectCache->remove($path2);
491
+                $this->objectCache->remove($path2 . '/');
492
+            } catch (ClientErrorResponseException $e) {
493
+                \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
494
+                return false;
495
+            }
496
+
497
+            $dh = $this->opendir($path1);
498
+            while ($file = readdir($dh)) {
499
+                if (\OC\Files\Filesystem::isIgnoredDir($file)) {
500
+                    continue;
501
+                }
502
+
503
+                $source = $path1 . '/' . $file;
504
+                $target = $path2 . '/' . $file;
505
+                $this->copy($source, $target);
506
+            }
507
+
508
+        } else {
509
+            //file does not exist
510
+            return false;
511
+        }
512
+
513
+        return true;
514
+    }
515
+
516
+    public function rename($path1, $path2) {
517
+        $path1 = $this->normalizePath($path1);
518
+        $path2 = $this->normalizePath($path2);
519
+
520
+        $fileType = $this->filetype($path1);
521
+
522
+        if ($fileType === 'dir' || $fileType === 'file') {
523
+            // copy
524
+            if ($this->copy($path1, $path2) === false) {
525
+                return false;
526
+            }
527
+
528
+            // cleanup
529
+            if ($this->unlink($path1) === false) {
530
+                $this->unlink($path2);
531
+                return false;
532
+            }
533
+
534
+            return true;
535
+        }
536
+
537
+        return false;
538
+    }
539
+
540
+    public function getId() {
541
+        return $this->id;
542
+    }
543
+
544
+    /**
545
+     * Returns the connection
546
+     *
547
+     * @return OpenCloud\ObjectStore\Service connected client
548
+     * @throws \Exception if connection could not be made
549
+     */
550
+    public function getConnection() {
551
+        if (!is_null($this->connection)) {
552
+            return $this->connection;
553
+        }
554
+
555
+        $settings = array(
556
+            'username' => $this->params['user'],
557
+        );
558
+
559
+        if (!empty($this->params['password'])) {
560
+            $settings['password'] = $this->params['password'];
561
+        } else if (!empty($this->params['key'])) {
562
+            $settings['apiKey'] = $this->params['key'];
563
+        }
564
+
565
+        if (!empty($this->params['tenant'])) {
566
+            $settings['tenantName'] = $this->params['tenant'];
567
+        }
568
+
569
+        if (!empty($this->params['timeout'])) {
570
+            $settings['timeout'] = $this->params['timeout'];
571
+        }
572
+
573
+        if (isset($settings['apiKey'])) {
574
+            $this->anchor = new Rackspace($this->params['url'], $settings);
575
+        } else {
576
+            $this->anchor = new OpenStack($this->params['url'], $settings);
577
+        }
578
+
579
+        $connection = $this->anchor->objectStoreService($this->params['service_name'], $this->params['region']);
580
+
581
+        if (!empty($this->params['endpoint_url'])) {
582
+            $endpoint = $connection->getEndpoint();
583
+            $endpoint->setPublicUrl($this->params['endpoint_url']);
584
+            $endpoint->setPrivateUrl($this->params['endpoint_url']);
585
+            $connection->setEndpoint($endpoint);
586
+        }
587
+
588
+        $this->connection = $connection;
589
+
590
+        return $this->connection;
591
+    }
592
+
593
+    /**
594
+     * Returns the initialized object store container.
595
+     *
596
+     * @return OpenCloud\ObjectStore\Resource\Container
597
+     */
598
+    public function getContainer() {
599
+        if (!is_null($this->container)) {
600
+            return $this->container;
601
+        }
602
+
603
+        try {
604
+            $this->container = $this->getConnection()->getContainer($this->bucket);
605
+        } catch (ClientErrorResponseException $e) {
606
+            $this->container = $this->getConnection()->createContainer($this->bucket);
607
+        }
608
+
609
+        if (!$this->file_exists('.')) {
610
+            $this->mkdir('.');
611
+        }
612
+
613
+        return $this->container;
614
+    }
615
+
616
+    public function writeBack($tmpFile, $path) {
617
+        $fileData = fopen($tmpFile, 'r');
618
+        $this->getContainer()->uploadObject($path, $fileData);
619
+        // invalidate target object to force repopulation on fetch
620
+        $this->objectCache->remove(self::$tmpFiles[$tmpFile]);
621
+        unlink($tmpFile);
622
+    }
623
+
624
+    public function hasUpdated($path, $time) {
625
+        if ($this->is_file($path)) {
626
+            return parent::hasUpdated($path, $time);
627
+        }
628
+        $path = $this->normalizePath($path);
629
+        $dh = $this->opendir($path);
630
+        $content = array();
631
+        while (($file = readdir($dh)) !== false) {
632
+            $content[] = $file;
633
+        }
634
+        if ($path === '.') {
635
+            $path = '';
636
+        }
637
+        $cachedContent = $this->getCache()->getFolderContents($path);
638
+        $cachedNames = array_map(function ($content) {
639
+            return $content['name'];
640
+        }, $cachedContent);
641
+        sort($cachedNames);
642
+        sort($content);
643
+        return $cachedNames != $content;
644
+    }
645
+
646
+    /**
647
+     * check if curl is installed
648
+     */
649
+    public static function checkDependencies() {
650
+        return true;
651
+    }
652 652
 
653 653
 }
Please login to merge, or discard this patch.
Spacing   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -163,7 +163,7 @@  discard block
 block discarded – undo
163 163
 			throw new \Exception("API Key or password, Username, Bucket and Region have to be configured.");
164 164
 		}
165 165
 
166
-		$this->id = 'swift::' . $params['user'] . md5($params['bucket']);
166
+		$this->id = 'swift::'.$params['user'].md5($params['bucket']);
167 167
 
168 168
 		$bucketUrl = Url::factory($params['bucket']);
169 169
 		if ($bucketUrl->isAbsolute()) {
@@ -236,16 +236,16 @@  discard block
 block discarded – undo
236 236
 				continue;
237 237
 			}
238 238
 
239
-			if ($this->is_dir($path . '/' . $file)) {
240
-				$this->rmdir($path . '/' . $file);
239
+			if ($this->is_dir($path.'/'.$file)) {
240
+				$this->rmdir($path.'/'.$file);
241 241
 			} else {
242
-				$this->unlink($path . '/' . $file);
242
+				$this->unlink($path.'/'.$file);
243 243
 			}
244 244
 		}
245 245
 
246 246
 		try {
247
-			$this->getContainer()->dataObject()->setName($path . '/')->delete();
248
-			$this->objectCache->remove($path . '/');
247
+			$this->getContainer()->dataObject()->setName($path.'/')->delete();
248
+			$this->objectCache->remove($path.'/');
249 249
 		} catch (Exceptions\DeleteError $e) {
250 250
 			\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
251 251
 			return false;
@@ -326,7 +326,7 @@  discard block
 block discarded – undo
326 326
 		}
327 327
 
328 328
 		$stat = array();
329
-		$stat['size'] = (int)$object->getContentLength();
329
+		$stat['size'] = (int) $object->getContentLength();
330 330
 		$stat['mtime'] = $mtime;
331 331
 		$stat['atime'] = time();
332 332
 		return $stat;
@@ -358,7 +358,7 @@  discard block
 block discarded – undo
358 358
 		try {
359 359
 			$this->getContainer()->dataObject()->setName($path)->delete();
360 360
 			$this->objectCache->remove($path);
361
-			$this->objectCache->remove($path . '/');
361
+			$this->objectCache->remove($path.'/');
362 362
 		} catch (ClientErrorResponseException $e) {
363 363
 			if ($e->getResponse()->getStatusCode() !== 404) {
364 364
 				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
@@ -387,8 +387,8 @@  discard block
 block discarded – undo
387 387
 					$streamInterface = $streamFactory->fromRequest($client->get($c->getUrl($path)));
388 388
 					$streamInterface->rewind();
389 389
 					$stream = $streamInterface->getStream();
390
-					stream_context_set_option($stream, 'swift','content', $streamInterface);
391
-					if(!strrpos($streamInterface
390
+					stream_context_set_option($stream, 'swift', 'content', $streamInterface);
391
+					if (!strrpos($streamInterface
392 392
 						->getMetaData('wrapper_data')[0], '404 Not Found')) {
393 393
 						return $stream;
394 394
 					}
@@ -422,7 +422,7 @@  discard block
 block discarded – undo
422 422
 					file_put_contents($tmpFile, $source);
423 423
 				}
424 424
 				$handle = fopen($tmpFile, $mode);
425
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
425
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
426 426
 					$this->writeBack($tmpFile, $path);
427 427
 				});
428 428
 		}
@@ -469,10 +469,10 @@  discard block
 block discarded – undo
469 469
 
470 470
 			try {
471 471
 				$source = $this->fetchObject($path1);
472
-				$source->copy($this->bucket . '/' . $path2);
472
+				$source->copy($this->bucket.'/'.$path2);
473 473
 				// invalidate target object to force repopulation on fetch
474 474
 				$this->objectCache->remove($path2);
475
-				$this->objectCache->remove($path2 . '/');
475
+				$this->objectCache->remove($path2.'/');
476 476
 			} catch (ClientErrorResponseException $e) {
477 477
 				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
478 478
 				return false;
@@ -484,11 +484,11 @@  discard block
 block discarded – undo
484 484
 			$this->unlink($path2);
485 485
 
486 486
 			try {
487
-				$source = $this->fetchObject($path1 . '/');
488
-				$source->copy($this->bucket . '/' . $path2 . '/');
487
+				$source = $this->fetchObject($path1.'/');
488
+				$source->copy($this->bucket.'/'.$path2.'/');
489 489
 				// invalidate target object to force repopulation on fetch
490 490
 				$this->objectCache->remove($path2);
491
-				$this->objectCache->remove($path2 . '/');
491
+				$this->objectCache->remove($path2.'/');
492 492
 			} catch (ClientErrorResponseException $e) {
493 493
 				\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
494 494
 				return false;
@@ -500,8 +500,8 @@  discard block
 block discarded – undo
500 500
 					continue;
501 501
 				}
502 502
 
503
-				$source = $path1 . '/' . $file;
504
-				$target = $path2 . '/' . $file;
503
+				$source = $path1.'/'.$file;
504
+				$target = $path2.'/'.$file;
505 505
 				$this->copy($source, $target);
506 506
 			}
507 507
 
@@ -635,7 +635,7 @@  discard block
 block discarded – undo
635 635
 			$path = '';
636 636
 		}
637 637
 		$cachedContent = $this->getCache()->getFolderContents($path);
638
-		$cachedNames = array_map(function ($content) {
638
+		$cachedNames = array_map(function($content) {
639 639
 			return $content['name'];
640 640
 		}, $cachedContent);
641 641
 		sort($cachedNames);
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareAPIController.php 1 patch
Indentation   +878 added lines, -878 removed lines patch added patch discarded remove patch
@@ -52,891 +52,891 @@
 block discarded – undo
52 52
  */
53 53
 class ShareAPIController extends OCSController {
54 54
 
55
-	/** @var IManager */
56
-	private $shareManager;
57
-	/** @var IGroupManager */
58
-	private $groupManager;
59
-	/** @var IUserManager */
60
-	private $userManager;
61
-	/** @var IRequest */
62
-	protected $request;
63
-	/** @var IRootFolder */
64
-	private $rootFolder;
65
-	/** @var IURLGenerator */
66
-	private $urlGenerator;
67
-	/** @var string */
68
-	private $currentUser;
69
-	/** @var IL10N */
70
-	private $l;
71
-	/** @var \OCP\Files\Node */
72
-	private $lockedNode;
73
-
74
-	/**
75
-	 * Share20OCS constructor.
76
-	 *
77
-	 * @param string $appName
78
-	 * @param IRequest $request
79
-	 * @param IManager $shareManager
80
-	 * @param IGroupManager $groupManager
81
-	 * @param IUserManager $userManager
82
-	 * @param IRootFolder $rootFolder
83
-	 * @param IURLGenerator $urlGenerator
84
-	 * @param string $userId
85
-	 * @param IL10N $l10n
86
-	 */
87
-	public function __construct(
88
-		$appName,
89
-		IRequest $request,
90
-		IManager $shareManager,
91
-		IGroupManager $groupManager,
92
-		IUserManager $userManager,
93
-		IRootFolder $rootFolder,
94
-		IURLGenerator $urlGenerator,
95
-		$userId,
96
-		IL10N $l10n
97
-	) {
98
-		parent::__construct($appName, $request);
99
-
100
-		$this->shareManager = $shareManager;
101
-		$this->userManager = $userManager;
102
-		$this->groupManager = $groupManager;
103
-		$this->request = $request;
104
-		$this->rootFolder = $rootFolder;
105
-		$this->urlGenerator = $urlGenerator;
106
-		$this->currentUser = $userId;
107
-		$this->l = $l10n;
108
-	}
109
-
110
-	/**
111
-	 * Convert an IShare to an array for OCS output
112
-	 *
113
-	 * @param \OCP\Share\IShare $share
114
-	 * @param Node|null $recipientNode
115
-	 * @return array
116
-	 * @throws NotFoundException In case the node can't be resolved.
117
-	 */
118
-	protected function formatShare(\OCP\Share\IShare $share, Node $recipientNode = null) {
119
-		$sharedBy = $this->userManager->get($share->getSharedBy());
120
-		$shareOwner = $this->userManager->get($share->getShareOwner());
121
-
122
-		$result = [
123
-			'id' => $share->getId(),
124
-			'share_type' => $share->getShareType(),
125
-			'uid_owner' => $share->getSharedBy(),
126
-			'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
127
-			'permissions' => $share->getPermissions(),
128
-			'stime' => $share->getShareTime()->getTimestamp(),
129
-			'parent' => null,
130
-			'expiration' => null,
131
-			'token' => null,
132
-			'uid_file_owner' => $share->getShareOwner(),
133
-			'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
134
-		];
135
-
136
-		$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
137
-		if ($recipientNode) {
138
-			$node = $recipientNode;
139
-		} else {
140
-			$nodes = $userFolder->getById($share->getNodeId());
141
-
142
-			if (empty($nodes)) {
143
-				// fallback to guessing the path
144
-				$node = $userFolder->get($share->getTarget());
145
-				if ($node === null) {
146
-					throw new NotFoundException();
147
-				}
148
-			} else {
149
-				$node = $nodes[0];
150
-			}
151
-		}
152
-
153
-		$result['path'] = $userFolder->getRelativePath($node->getPath());
154
-		if ($node instanceOf \OCP\Files\Folder) {
155
-			$result['item_type'] = 'folder';
156
-		} else {
157
-			$result['item_type'] = 'file';
158
-		}
159
-		$result['mimetype'] = $node->getMimetype();
160
-		$result['storage_id'] = $node->getStorage()->getId();
161
-		$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
162
-		$result['item_source'] = $node->getId();
163
-		$result['file_source'] = $node->getId();
164
-		$result['file_parent'] = $node->getParent()->getId();
165
-		$result['file_target'] = $share->getTarget();
166
-
167
-		$expiration = $share->getExpirationDate();
168
-		if ($expiration !== null) {
169
-			$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
170
-		}
171
-
172
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
173
-			$sharedWith = $this->userManager->get($share->getSharedWith());
174
-			$result['share_with'] = $share->getSharedWith();
175
-			$result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
176
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
177
-			$group = $this->groupManager->get($share->getSharedWith());
178
-			$result['share_with'] = $share->getSharedWith();
179
-			$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
180
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
181
-
182
-			$result['share_with'] = $share->getPassword();
183
-			$result['share_with_displayname'] = $share->getPassword();
184
-
185
-			$result['token'] = $share->getToken();
186
-			$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
187
-
188
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
189
-			$result['share_with'] = $share->getSharedWith();
190
-			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
191
-			$result['token'] = $share->getToken();
192
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
193
-			$result['share_with'] = $share->getSharedWith();
194
-			$result['password'] = $share->getPassword();
195
-			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
196
-			$result['token'] = $share->getToken();
197
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
198
-			$result['share_with_displayname'] = $share->getSharedWith();
199
-			$result['share_with'] = explode(' ', $share->getSharedWith(), 2)[0];
200
-		}
201
-
202
-
203
-		$result['mail_send'] = $share->getMailSend() ? 1 : 0;
204
-
205
-		return $result;
206
-	}
207
-
208
-	/**
209
-	 * Check if one of the users address books knows the exact property, if
210
-	 * yes we return the full name.
211
-	 *
212
-	 * @param string $query
213
-	 * @param string $property
214
-	 * @return string
215
-	 */
216
-	private function getDisplayNameFromAddressBook($query, $property) {
217
-		// FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered
218
-		$result = \OC::$server->getContactsManager()->search($query, [$property]);
219
-		foreach ($result as $r) {
220
-			foreach($r[$property] as $value) {
221
-				if ($value === $query) {
222
-					return $r['FN'];
223
-				}
224
-			}
225
-		}
226
-
227
-		return $query;
228
-	}
229
-
230
-	/**
231
-	 * Get a specific share by id
232
-	 *
233
-	 * @NoAdminRequired
234
-	 *
235
-	 * @param string $id
236
-	 * @return DataResponse
237
-	 * @throws OCSNotFoundException
238
-	 */
239
-	public function getShare($id) {
240
-		try {
241
-			$share = $this->getShareById($id);
242
-		} catch (ShareNotFound $e) {
243
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
244
-		}
245
-
246
-		if ($this->canAccessShare($share)) {
247
-			try {
248
-				$share = $this->formatShare($share);
249
-				return new DataResponse([$share]);
250
-			} catch (NotFoundException $e) {
251
-				//Fall trough
252
-			}
253
-		}
254
-
255
-		throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
256
-	}
257
-
258
-	/**
259
-	 * Delete a share
260
-	 *
261
-	 * @NoAdminRequired
262
-	 *
263
-	 * @param string $id
264
-	 * @return DataResponse
265
-	 * @throws OCSNotFoundException
266
-	 */
267
-	public function deleteShare($id) {
268
-		try {
269
-			$share = $this->getShareById($id);
270
-		} catch (ShareNotFound $e) {
271
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
272
-		}
273
-
274
-		try {
275
-			$this->lock($share->getNode());
276
-		} catch (LockedException $e) {
277
-			throw new OCSNotFoundException($this->l->t('could not delete share'));
278
-		}
279
-
280
-		if (!$this->canAccessShare($share)) {
281
-			throw new OCSNotFoundException($this->l->t('Could not delete share'));
282
-		}
283
-
284
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP &&
285
-			$share->getShareOwner() !== $this->currentUser &&
286
-			$share->getSharedBy() !== $this->currentUser) {
287
-			$this->shareManager->deleteFromSelf($share, $this->currentUser);
288
-		} else {
289
-			$this->shareManager->deleteShare($share);
290
-		}
291
-
292
-		return new DataResponse();
293
-	}
294
-
295
-	/**
296
-	 * @NoAdminRequired
297
-	 *
298
-	 * @param string $path
299
-	 * @param int $permissions
300
-	 * @param int $shareType
301
-	 * @param string $shareWith
302
-	 * @param string $publicUpload
303
-	 * @param string $password
304
-	 * @param string $expireDate
305
-	 *
306
-	 * @return DataResponse
307
-	 * @throws OCSNotFoundException
308
-	 * @throws OCSForbiddenException
309
-	 * @throws OCSBadRequestException
310
-	 * @throws OCSException
311
-	 *
312
-	 * @suppress PhanUndeclaredClassMethod
313
-	 */
314
-	public function createShare(
315
-		$path = null,
316
-		$permissions = \OCP\Constants::PERMISSION_ALL,
317
-		$shareType = -1,
318
-		$shareWith = null,
319
-		$publicUpload = 'false',
320
-		$password = '',
321
-		$expireDate = ''
322
-	) {
323
-		$share = $this->shareManager->newShare();
324
-
325
-		// Verify path
326
-		if ($path === null) {
327
-			throw new OCSNotFoundException($this->l->t('Please specify a file or folder path'));
328
-		}
329
-
330
-		$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
331
-		try {
332
-			$path = $userFolder->get($path);
333
-		} catch (NotFoundException $e) {
334
-			throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
335
-		}
336
-
337
-		$share->setNode($path);
338
-
339
-		try {
340
-			$this->lock($share->getNode());
341
-		} catch (LockedException $e) {
342
-			throw new OCSNotFoundException($this->l->t('Could not create share'));
343
-		}
344
-
345
-		if ($permissions < 0 || $permissions > \OCP\Constants::PERMISSION_ALL) {
346
-			throw new OCSNotFoundException($this->l->t('invalid permissions'));
347
-		}
348
-
349
-		// Shares always require read permissions
350
-		$permissions |= \OCP\Constants::PERMISSION_READ;
351
-
352
-		if ($path instanceof \OCP\Files\File) {
353
-			// Single file shares should never have delete or create permissions
354
-			$permissions &= ~\OCP\Constants::PERMISSION_DELETE;
355
-			$permissions &= ~\OCP\Constants::PERMISSION_CREATE;
356
-		}
357
-
358
-		/*
55
+    /** @var IManager */
56
+    private $shareManager;
57
+    /** @var IGroupManager */
58
+    private $groupManager;
59
+    /** @var IUserManager */
60
+    private $userManager;
61
+    /** @var IRequest */
62
+    protected $request;
63
+    /** @var IRootFolder */
64
+    private $rootFolder;
65
+    /** @var IURLGenerator */
66
+    private $urlGenerator;
67
+    /** @var string */
68
+    private $currentUser;
69
+    /** @var IL10N */
70
+    private $l;
71
+    /** @var \OCP\Files\Node */
72
+    private $lockedNode;
73
+
74
+    /**
75
+     * Share20OCS constructor.
76
+     *
77
+     * @param string $appName
78
+     * @param IRequest $request
79
+     * @param IManager $shareManager
80
+     * @param IGroupManager $groupManager
81
+     * @param IUserManager $userManager
82
+     * @param IRootFolder $rootFolder
83
+     * @param IURLGenerator $urlGenerator
84
+     * @param string $userId
85
+     * @param IL10N $l10n
86
+     */
87
+    public function __construct(
88
+        $appName,
89
+        IRequest $request,
90
+        IManager $shareManager,
91
+        IGroupManager $groupManager,
92
+        IUserManager $userManager,
93
+        IRootFolder $rootFolder,
94
+        IURLGenerator $urlGenerator,
95
+        $userId,
96
+        IL10N $l10n
97
+    ) {
98
+        parent::__construct($appName, $request);
99
+
100
+        $this->shareManager = $shareManager;
101
+        $this->userManager = $userManager;
102
+        $this->groupManager = $groupManager;
103
+        $this->request = $request;
104
+        $this->rootFolder = $rootFolder;
105
+        $this->urlGenerator = $urlGenerator;
106
+        $this->currentUser = $userId;
107
+        $this->l = $l10n;
108
+    }
109
+
110
+    /**
111
+     * Convert an IShare to an array for OCS output
112
+     *
113
+     * @param \OCP\Share\IShare $share
114
+     * @param Node|null $recipientNode
115
+     * @return array
116
+     * @throws NotFoundException In case the node can't be resolved.
117
+     */
118
+    protected function formatShare(\OCP\Share\IShare $share, Node $recipientNode = null) {
119
+        $sharedBy = $this->userManager->get($share->getSharedBy());
120
+        $shareOwner = $this->userManager->get($share->getShareOwner());
121
+
122
+        $result = [
123
+            'id' => $share->getId(),
124
+            'share_type' => $share->getShareType(),
125
+            'uid_owner' => $share->getSharedBy(),
126
+            'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
127
+            'permissions' => $share->getPermissions(),
128
+            'stime' => $share->getShareTime()->getTimestamp(),
129
+            'parent' => null,
130
+            'expiration' => null,
131
+            'token' => null,
132
+            'uid_file_owner' => $share->getShareOwner(),
133
+            'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
134
+        ];
135
+
136
+        $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
137
+        if ($recipientNode) {
138
+            $node = $recipientNode;
139
+        } else {
140
+            $nodes = $userFolder->getById($share->getNodeId());
141
+
142
+            if (empty($nodes)) {
143
+                // fallback to guessing the path
144
+                $node = $userFolder->get($share->getTarget());
145
+                if ($node === null) {
146
+                    throw new NotFoundException();
147
+                }
148
+            } else {
149
+                $node = $nodes[0];
150
+            }
151
+        }
152
+
153
+        $result['path'] = $userFolder->getRelativePath($node->getPath());
154
+        if ($node instanceOf \OCP\Files\Folder) {
155
+            $result['item_type'] = 'folder';
156
+        } else {
157
+            $result['item_type'] = 'file';
158
+        }
159
+        $result['mimetype'] = $node->getMimetype();
160
+        $result['storage_id'] = $node->getStorage()->getId();
161
+        $result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
162
+        $result['item_source'] = $node->getId();
163
+        $result['file_source'] = $node->getId();
164
+        $result['file_parent'] = $node->getParent()->getId();
165
+        $result['file_target'] = $share->getTarget();
166
+
167
+        $expiration = $share->getExpirationDate();
168
+        if ($expiration !== null) {
169
+            $result['expiration'] = $expiration->format('Y-m-d 00:00:00');
170
+        }
171
+
172
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
173
+            $sharedWith = $this->userManager->get($share->getSharedWith());
174
+            $result['share_with'] = $share->getSharedWith();
175
+            $result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
176
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
177
+            $group = $this->groupManager->get($share->getSharedWith());
178
+            $result['share_with'] = $share->getSharedWith();
179
+            $result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
180
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
181
+
182
+            $result['share_with'] = $share->getPassword();
183
+            $result['share_with_displayname'] = $share->getPassword();
184
+
185
+            $result['token'] = $share->getToken();
186
+            $result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
187
+
188
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
189
+            $result['share_with'] = $share->getSharedWith();
190
+            $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
191
+            $result['token'] = $share->getToken();
192
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
193
+            $result['share_with'] = $share->getSharedWith();
194
+            $result['password'] = $share->getPassword();
195
+            $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
196
+            $result['token'] = $share->getToken();
197
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
198
+            $result['share_with_displayname'] = $share->getSharedWith();
199
+            $result['share_with'] = explode(' ', $share->getSharedWith(), 2)[0];
200
+        }
201
+
202
+
203
+        $result['mail_send'] = $share->getMailSend() ? 1 : 0;
204
+
205
+        return $result;
206
+    }
207
+
208
+    /**
209
+     * Check if one of the users address books knows the exact property, if
210
+     * yes we return the full name.
211
+     *
212
+     * @param string $query
213
+     * @param string $property
214
+     * @return string
215
+     */
216
+    private function getDisplayNameFromAddressBook($query, $property) {
217
+        // FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered
218
+        $result = \OC::$server->getContactsManager()->search($query, [$property]);
219
+        foreach ($result as $r) {
220
+            foreach($r[$property] as $value) {
221
+                if ($value === $query) {
222
+                    return $r['FN'];
223
+                }
224
+            }
225
+        }
226
+
227
+        return $query;
228
+    }
229
+
230
+    /**
231
+     * Get a specific share by id
232
+     *
233
+     * @NoAdminRequired
234
+     *
235
+     * @param string $id
236
+     * @return DataResponse
237
+     * @throws OCSNotFoundException
238
+     */
239
+    public function getShare($id) {
240
+        try {
241
+            $share = $this->getShareById($id);
242
+        } catch (ShareNotFound $e) {
243
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
244
+        }
245
+
246
+        if ($this->canAccessShare($share)) {
247
+            try {
248
+                $share = $this->formatShare($share);
249
+                return new DataResponse([$share]);
250
+            } catch (NotFoundException $e) {
251
+                //Fall trough
252
+            }
253
+        }
254
+
255
+        throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
256
+    }
257
+
258
+    /**
259
+     * Delete a share
260
+     *
261
+     * @NoAdminRequired
262
+     *
263
+     * @param string $id
264
+     * @return DataResponse
265
+     * @throws OCSNotFoundException
266
+     */
267
+    public function deleteShare($id) {
268
+        try {
269
+            $share = $this->getShareById($id);
270
+        } catch (ShareNotFound $e) {
271
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
272
+        }
273
+
274
+        try {
275
+            $this->lock($share->getNode());
276
+        } catch (LockedException $e) {
277
+            throw new OCSNotFoundException($this->l->t('could not delete share'));
278
+        }
279
+
280
+        if (!$this->canAccessShare($share)) {
281
+            throw new OCSNotFoundException($this->l->t('Could not delete share'));
282
+        }
283
+
284
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP &&
285
+            $share->getShareOwner() !== $this->currentUser &&
286
+            $share->getSharedBy() !== $this->currentUser) {
287
+            $this->shareManager->deleteFromSelf($share, $this->currentUser);
288
+        } else {
289
+            $this->shareManager->deleteShare($share);
290
+        }
291
+
292
+        return new DataResponse();
293
+    }
294
+
295
+    /**
296
+     * @NoAdminRequired
297
+     *
298
+     * @param string $path
299
+     * @param int $permissions
300
+     * @param int $shareType
301
+     * @param string $shareWith
302
+     * @param string $publicUpload
303
+     * @param string $password
304
+     * @param string $expireDate
305
+     *
306
+     * @return DataResponse
307
+     * @throws OCSNotFoundException
308
+     * @throws OCSForbiddenException
309
+     * @throws OCSBadRequestException
310
+     * @throws OCSException
311
+     *
312
+     * @suppress PhanUndeclaredClassMethod
313
+     */
314
+    public function createShare(
315
+        $path = null,
316
+        $permissions = \OCP\Constants::PERMISSION_ALL,
317
+        $shareType = -1,
318
+        $shareWith = null,
319
+        $publicUpload = 'false',
320
+        $password = '',
321
+        $expireDate = ''
322
+    ) {
323
+        $share = $this->shareManager->newShare();
324
+
325
+        // Verify path
326
+        if ($path === null) {
327
+            throw new OCSNotFoundException($this->l->t('Please specify a file or folder path'));
328
+        }
329
+
330
+        $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
331
+        try {
332
+            $path = $userFolder->get($path);
333
+        } catch (NotFoundException $e) {
334
+            throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
335
+        }
336
+
337
+        $share->setNode($path);
338
+
339
+        try {
340
+            $this->lock($share->getNode());
341
+        } catch (LockedException $e) {
342
+            throw new OCSNotFoundException($this->l->t('Could not create share'));
343
+        }
344
+
345
+        if ($permissions < 0 || $permissions > \OCP\Constants::PERMISSION_ALL) {
346
+            throw new OCSNotFoundException($this->l->t('invalid permissions'));
347
+        }
348
+
349
+        // Shares always require read permissions
350
+        $permissions |= \OCP\Constants::PERMISSION_READ;
351
+
352
+        if ($path instanceof \OCP\Files\File) {
353
+            // Single file shares should never have delete or create permissions
354
+            $permissions &= ~\OCP\Constants::PERMISSION_DELETE;
355
+            $permissions &= ~\OCP\Constants::PERMISSION_CREATE;
356
+        }
357
+
358
+        /*
359 359
 		 * Hack for https://github.com/owncloud/core/issues/22587
360 360
 		 * We check the permissions via webdav. But the permissions of the mount point
361 361
 		 * do not equal the share permissions. Here we fix that for federated mounts.
362 362
 		 */
363
-		if ($path->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
364
-			$permissions &= ~($permissions & ~$path->getPermissions());
365
-		}
366
-
367
-		if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
368
-			// Valid user is required to share
369
-			if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
370
-				throw new OCSNotFoundException($this->l->t('Please specify a valid user'));
371
-			}
372
-			$share->setSharedWith($shareWith);
373
-			$share->setPermissions($permissions);
374
-		} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
375
-			if (!$this->shareManager->allowGroupSharing()) {
376
-				throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
377
-			}
378
-
379
-			// Valid group is required to share
380
-			if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
381
-				throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
382
-			}
383
-			$share->setSharedWith($shareWith);
384
-			$share->setPermissions($permissions);
385
-		} else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
386
-			//Can we even share links?
387
-			if (!$this->shareManager->shareApiAllowLinks()) {
388
-				throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
389
-			}
390
-
391
-			/*
363
+        if ($path->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
364
+            $permissions &= ~($permissions & ~$path->getPermissions());
365
+        }
366
+
367
+        if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
368
+            // Valid user is required to share
369
+            if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
370
+                throw new OCSNotFoundException($this->l->t('Please specify a valid user'));
371
+            }
372
+            $share->setSharedWith($shareWith);
373
+            $share->setPermissions($permissions);
374
+        } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
375
+            if (!$this->shareManager->allowGroupSharing()) {
376
+                throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
377
+            }
378
+
379
+            // Valid group is required to share
380
+            if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
381
+                throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
382
+            }
383
+            $share->setSharedWith($shareWith);
384
+            $share->setPermissions($permissions);
385
+        } else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
386
+            //Can we even share links?
387
+            if (!$this->shareManager->shareApiAllowLinks()) {
388
+                throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
389
+            }
390
+
391
+            /*
392 392
 			 * For now we only allow 1 link share.
393 393
 			 * Return the existing link share if this is a duplicate
394 394
 			 */
395
-			$existingShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, false, 1, 0);
396
-			if (!empty($existingShares)) {
397
-				return new DataResponse($this->formatShare($existingShares[0]));
398
-			}
399
-
400
-			if ($publicUpload === 'true') {
401
-				// Check if public upload is allowed
402
-				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
403
-					throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
404
-				}
405
-
406
-				// Public upload can only be set for folders
407
-				if ($path instanceof \OCP\Files\File) {
408
-					throw new OCSNotFoundException($this->l->t('Public upload is only possible for publicly shared folders'));
409
-				}
410
-
411
-				$share->setPermissions(
412
-					\OCP\Constants::PERMISSION_READ |
413
-					\OCP\Constants::PERMISSION_CREATE |
414
-					\OCP\Constants::PERMISSION_UPDATE |
415
-					\OCP\Constants::PERMISSION_DELETE
416
-				);
417
-			} else {
418
-				$share->setPermissions(\OCP\Constants::PERMISSION_READ);
419
-			}
420
-
421
-			// Set password
422
-			if ($password !== '') {
423
-				$share->setPassword($password);
424
-			}
425
-
426
-			//Expire date
427
-			if ($expireDate !== '') {
428
-				try {
429
-					$expireDate = $this->parseDate($expireDate);
430
-					$share->setExpirationDate($expireDate);
431
-				} catch (\Exception $e) {
432
-					throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD'));
433
-				}
434
-			}
435
-
436
-		} else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
437
-			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
438
-				throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not allow shares from type %s', [$path->getPath(), $shareType]));
439
-			}
440
-
441
-			$share->setSharedWith($shareWith);
442
-			$share->setPermissions($permissions);
443
-		} else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) {
444
-			if ($share->getNodeType() === 'file') {
445
-				$share->setPermissions(\OCP\Constants::PERMISSION_READ);
446
-			} else {
447
-				$share->setPermissions(
448
-					\OCP\Constants::PERMISSION_READ |
449
-					\OCP\Constants::PERMISSION_CREATE |
450
-					\OCP\Constants::PERMISSION_UPDATE |
451
-					\OCP\Constants::PERMISSION_DELETE);
452
-			}
453
-			$share->setSharedWith($shareWith);
454
-		} else if ($shareType === \OCP\Share::SHARE_TYPE_CIRCLE) {
455
-			if (!\OC::$server->getAppManager()->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
456
-				throw new OCSNotFoundException($this->l->t('You cannot share to a Circle if the app is not enabled'));
457
-			}
458
-
459
-			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($shareWith);
460
-
461
-			// Valid circle is required to share
462
-			if ($circle === null) {
463
-				throw new OCSNotFoundException($this->l->t('Please specify a valid circle'));
464
-			}
465
-			$share->setSharedWith($shareWith);
466
-			$share->setPermissions($permissions);
467
-		} else {
468
-			throw new OCSBadRequestException($this->l->t('Unknown share type'));
469
-		}
470
-
471
-		$share->setShareType($shareType);
472
-		$share->setSharedBy($this->currentUser);
473
-
474
-		try {
475
-			$share = $this->shareManager->createShare($share);
476
-		} catch (GenericShareException $e) {
477
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
478
-			throw new OCSException($e->getHint(), $code);
479
-		} catch (\Exception $e) {
480
-			throw new OCSForbiddenException($e->getMessage());
481
-		}
482
-
483
-		$output = $this->formatShare($share);
484
-
485
-		return new DataResponse($output);
486
-	}
487
-
488
-	/**
489
-	 * @param \OCP\Files\File|\OCP\Files\Folder $node
490
-	 * @param boolean $includeTags
491
-	 * @return DataResponse
492
-	 */
493
-	private function getSharedWithMe($node = null, $includeTags) {
494
-
495
-		$userShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, -1, 0);
496
-		$groupShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0);
497
-		$circleShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_CIRCLE, $node, -1, 0);
498
-
499
-		$shares = array_merge($userShares, $groupShares, $circleShares);
500
-
501
-		$shares = array_filter($shares, function (IShare $share) {
502
-			return $share->getShareOwner() !== $this->currentUser;
503
-		});
504
-
505
-		$formatted = [];
506
-		foreach ($shares as $share) {
507
-			if ($this->canAccessShare($share)) {
508
-				try {
509
-					$formatted[] = $this->formatShare($share);
510
-				} catch (NotFoundException $e) {
511
-					// Ignore this share
512
-				}
513
-			}
514
-		}
515
-
516
-		if ($includeTags) {
517
-			$formatted = Helper::populateTags($formatted, 'file_source');
518
-		}
519
-
520
-		return new DataResponse($formatted);
521
-	}
522
-
523
-	/**
524
-	 * @param \OCP\Files\Folder $folder
525
-	 * @return DataResponse
526
-	 * @throws OCSBadRequestException
527
-	 */
528
-	private function getSharesInDir($folder) {
529
-		if (!($folder instanceof \OCP\Files\Folder)) {
530
-			throw new OCSBadRequestException($this->l->t('Not a directory'));
531
-		}
532
-
533
-		$nodes = $folder->getDirectoryListing();
534
-		/** @var \OCP\Share\IShare[] $shares */
535
-		$shares = [];
536
-		foreach ($nodes as $node) {
537
-			$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0));
538
-			$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0));
539
-			$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0));
540
-			if($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
541
-				$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $node, false, -1, 0));
542
-			}
543
-			if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
544
-				$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0));
545
-			}
546
-		}
547
-
548
-		$formatted = [];
549
-		foreach ($shares as $share) {
550
-			try {
551
-				$formatted[] = $this->formatShare($share);
552
-			} catch (NotFoundException $e) {
553
-				//Ignore this share
554
-			}
555
-		}
556
-
557
-		return new DataResponse($formatted);
558
-	}
559
-
560
-	/**
561
-	 * The getShares function.
562
-	 *
563
-	 * @NoAdminRequired
564
-	 *
565
-	 * @param string $shared_with_me
566
-	 * @param string $reshares
567
-	 * @param string $subfiles
568
-	 * @param string $path
569
-	 *
570
-	 * - Get shares by the current user
571
-	 * - Get shares by the current user and reshares (?reshares=true)
572
-	 * - Get shares with the current user (?shared_with_me=true)
573
-	 * - Get shares for a specific path (?path=...)
574
-	 * - Get all shares in a folder (?subfiles=true&path=..)
575
-	 *
576
-	 * @return DataResponse
577
-	 * @throws OCSNotFoundException
578
-	 */
579
-	public function getShares(
580
-		$shared_with_me = 'false',
581
-		$reshares = 'false',
582
-		$subfiles = 'false',
583
-		$path = null,
584
-		$include_tags = 'false'
585
-	) {
586
-
587
-		if ($path !== null) {
588
-			$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
589
-			try {
590
-				$path = $userFolder->get($path);
591
-				$this->lock($path);
592
-			} catch (\OCP\Files\NotFoundException $e) {
593
-				throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
594
-			} catch (LockedException $e) {
595
-				throw new OCSNotFoundException($this->l->t('Could not lock path'));
596
-			}
597
-		}
598
-
599
-		if ($shared_with_me === 'true') {
600
-			$result = $this->getSharedWithMe($path, $include_tags);
601
-			return $result;
602
-		}
603
-
604
-		if ($subfiles === 'true') {
605
-			$result = $this->getSharesInDir($path);
606
-			return $result;
607
-		}
608
-
609
-		if ($reshares === 'true') {
610
-			$reshares = true;
611
-		} else {
612
-			$reshares = false;
613
-		}
614
-
615
-		// Get all shares
616
-		$userShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0);
617
-		$groupShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0);
618
-		$linkShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0);
619
-		if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
620
-			$mailShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $path, $reshares, -1, 0);
621
-		} else {
622
-			$mailShares = [];
623
-		}
624
-		if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) {
625
-			$circleShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_CIRCLE, $path, $reshares, -1, 0);
626
-		} else {
627
-			$circleShares = [];
628
-		}
629
-
630
-		$shares = array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares);
631
-
632
-		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
633
-			$federatedShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0);
634
-			$shares = array_merge($shares, $federatedShares);
635
-		}
636
-
637
-		$formatted = [];
638
-		foreach ($shares as $share) {
639
-			try {
640
-				$formatted[] = $this->formatShare($share, $path);
641
-			} catch (NotFoundException $e) {
642
-				//Ignore share
643
-			}
644
-		}
645
-
646
-		if ($include_tags) {
647
-			$formatted = Helper::populateTags($formatted, 'file_source');
648
-		}
649
-
650
-		return new DataResponse($formatted);
651
-	}
652
-
653
-	/**
654
-	 * @NoAdminRequired
655
-	 *
656
-	 * @param int $id
657
-	 * @param int $permissions
658
-	 * @param string $password
659
-	 * @param string $publicUpload
660
-	 * @param string $expireDate
661
-	 * @return DataResponse
662
-	 * @throws OCSNotFoundException
663
-	 * @throws OCSBadRequestException
664
-	 * @throws OCSForbiddenException
665
-	 */
666
-	public function updateShare(
667
-		$id,
668
-		$permissions = null,
669
-		$password = null,
670
-		$publicUpload = null,
671
-		$expireDate = null
672
-	) {
673
-		try {
674
-			$share = $this->getShareById($id);
675
-		} catch (ShareNotFound $e) {
676
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
677
-		}
678
-
679
-		$this->lock($share->getNode());
680
-
681
-		if (!$this->canAccessShare($share, false)) {
682
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
683
-		}
684
-
685
-		if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null) {
686
-			throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
687
-		}
688
-
689
-		/*
395
+            $existingShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, false, 1, 0);
396
+            if (!empty($existingShares)) {
397
+                return new DataResponse($this->formatShare($existingShares[0]));
398
+            }
399
+
400
+            if ($publicUpload === 'true') {
401
+                // Check if public upload is allowed
402
+                if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
403
+                    throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
404
+                }
405
+
406
+                // Public upload can only be set for folders
407
+                if ($path instanceof \OCP\Files\File) {
408
+                    throw new OCSNotFoundException($this->l->t('Public upload is only possible for publicly shared folders'));
409
+                }
410
+
411
+                $share->setPermissions(
412
+                    \OCP\Constants::PERMISSION_READ |
413
+                    \OCP\Constants::PERMISSION_CREATE |
414
+                    \OCP\Constants::PERMISSION_UPDATE |
415
+                    \OCP\Constants::PERMISSION_DELETE
416
+                );
417
+            } else {
418
+                $share->setPermissions(\OCP\Constants::PERMISSION_READ);
419
+            }
420
+
421
+            // Set password
422
+            if ($password !== '') {
423
+                $share->setPassword($password);
424
+            }
425
+
426
+            //Expire date
427
+            if ($expireDate !== '') {
428
+                try {
429
+                    $expireDate = $this->parseDate($expireDate);
430
+                    $share->setExpirationDate($expireDate);
431
+                } catch (\Exception $e) {
432
+                    throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD'));
433
+                }
434
+            }
435
+
436
+        } else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
437
+            if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
438
+                throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not allow shares from type %s', [$path->getPath(), $shareType]));
439
+            }
440
+
441
+            $share->setSharedWith($shareWith);
442
+            $share->setPermissions($permissions);
443
+        } else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) {
444
+            if ($share->getNodeType() === 'file') {
445
+                $share->setPermissions(\OCP\Constants::PERMISSION_READ);
446
+            } else {
447
+                $share->setPermissions(
448
+                    \OCP\Constants::PERMISSION_READ |
449
+                    \OCP\Constants::PERMISSION_CREATE |
450
+                    \OCP\Constants::PERMISSION_UPDATE |
451
+                    \OCP\Constants::PERMISSION_DELETE);
452
+            }
453
+            $share->setSharedWith($shareWith);
454
+        } else if ($shareType === \OCP\Share::SHARE_TYPE_CIRCLE) {
455
+            if (!\OC::$server->getAppManager()->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
456
+                throw new OCSNotFoundException($this->l->t('You cannot share to a Circle if the app is not enabled'));
457
+            }
458
+
459
+            $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($shareWith);
460
+
461
+            // Valid circle is required to share
462
+            if ($circle === null) {
463
+                throw new OCSNotFoundException($this->l->t('Please specify a valid circle'));
464
+            }
465
+            $share->setSharedWith($shareWith);
466
+            $share->setPermissions($permissions);
467
+        } else {
468
+            throw new OCSBadRequestException($this->l->t('Unknown share type'));
469
+        }
470
+
471
+        $share->setShareType($shareType);
472
+        $share->setSharedBy($this->currentUser);
473
+
474
+        try {
475
+            $share = $this->shareManager->createShare($share);
476
+        } catch (GenericShareException $e) {
477
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
478
+            throw new OCSException($e->getHint(), $code);
479
+        } catch (\Exception $e) {
480
+            throw new OCSForbiddenException($e->getMessage());
481
+        }
482
+
483
+        $output = $this->formatShare($share);
484
+
485
+        return new DataResponse($output);
486
+    }
487
+
488
+    /**
489
+     * @param \OCP\Files\File|\OCP\Files\Folder $node
490
+     * @param boolean $includeTags
491
+     * @return DataResponse
492
+     */
493
+    private function getSharedWithMe($node = null, $includeTags) {
494
+
495
+        $userShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, -1, 0);
496
+        $groupShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0);
497
+        $circleShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_CIRCLE, $node, -1, 0);
498
+
499
+        $shares = array_merge($userShares, $groupShares, $circleShares);
500
+
501
+        $shares = array_filter($shares, function (IShare $share) {
502
+            return $share->getShareOwner() !== $this->currentUser;
503
+        });
504
+
505
+        $formatted = [];
506
+        foreach ($shares as $share) {
507
+            if ($this->canAccessShare($share)) {
508
+                try {
509
+                    $formatted[] = $this->formatShare($share);
510
+                } catch (NotFoundException $e) {
511
+                    // Ignore this share
512
+                }
513
+            }
514
+        }
515
+
516
+        if ($includeTags) {
517
+            $formatted = Helper::populateTags($formatted, 'file_source');
518
+        }
519
+
520
+        return new DataResponse($formatted);
521
+    }
522
+
523
+    /**
524
+     * @param \OCP\Files\Folder $folder
525
+     * @return DataResponse
526
+     * @throws OCSBadRequestException
527
+     */
528
+    private function getSharesInDir($folder) {
529
+        if (!($folder instanceof \OCP\Files\Folder)) {
530
+            throw new OCSBadRequestException($this->l->t('Not a directory'));
531
+        }
532
+
533
+        $nodes = $folder->getDirectoryListing();
534
+        /** @var \OCP\Share\IShare[] $shares */
535
+        $shares = [];
536
+        foreach ($nodes as $node) {
537
+            $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0));
538
+            $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0));
539
+            $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0));
540
+            if($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
541
+                $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $node, false, -1, 0));
542
+            }
543
+            if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
544
+                $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0));
545
+            }
546
+        }
547
+
548
+        $formatted = [];
549
+        foreach ($shares as $share) {
550
+            try {
551
+                $formatted[] = $this->formatShare($share);
552
+            } catch (NotFoundException $e) {
553
+                //Ignore this share
554
+            }
555
+        }
556
+
557
+        return new DataResponse($formatted);
558
+    }
559
+
560
+    /**
561
+     * The getShares function.
562
+     *
563
+     * @NoAdminRequired
564
+     *
565
+     * @param string $shared_with_me
566
+     * @param string $reshares
567
+     * @param string $subfiles
568
+     * @param string $path
569
+     *
570
+     * - Get shares by the current user
571
+     * - Get shares by the current user and reshares (?reshares=true)
572
+     * - Get shares with the current user (?shared_with_me=true)
573
+     * - Get shares for a specific path (?path=...)
574
+     * - Get all shares in a folder (?subfiles=true&path=..)
575
+     *
576
+     * @return DataResponse
577
+     * @throws OCSNotFoundException
578
+     */
579
+    public function getShares(
580
+        $shared_with_me = 'false',
581
+        $reshares = 'false',
582
+        $subfiles = 'false',
583
+        $path = null,
584
+        $include_tags = 'false'
585
+    ) {
586
+
587
+        if ($path !== null) {
588
+            $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
589
+            try {
590
+                $path = $userFolder->get($path);
591
+                $this->lock($path);
592
+            } catch (\OCP\Files\NotFoundException $e) {
593
+                throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
594
+            } catch (LockedException $e) {
595
+                throw new OCSNotFoundException($this->l->t('Could not lock path'));
596
+            }
597
+        }
598
+
599
+        if ($shared_with_me === 'true') {
600
+            $result = $this->getSharedWithMe($path, $include_tags);
601
+            return $result;
602
+        }
603
+
604
+        if ($subfiles === 'true') {
605
+            $result = $this->getSharesInDir($path);
606
+            return $result;
607
+        }
608
+
609
+        if ($reshares === 'true') {
610
+            $reshares = true;
611
+        } else {
612
+            $reshares = false;
613
+        }
614
+
615
+        // Get all shares
616
+        $userShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0);
617
+        $groupShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0);
618
+        $linkShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0);
619
+        if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
620
+            $mailShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $path, $reshares, -1, 0);
621
+        } else {
622
+            $mailShares = [];
623
+        }
624
+        if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) {
625
+            $circleShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_CIRCLE, $path, $reshares, -1, 0);
626
+        } else {
627
+            $circleShares = [];
628
+        }
629
+
630
+        $shares = array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares);
631
+
632
+        if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
633
+            $federatedShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0);
634
+            $shares = array_merge($shares, $federatedShares);
635
+        }
636
+
637
+        $formatted = [];
638
+        foreach ($shares as $share) {
639
+            try {
640
+                $formatted[] = $this->formatShare($share, $path);
641
+            } catch (NotFoundException $e) {
642
+                //Ignore share
643
+            }
644
+        }
645
+
646
+        if ($include_tags) {
647
+            $formatted = Helper::populateTags($formatted, 'file_source');
648
+        }
649
+
650
+        return new DataResponse($formatted);
651
+    }
652
+
653
+    /**
654
+     * @NoAdminRequired
655
+     *
656
+     * @param int $id
657
+     * @param int $permissions
658
+     * @param string $password
659
+     * @param string $publicUpload
660
+     * @param string $expireDate
661
+     * @return DataResponse
662
+     * @throws OCSNotFoundException
663
+     * @throws OCSBadRequestException
664
+     * @throws OCSForbiddenException
665
+     */
666
+    public function updateShare(
667
+        $id,
668
+        $permissions = null,
669
+        $password = null,
670
+        $publicUpload = null,
671
+        $expireDate = null
672
+    ) {
673
+        try {
674
+            $share = $this->getShareById($id);
675
+        } catch (ShareNotFound $e) {
676
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
677
+        }
678
+
679
+        $this->lock($share->getNode());
680
+
681
+        if (!$this->canAccessShare($share, false)) {
682
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
683
+        }
684
+
685
+        if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null) {
686
+            throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
687
+        }
688
+
689
+        /*
690 690
 		 * expirationdate, password and publicUpload only make sense for link shares
691 691
 		 */
692
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
693
-
694
-			$newPermissions = null;
695
-			if ($publicUpload === 'true') {
696
-				$newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
697
-			} else if ($publicUpload === 'false') {
698
-				$newPermissions = \OCP\Constants::PERMISSION_READ;
699
-			}
700
-
701
-			if ($permissions !== null) {
702
-				$newPermissions = (int)$permissions;
703
-			}
704
-
705
-			if ($newPermissions !== null &&
706
-				!in_array($newPermissions, [
707
-					\OCP\Constants::PERMISSION_READ,
708
-					\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE, // legacy
709
-					\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE, // correct
710
-					\OCP\Constants::PERMISSION_CREATE, // hidden file list
711
-					\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE, // allow to edit single files
712
-				])
713
-			) {
714
-				throw new OCSBadRequestException($this->l->t('Can\'t change permissions for public share links'));
715
-			}
716
-
717
-			if (
718
-				// legacy
719
-				$newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) ||
720
-				// correct
721
-				$newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)
722
-			) {
723
-				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
724
-					throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
725
-				}
726
-
727
-				if (!($share->getNode() instanceof \OCP\Files\Folder)) {
728
-					throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
729
-				}
730
-
731
-				// normalize to correct public upload permissions
732
-				$newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
733
-			}
734
-
735
-			if ($newPermissions !== null) {
736
-				$share->setPermissions($newPermissions);
737
-				$permissions = $newPermissions;
738
-			}
739
-
740
-			if ($expireDate === '') {
741
-				$share->setExpirationDate(null);
742
-			} else if ($expireDate !== null) {
743
-				try {
744
-					$expireDate = $this->parseDate($expireDate);
745
-				} catch (\Exception $e) {
746
-					throw new OCSBadRequestException($e->getMessage());
747
-				}
748
-				$share->setExpirationDate($expireDate);
749
-			}
750
-
751
-			if ($password === '') {
752
-				$share->setPassword(null);
753
-			} else if ($password !== null) {
754
-				$share->setPassword($password);
755
-			}
756
-
757
-		} else {
758
-			if ($permissions !== null) {
759
-				$permissions = (int)$permissions;
760
-				$share->setPermissions($permissions);
761
-			}
762
-
763
-			if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
764
-				if ($password === '') {
765
-					$share->setPassword(null);
766
-				} else if ($password !== null) {
767
-					$share->setPassword($password);
768
-				}
769
-			}
770
-
771
-			if ($expireDate === '') {
772
-				$share->setExpirationDate(null);
773
-			} else if ($expireDate !== null) {
774
-				try {
775
-					$expireDate = $this->parseDate($expireDate);
776
-				} catch (\Exception $e) {
777
-					throw new OCSBadRequestException($e->getMessage());
778
-				}
779
-				$share->setExpirationDate($expireDate);
780
-			}
781
-
782
-		}
783
-
784
-		if ($permissions !== null && $share->getShareOwner() !== $this->currentUser) {
785
-			/* Check if this is an incomming share */
786
-			$incomingShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $share->getNode(), -1, 0);
787
-			$incomingShares = array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0));
788
-
789
-			/** @var \OCP\Share\IShare[] $incomingShares */
790
-			if (!empty($incomingShares)) {
791
-				$maxPermissions = 0;
792
-				foreach ($incomingShares as $incomingShare) {
793
-					$maxPermissions |= $incomingShare->getPermissions();
794
-				}
795
-
796
-				if ($share->getPermissions() & ~$maxPermissions) {
797
-					throw new OCSNotFoundException($this->l->t('Cannot increase permissions'));
798
-				}
799
-			}
800
-		}
801
-
802
-
803
-		try {
804
-			$share = $this->shareManager->updateShare($share);
805
-		} catch (\Exception $e) {
806
-			throw new OCSBadRequestException($e->getMessage());
807
-		}
808
-
809
-		return new DataResponse($this->formatShare($share));
810
-	}
811
-
812
-	/**
813
-	 * @param \OCP\Share\IShare $share
814
-	 * @return bool
815
-	 */
816
-	protected function canAccessShare(\OCP\Share\IShare $share, $checkGroups = true) {
817
-		// A file with permissions 0 can't be accessed by us. So Don't show it
818
-		if ($share->getPermissions() === 0) {
819
-			return false;
820
-		}
821
-
822
-		// Owner of the file and the sharer of the file can always get share
823
-		if ($share->getShareOwner() === $this->currentUser ||
824
-			$share->getSharedBy() === $this->currentUser
825
-		) {
826
-			return true;
827
-		}
828
-
829
-		// If the share is shared with you (or a group you are a member of)
830
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
831
-			$share->getSharedWith() === $this->currentUser
832
-		) {
833
-			return true;
834
-		}
835
-
836
-		if ($checkGroups && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
837
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
838
-			$user = $this->userManager->get($this->currentUser);
839
-			if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
840
-				return true;
841
-			}
842
-		}
843
-
844
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
845
-			// TODO: have a sanity check like above?
846
-			return true;
847
-		}
848
-
849
-		return false;
850
-	}
851
-
852
-	/**
853
-	 * Make sure that the passed date is valid ISO 8601
854
-	 * So YYYY-MM-DD
855
-	 * If not throw an exception
856
-	 *
857
-	 * @param string $expireDate
858
-	 *
859
-	 * @throws \Exception
860
-	 * @return \DateTime
861
-	 */
862
-	private function parseDate($expireDate) {
863
-		try {
864
-			$date = new \DateTime($expireDate);
865
-		} catch (\Exception $e) {
866
-			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
867
-		}
868
-
869
-		if ($date === false) {
870
-			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
871
-		}
872
-
873
-		$date->setTime(0, 0, 0);
874
-
875
-		return $date;
876
-	}
877
-
878
-	/**
879
-	 * Since we have multiple providers but the OCS Share API v1 does
880
-	 * not support this we need to check all backends.
881
-	 *
882
-	 * @param string $id
883
-	 * @return \OCP\Share\IShare
884
-	 * @throws ShareNotFound
885
-	 */
886
-	private function getShareById($id) {
887
-		$share = null;
888
-
889
-		// First check if it is an internal share.
890
-		try {
891
-			$share = $this->shareManager->getShareById('ocinternal:' . $id);
892
-			return $share;
893
-		} catch (ShareNotFound $e) {
894
-			// Do nothing, just try the other share type
895
-		}
896
-
897
-
898
-		try {
899
-			if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) {
900
-				$share = $this->shareManager->getShareById('ocCircleShare:' . $id);
901
-				return $share;
902
-			}
903
-		} catch (ShareNotFound $e) {
904
-			// Do nothing, just try the other share type
905
-		}
906
-
907
-		try {
908
-			if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
909
-				$share = $this->shareManager->getShareById('ocMailShare:' . $id);
910
-				return $share;
911
-			}
912
-		} catch (ShareNotFound $e) {
913
-			// Do nothing, just try the other share type
914
-		}
915
-
916
-		if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
917
-			throw new ShareNotFound();
918
-		}
919
-		$share = $this->shareManager->getShareById('ocFederatedSharing:' . $id);
920
-
921
-		return $share;
922
-	}
923
-
924
-	/**
925
-	 * Lock a Node
926
-	 *
927
-	 * @param \OCP\Files\Node $node
928
-	 */
929
-	private function lock(\OCP\Files\Node $node) {
930
-		$node->lock(ILockingProvider::LOCK_SHARED);
931
-		$this->lockedNode = $node;
932
-	}
933
-
934
-	/**
935
-	 * Cleanup the remaining locks
936
-	 */
937
-	public function cleanup() {
938
-		if ($this->lockedNode !== null) {
939
-			$this->lockedNode->unlock(ILockingProvider::LOCK_SHARED);
940
-		}
941
-	}
692
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
693
+
694
+            $newPermissions = null;
695
+            if ($publicUpload === 'true') {
696
+                $newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
697
+            } else if ($publicUpload === 'false') {
698
+                $newPermissions = \OCP\Constants::PERMISSION_READ;
699
+            }
700
+
701
+            if ($permissions !== null) {
702
+                $newPermissions = (int)$permissions;
703
+            }
704
+
705
+            if ($newPermissions !== null &&
706
+                !in_array($newPermissions, [
707
+                    \OCP\Constants::PERMISSION_READ,
708
+                    \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE, // legacy
709
+                    \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE, // correct
710
+                    \OCP\Constants::PERMISSION_CREATE, // hidden file list
711
+                    \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE, // allow to edit single files
712
+                ])
713
+            ) {
714
+                throw new OCSBadRequestException($this->l->t('Can\'t change permissions for public share links'));
715
+            }
716
+
717
+            if (
718
+                // legacy
719
+                $newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) ||
720
+                // correct
721
+                $newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)
722
+            ) {
723
+                if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
724
+                    throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
725
+                }
726
+
727
+                if (!($share->getNode() instanceof \OCP\Files\Folder)) {
728
+                    throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
729
+                }
730
+
731
+                // normalize to correct public upload permissions
732
+                $newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
733
+            }
734
+
735
+            if ($newPermissions !== null) {
736
+                $share->setPermissions($newPermissions);
737
+                $permissions = $newPermissions;
738
+            }
739
+
740
+            if ($expireDate === '') {
741
+                $share->setExpirationDate(null);
742
+            } else if ($expireDate !== null) {
743
+                try {
744
+                    $expireDate = $this->parseDate($expireDate);
745
+                } catch (\Exception $e) {
746
+                    throw new OCSBadRequestException($e->getMessage());
747
+                }
748
+                $share->setExpirationDate($expireDate);
749
+            }
750
+
751
+            if ($password === '') {
752
+                $share->setPassword(null);
753
+            } else if ($password !== null) {
754
+                $share->setPassword($password);
755
+            }
756
+
757
+        } else {
758
+            if ($permissions !== null) {
759
+                $permissions = (int)$permissions;
760
+                $share->setPermissions($permissions);
761
+            }
762
+
763
+            if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
764
+                if ($password === '') {
765
+                    $share->setPassword(null);
766
+                } else if ($password !== null) {
767
+                    $share->setPassword($password);
768
+                }
769
+            }
770
+
771
+            if ($expireDate === '') {
772
+                $share->setExpirationDate(null);
773
+            } else if ($expireDate !== null) {
774
+                try {
775
+                    $expireDate = $this->parseDate($expireDate);
776
+                } catch (\Exception $e) {
777
+                    throw new OCSBadRequestException($e->getMessage());
778
+                }
779
+                $share->setExpirationDate($expireDate);
780
+            }
781
+
782
+        }
783
+
784
+        if ($permissions !== null && $share->getShareOwner() !== $this->currentUser) {
785
+            /* Check if this is an incomming share */
786
+            $incomingShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $share->getNode(), -1, 0);
787
+            $incomingShares = array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0));
788
+
789
+            /** @var \OCP\Share\IShare[] $incomingShares */
790
+            if (!empty($incomingShares)) {
791
+                $maxPermissions = 0;
792
+                foreach ($incomingShares as $incomingShare) {
793
+                    $maxPermissions |= $incomingShare->getPermissions();
794
+                }
795
+
796
+                if ($share->getPermissions() & ~$maxPermissions) {
797
+                    throw new OCSNotFoundException($this->l->t('Cannot increase permissions'));
798
+                }
799
+            }
800
+        }
801
+
802
+
803
+        try {
804
+            $share = $this->shareManager->updateShare($share);
805
+        } catch (\Exception $e) {
806
+            throw new OCSBadRequestException($e->getMessage());
807
+        }
808
+
809
+        return new DataResponse($this->formatShare($share));
810
+    }
811
+
812
+    /**
813
+     * @param \OCP\Share\IShare $share
814
+     * @return bool
815
+     */
816
+    protected function canAccessShare(\OCP\Share\IShare $share, $checkGroups = true) {
817
+        // A file with permissions 0 can't be accessed by us. So Don't show it
818
+        if ($share->getPermissions() === 0) {
819
+            return false;
820
+        }
821
+
822
+        // Owner of the file and the sharer of the file can always get share
823
+        if ($share->getShareOwner() === $this->currentUser ||
824
+            $share->getSharedBy() === $this->currentUser
825
+        ) {
826
+            return true;
827
+        }
828
+
829
+        // If the share is shared with you (or a group you are a member of)
830
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
831
+            $share->getSharedWith() === $this->currentUser
832
+        ) {
833
+            return true;
834
+        }
835
+
836
+        if ($checkGroups && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
837
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
838
+            $user = $this->userManager->get($this->currentUser);
839
+            if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
840
+                return true;
841
+            }
842
+        }
843
+
844
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
845
+            // TODO: have a sanity check like above?
846
+            return true;
847
+        }
848
+
849
+        return false;
850
+    }
851
+
852
+    /**
853
+     * Make sure that the passed date is valid ISO 8601
854
+     * So YYYY-MM-DD
855
+     * If not throw an exception
856
+     *
857
+     * @param string $expireDate
858
+     *
859
+     * @throws \Exception
860
+     * @return \DateTime
861
+     */
862
+    private function parseDate($expireDate) {
863
+        try {
864
+            $date = new \DateTime($expireDate);
865
+        } catch (\Exception $e) {
866
+            throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
867
+        }
868
+
869
+        if ($date === false) {
870
+            throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
871
+        }
872
+
873
+        $date->setTime(0, 0, 0);
874
+
875
+        return $date;
876
+    }
877
+
878
+    /**
879
+     * Since we have multiple providers but the OCS Share API v1 does
880
+     * not support this we need to check all backends.
881
+     *
882
+     * @param string $id
883
+     * @return \OCP\Share\IShare
884
+     * @throws ShareNotFound
885
+     */
886
+    private function getShareById($id) {
887
+        $share = null;
888
+
889
+        // First check if it is an internal share.
890
+        try {
891
+            $share = $this->shareManager->getShareById('ocinternal:' . $id);
892
+            return $share;
893
+        } catch (ShareNotFound $e) {
894
+            // Do nothing, just try the other share type
895
+        }
896
+
897
+
898
+        try {
899
+            if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) {
900
+                $share = $this->shareManager->getShareById('ocCircleShare:' . $id);
901
+                return $share;
902
+            }
903
+        } catch (ShareNotFound $e) {
904
+            // Do nothing, just try the other share type
905
+        }
906
+
907
+        try {
908
+            if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
909
+                $share = $this->shareManager->getShareById('ocMailShare:' . $id);
910
+                return $share;
911
+            }
912
+        } catch (ShareNotFound $e) {
913
+            // Do nothing, just try the other share type
914
+        }
915
+
916
+        if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
917
+            throw new ShareNotFound();
918
+        }
919
+        $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id);
920
+
921
+        return $share;
922
+    }
923
+
924
+    /**
925
+     * Lock a Node
926
+     *
927
+     * @param \OCP\Files\Node $node
928
+     */
929
+    private function lock(\OCP\Files\Node $node) {
930
+        $node->lock(ILockingProvider::LOCK_SHARED);
931
+        $this->lockedNode = $node;
932
+    }
933
+
934
+    /**
935
+     * Cleanup the remaining locks
936
+     */
937
+    public function cleanup() {
938
+        if ($this->lockedNode !== null) {
939
+            $this->lockedNode->unlock(ILockingProvider::LOCK_SHARED);
940
+        }
941
+    }
942 942
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareesAPIController.php 1 patch
Indentation   +721 added lines, -721 removed lines patch added patch discarded remove patch
@@ -43,725 +43,725 @@
 block discarded – undo
43 43
 
44 44
 class ShareesAPIController extends OCSController {
45 45
 
46
-	/** @var IGroupManager */
47
-	protected $groupManager;
48
-
49
-	/** @var IUserManager */
50
-	protected $userManager;
51
-
52
-	/** @var IManager */
53
-	protected $contactsManager;
54
-
55
-	/** @var IConfig */
56
-	protected $config;
57
-
58
-	/** @var IUserSession */
59
-	protected $userSession;
60
-
61
-	/** @var IURLGenerator */
62
-	protected $urlGenerator;
63
-
64
-	/** @var ILogger */
65
-	protected $logger;
66
-
67
-	/** @var \OCP\Share\IManager */
68
-	protected $shareManager;
69
-
70
-	/** @var IClientService */
71
-	protected $clientService;
72
-
73
-	/** @var ICloudIdManager  */
74
-	protected $cloudIdManager;
75
-
76
-	/** @var bool */
77
-	protected $shareWithGroupOnly = false;
78
-
79
-	/** @var bool */
80
-	protected $shareeEnumeration = true;
81
-
82
-	/** @var int */
83
-	protected $offset = 0;
84
-
85
-	/** @var int */
86
-	protected $limit = 10;
87
-
88
-	/** @var array */
89
-	protected $result = [
90
-		'exact' => [
91
-			'users' => [],
92
-			'groups' => [],
93
-			'remotes' => [],
94
-			'emails' => [],
95
-			'circles' => [],
96
-		],
97
-		'users' => [],
98
-		'groups' => [],
99
-		'remotes' => [],
100
-		'emails' => [],
101
-		'lookup' => [],
102
-		'circles' => [],
103
-	];
104
-
105
-	protected $reachedEndFor = [];
106
-
107
-	/**
108
-	 * @param string $appName
109
-	 * @param IRequest $request
110
-	 * @param IGroupManager $groupManager
111
-	 * @param IUserManager $userManager
112
-	 * @param IManager $contactsManager
113
-	 * @param IConfig $config
114
-	 * @param IUserSession $userSession
115
-	 * @param IURLGenerator $urlGenerator
116
-	 * @param ILogger $logger
117
-	 * @param \OCP\Share\IManager $shareManager
118
-	 * @param IClientService $clientService
119
-	 * @param ICloudIdManager $cloudIdManager
120
-	 */
121
-	public function __construct($appName,
122
-								IRequest $request,
123
-								IGroupManager $groupManager,
124
-								IUserManager $userManager,
125
-								IManager $contactsManager,
126
-								IConfig $config,
127
-								IUserSession $userSession,
128
-								IURLGenerator $urlGenerator,
129
-								ILogger $logger,
130
-								\OCP\Share\IManager $shareManager,
131
-								IClientService $clientService,
132
-								ICloudIdManager $cloudIdManager
133
-	) {
134
-		parent::__construct($appName, $request);
135
-
136
-		$this->groupManager = $groupManager;
137
-		$this->userManager = $userManager;
138
-		$this->contactsManager = $contactsManager;
139
-		$this->config = $config;
140
-		$this->userSession = $userSession;
141
-		$this->urlGenerator = $urlGenerator;
142
-		$this->logger = $logger;
143
-		$this->shareManager = $shareManager;
144
-		$this->clientService = $clientService;
145
-		$this->cloudIdManager = $cloudIdManager;
146
-	}
147
-
148
-	/**
149
-	 * @param string $search
150
-	 */
151
-	protected function getUsers($search) {
152
-		$this->result['users'] = $this->result['exact']['users'] = $users = [];
153
-
154
-		$userGroups = [];
155
-		if ($this->shareWithGroupOnly) {
156
-			// Search in all the groups this user is part of
157
-			$userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
158
-			foreach ($userGroups as $userGroup) {
159
-				$usersTmp = $this->groupManager->displayNamesInGroup($userGroup, $search, $this->limit, $this->offset);
160
-				foreach ($usersTmp as $uid => $userDisplayName) {
161
-					$users[$uid] = $userDisplayName;
162
-				}
163
-			}
164
-		} else {
165
-			// Search in all users
166
-			$usersTmp = $this->userManager->searchDisplayName($search, $this->limit, $this->offset);
167
-
168
-			foreach ($usersTmp as $user) {
169
-				$users[$user->getUID()] = $user->getDisplayName();
170
-			}
171
-		}
172
-
173
-		if (!$this->shareeEnumeration || sizeof($users) < $this->limit) {
174
-			$this->reachedEndFor[] = 'users';
175
-		}
176
-
177
-		$foundUserById = false;
178
-		$lowerSearch = strtolower($search);
179
-		foreach ($users as $uid => $userDisplayName) {
180
-			if (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch) {
181
-				if (strtolower($uid) === $lowerSearch) {
182
-					$foundUserById = true;
183
-				}
184
-				$this->result['exact']['users'][] = [
185
-					'label' => $userDisplayName,
186
-					'value' => [
187
-						'shareType' => Share::SHARE_TYPE_USER,
188
-						'shareWith' => $uid,
189
-					],
190
-				];
191
-			} else {
192
-				$this->result['users'][] = [
193
-					'label' => $userDisplayName,
194
-					'value' => [
195
-						'shareType' => Share::SHARE_TYPE_USER,
196
-						'shareWith' => $uid,
197
-					],
198
-				];
199
-			}
200
-		}
201
-
202
-		if ($this->offset === 0 && !$foundUserById) {
203
-			// On page one we try if the search result has a direct hit on the
204
-			// user id and if so, we add that to the exact match list
205
-			$user = $this->userManager->get($search);
206
-			if ($user instanceof IUser) {
207
-				$addUser = true;
208
-
209
-				if ($this->shareWithGroupOnly) {
210
-					// Only add, if we have a common group
211
-					$commonGroups = array_intersect($userGroups, $this->groupManager->getUserGroupIds($user));
212
-					$addUser = !empty($commonGroups);
213
-				}
214
-
215
-				if ($addUser) {
216
-					array_push($this->result['exact']['users'], [
217
-						'label' => $user->getDisplayName(),
218
-						'value' => [
219
-							'shareType' => Share::SHARE_TYPE_USER,
220
-							'shareWith' => $user->getUID(),
221
-						],
222
-					]);
223
-				}
224
-			}
225
-		}
226
-
227
-		if (!$this->shareeEnumeration) {
228
-			$this->result['users'] = [];
229
-		}
230
-	}
231
-
232
-	/**
233
-	 * @param string $search
234
-	 */
235
-	protected function getGroups($search) {
236
-		$this->result['groups'] = $this->result['exact']['groups'] = [];
237
-
238
-		$groups = $this->groupManager->search($search, $this->limit, $this->offset);
239
-		$groupIds = array_map(function (IGroup $group) { return $group->getGID(); }, $groups);
240
-
241
-		if (!$this->shareeEnumeration || sizeof($groups) < $this->limit) {
242
-			$this->reachedEndFor[] = 'groups';
243
-		}
244
-
245
-		$userGroups =  [];
246
-		if (!empty($groups) && $this->shareWithGroupOnly) {
247
-			// Intersect all the groups that match with the groups this user is a member of
248
-			$userGroups = $this->groupManager->getUserGroups($this->userSession->getUser());
249
-			$userGroups = array_map(function (IGroup $group) { return $group->getGID(); }, $userGroups);
250
-			$groupIds = array_intersect($groupIds, $userGroups);
251
-		}
252
-
253
-		$lowerSearch = strtolower($search);
254
-		foreach ($groups as $group) {
255
-			// FIXME: use a more efficient approach
256
-			$gid = $group->getGID();
257
-			if (!in_array($gid, $groupIds)) {
258
-				continue;
259
-			}
260
-			if (strtolower($gid) === $lowerSearch || strtolower($group->getDisplayName()) === $lowerSearch) {
261
-				$this->result['exact']['groups'][] = [
262
-					'label' => $group->getDisplayName(),
263
-					'value' => [
264
-						'shareType' => Share::SHARE_TYPE_GROUP,
265
-						'shareWith' => $gid,
266
-					],
267
-				];
268
-			} else {
269
-				$this->result['groups'][] = [
270
-					'label' => $group->getDisplayName(),
271
-					'value' => [
272
-						'shareType' => Share::SHARE_TYPE_GROUP,
273
-						'shareWith' => $gid,
274
-					],
275
-				];
276
-			}
277
-		}
278
-
279
-		if ($this->offset === 0 && empty($this->result['exact']['groups'])) {
280
-			// On page one we try if the search result has a direct hit on the
281
-			// user id and if so, we add that to the exact match list
282
-			$group = $this->groupManager->get($search);
283
-			if ($group instanceof IGroup && (!$this->shareWithGroupOnly || in_array($group->getGID(), $userGroups))) {
284
-				array_push($this->result['exact']['groups'], [
285
-					'label' => $group->getDisplayName(),
286
-					'value' => [
287
-						'shareType' => Share::SHARE_TYPE_GROUP,
288
-						'shareWith' => $group->getGID(),
289
-					],
290
-				]);
291
-			}
292
-		}
293
-
294
-		if (!$this->shareeEnumeration) {
295
-			$this->result['groups'] = [];
296
-		}
297
-	}
298
-
299
-
300
-	/**
301
-	 * @param string $search
302
-	 * @suppress PhanUndeclaredClassMethod
303
-	 */
304
-	protected function getCircles($search) {
305
-		$this->result['circles'] = $this->result['exact']['circles'] = [];
306
-
307
-		$result = \OCA\Circles\Api\Sharees::search($search, $this->limit, $this->offset);
308
-		if (array_key_exists('circles', $result['exact'])) {
309
-			$this->result['exact']['circles'] = $result['exact']['circles'];
310
-		}
311
-		if (array_key_exists('circles', $result)) {
312
-			$this->result['circles'] = $result['circles'];
313
-		}
314
-	}
315
-
316
-
317
-	/**
318
-	 * @param string $search
319
-	 * @return array
320
-	 */
321
-	protected function getRemote($search) {
322
-		$result = ['results' => [], 'exact' => []];
323
-
324
-		// Search in contacts
325
-		//@todo Pagination missing
326
-		$addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']);
327
-		$result['exactIdMatch'] = false;
328
-		foreach ($addressBookContacts as $contact) {
329
-			if (isset($contact['isLocalSystemBook'])) {
330
-				continue;
331
-			}
332
-			if (isset($contact['CLOUD'])) {
333
-				$cloudIds = $contact['CLOUD'];
334
-				if (!is_array($cloudIds)) {
335
-					$cloudIds = [$cloudIds];
336
-				}
337
-				$lowerSearch = strtolower($search);
338
-				foreach ($cloudIds as $cloudId) {
339
-					list(, $serverUrl) = $this->splitUserRemote($cloudId);
340
-					if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) {
341
-						if (strtolower($cloudId) === $lowerSearch) {
342
-							$result['exactIdMatch'] = true;
343
-						}
344
-						$result['exact'][] = [
345
-							'label' => $contact['FN'] . " ($cloudId)",
346
-							'value' => [
347
-								'shareType' => Share::SHARE_TYPE_REMOTE,
348
-								'shareWith' => $cloudId,
349
-								'server' => $serverUrl,
350
-							],
351
-						];
352
-					} else {
353
-						$result['results'][] = [
354
-							'label' => $contact['FN'] . " ($cloudId)",
355
-							'value' => [
356
-								'shareType' => Share::SHARE_TYPE_REMOTE,
357
-								'shareWith' => $cloudId,
358
-								'server' => $serverUrl,
359
-							],
360
-						];
361
-					}
362
-				}
363
-			}
364
-		}
365
-
366
-		if (!$this->shareeEnumeration) {
367
-			$result['results'] = [];
368
-		}
369
-
370
-		if (!$result['exactIdMatch'] && $this->cloudIdManager->isValidCloudId($search) && $this->offset === 0) {
371
-			$result['exact'][] = [
372
-				'label' => $search,
373
-				'value' => [
374
-					'shareType' => Share::SHARE_TYPE_REMOTE,
375
-					'shareWith' => $search,
376
-				],
377
-			];
378
-		}
379
-
380
-		$this->reachedEndFor[] = 'remotes';
381
-
382
-		return $result;
383
-	}
384
-
385
-	/**
386
-	 * split user and remote from federated cloud id
387
-	 *
388
-	 * @param string $address federated share address
389
-	 * @return array [user, remoteURL]
390
-	 * @throws \Exception
391
-	 */
392
-	public function splitUserRemote($address) {
393
-		try {
394
-			$cloudId = $this->cloudIdManager->resolveCloudId($address);
395
-			return [$cloudId->getUser(), $cloudId->getRemote()];
396
-		} catch (\InvalidArgumentException $e) {
397
-			throw new \Exception('Invalid Federated Cloud ID', 0, $e);
398
-		}
399
-	}
400
-
401
-	/**
402
-	 * Strips away a potential file names and trailing slashes:
403
-	 * - http://localhost
404
-	 * - http://localhost/
405
-	 * - http://localhost/index.php
406
-	 * - http://localhost/index.php/s/{shareToken}
407
-	 *
408
-	 * all return: http://localhost
409
-	 *
410
-	 * @param string $remote
411
-	 * @return string
412
-	 */
413
-	protected function fixRemoteURL($remote) {
414
-		$remote = str_replace('\\', '/', $remote);
415
-		if ($fileNamePosition = strpos($remote, '/index.php')) {
416
-			$remote = substr($remote, 0, $fileNamePosition);
417
-		}
418
-		$remote = rtrim($remote, '/');
419
-
420
-		return $remote;
421
-	}
422
-
423
-	/**
424
-	 * @NoAdminRequired
425
-	 *
426
-	 * @param string $search
427
-	 * @param string $itemType
428
-	 * @param int $page
429
-	 * @param int $perPage
430
-	 * @param int|int[] $shareType
431
-	 * @param bool $lookup
432
-	 * @return DataResponse
433
-	 * @throws OCSBadRequestException
434
-	 */
435
-	public function search($search = '', $itemType = null, $page = 1, $perPage = 200, $shareType = null, $lookup = true) {
436
-
437
-		// only search for string larger than a given threshold
438
-		$threshold = (int)$this->config->getSystemValue('sharing.minSearchStringLength', 0);
439
-		if (strlen($search) < $threshold) {
440
-			return new DataResponse($this->result);
441
-		}
442
-
443
-		// never return more than the max. number of results configured in the config.php
444
-		$maxResults = (int)$this->config->getSystemValue('sharing.maxAutocompleteResults', 0);
445
-		if ($maxResults > 0) {
446
-			$perPage = min($perPage, $maxResults);
447
-		}
448
-		if ($perPage <= 0) {
449
-			throw new OCSBadRequestException('Invalid perPage argument');
450
-		}
451
-		if ($page <= 0) {
452
-			throw new OCSBadRequestException('Invalid page');
453
-		}
454
-
455
-		$shareTypes = [
456
-			Share::SHARE_TYPE_USER,
457
-		];
458
-
459
-		if ($itemType === 'file' || $itemType === 'folder') {
460
-			if ($this->shareManager->allowGroupSharing()) {
461
-				$shareTypes[] = Share::SHARE_TYPE_GROUP;
462
-			}
463
-
464
-			if ($this->isRemoteSharingAllowed($itemType)) {
465
-				$shareTypes[] = Share::SHARE_TYPE_REMOTE;
466
-			}
467
-
468
-			if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) {
469
-				$shareTypes[] = Share::SHARE_TYPE_EMAIL;
470
-			}
471
-		} else {
472
-			$shareTypes[] = Share::SHARE_TYPE_GROUP;
473
-			$shareTypes[] = Share::SHARE_TYPE_EMAIL;
474
-		}
475
-
476
-		if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
477
-			$shareTypes[] = Share::SHARE_TYPE_CIRCLE;
478
-		}
479
-
480
-		if (isset($_GET['shareType']) && is_array($_GET['shareType'])) {
481
-			$shareTypes = array_intersect($shareTypes, $_GET['shareType']);
482
-			sort($shareTypes);
483
-		} else if (is_numeric($shareType)) {
484
-			$shareTypes = array_intersect($shareTypes, [(int) $shareType]);
485
-			sort($shareTypes);
486
-		}
487
-
488
-		$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
489
-		$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
490
-		$this->limit = (int) $perPage;
491
-		$this->offset = $perPage * ($page - 1);
492
-
493
-		return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage, $lookup);
494
-	}
495
-
496
-	/**
497
-	 * Method to get out the static call for better testing
498
-	 *
499
-	 * @param string $itemType
500
-	 * @return bool
501
-	 */
502
-	protected function isRemoteSharingAllowed($itemType) {
503
-		try {
504
-			$backend = Share::getBackend($itemType);
505
-			return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE);
506
-		} catch (\Exception $e) {
507
-			return false;
508
-		}
509
-	}
510
-
511
-	/**
512
-	 * Testable search function that does not need globals
513
-	 *
514
-	 * @param string $search
515
-	 * @param string $itemType
516
-	 * @param array $shareTypes
517
-	 * @param int $page
518
-	 * @param int $perPage
519
-	 * @param bool $lookup
520
-	 * @return DataResponse
521
-	 * @throws OCSBadRequestException
522
-	 */
523
-	protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage, $lookup) {
524
-		// Verify arguments
525
-		if ($itemType === null) {
526
-			throw new OCSBadRequestException('Missing itemType');
527
-		}
528
-
529
-		// Get users
530
-		if (in_array(Share::SHARE_TYPE_USER, $shareTypes)) {
531
-			$this->getUsers($search);
532
-		}
533
-
534
-		// Get groups
535
-		if (in_array(Share::SHARE_TYPE_GROUP, $shareTypes)) {
536
-			$this->getGroups($search);
537
-		}
538
-
539
-		// Get circles
540
-		if (in_array(Share::SHARE_TYPE_CIRCLE, $shareTypes)) {
541
-			$this->getCircles($search);
542
-		}
543
-
544
-
545
-		// Get remote
546
-		$remoteResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
547
-		if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) {
548
-			$remoteResults = $this->getRemote($search);
549
-		}
550
-
551
-		// Get emails
552
-		$mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
553
-		if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) {
554
-			$mailResults = $this->getEmail($search);
555
-		}
556
-
557
-		// Get from lookup server
558
-		if ($lookup) {
559
-			$this->getLookup($search);
560
-		}
561
-
562
-		// if we have a exact match, either for the federated cloud id or for the
563
-		// email address we only return the exact match. It is highly unlikely
564
-		// that the exact same email address and federated cloud id exists
565
-		if ($mailResults['exactIdMatch'] && !$remoteResults['exactIdMatch']) {
566
-			$this->result['emails'] = $mailResults['results'];
567
-			$this->result['exact']['emails'] = $mailResults['exact'];
568
-		} else if (!$mailResults['exactIdMatch'] && $remoteResults['exactIdMatch']) {
569
-			$this->result['remotes'] = $remoteResults['results'];
570
-			$this->result['exact']['remotes'] = $remoteResults['exact'];
571
-		} else {
572
-			$this->result['remotes'] = $remoteResults['results'];
573
-			$this->result['exact']['remotes'] = $remoteResults['exact'];
574
-			$this->result['emails'] = $mailResults['results'];
575
-			$this->result['exact']['emails'] = $mailResults['exact'];
576
-		}
577
-
578
-		$response = new DataResponse($this->result);
579
-
580
-		if (sizeof($this->reachedEndFor) < 3) {
581
-			$response->addHeader('Link', $this->getPaginationLink($page, [
582
-				'search' => $search,
583
-				'itemType' => $itemType,
584
-				'shareType' => $shareTypes,
585
-				'perPage' => $perPage,
586
-			]));
587
-		}
588
-
589
-		return $response;
590
-	}
591
-
592
-	/**
593
-	 * @param string $search
594
-	 * @return array
595
-	 */
596
-	protected function getEmail($search) {
597
-		$result = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
598
-
599
-		// Search in contacts
600
-		//@todo Pagination missing
601
-		$addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']);
602
-		$lowerSearch = strtolower($search);
603
-		foreach ($addressBookContacts as $contact) {
604
-			if (isset($contact['EMAIL'])) {
605
-				$emailAddresses = $contact['EMAIL'];
606
-				if (!is_array($emailAddresses)) {
607
-					$emailAddresses = [$emailAddresses];
608
-				}
609
-				foreach ($emailAddresses as $emailAddress) {
610
-					$exactEmailMatch = strtolower($emailAddress) === $lowerSearch;
611
-
612
-					if (isset($contact['isLocalSystemBook'])) {
613
-						if ($exactEmailMatch) {
614
-							$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
615
-							if (!$this->hasUserInResult($cloud->getUser())) {
616
-								$this->result['exact']['users'][] = [
617
-									'label' => $contact['FN'] . " ($emailAddress)",
618
-									'value' => [
619
-										'shareType' => Share::SHARE_TYPE_USER,
620
-										'shareWith' => $cloud->getUser(),
621
-									],
622
-								];
623
-							}
624
-							return ['results' => [], 'exact' => [], 'exactIdMatch' => true];
625
-						}
626
-						if ($this->shareeEnumeration) {
627
-							$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
628
-							if (!$this->hasUserInResult($cloud->getUser())) {
629
-								$this->result['users'][] = [
630
-									'label' => $contact['FN'] . " ($emailAddress)",
631
-									'value' => [
632
-										'shareType' => Share::SHARE_TYPE_USER,
633
-										'shareWith' => $cloud->getUser(),
634
-									],
635
-								];
636
-							}
637
-						}
638
-						continue;
639
-					}
640
-
641
-					if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) {
642
-						if ($exactEmailMatch) {
643
-							$result['exactIdMatch'] = true;
644
-						}
645
-						$result['exact'][] = [
646
-							'label' => $contact['FN'] . " ($emailAddress)",
647
-							'value' => [
648
-								'shareType' => Share::SHARE_TYPE_EMAIL,
649
-								'shareWith' => $emailAddress,
650
-							],
651
-						];
652
-					} else {
653
-						$result['results'][] = [
654
-							'label' => $contact['FN'] . " ($emailAddress)",
655
-							'value' => [
656
-								'shareType' => Share::SHARE_TYPE_EMAIL,
657
-								'shareWith' => $emailAddress,
658
-							],
659
-						];
660
-					}
661
-				}
662
-			}
663
-		}
664
-
665
-		if (!$this->shareeEnumeration) {
666
-			$result['results'] = [];
667
-		}
668
-
669
-		if (!$result['exactIdMatch'] && filter_var($search, FILTER_VALIDATE_EMAIL)) {
670
-			$result['exact'][] = [
671
-				'label' => $search,
672
-				'value' => [
673
-					'shareType' => Share::SHARE_TYPE_EMAIL,
674
-					'shareWith' => $search,
675
-				],
676
-			];
677
-		}
678
-
679
-		$this->reachedEndFor[] = 'emails';
680
-
681
-		return $result;
682
-	}
683
-
684
-	protected function getLookup($search) {
685
-		$isEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
686
-		$lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
687
-		$lookupServerUrl = rtrim($lookupServerUrl, '/');
688
-		$result = [];
689
-
690
-		if($isEnabled === 'yes') {
691
-			try {
692
-				$client = $this->clientService->newClient();
693
-				$response = $client->get(
694
-					$lookupServerUrl . '/users?search=' . urlencode($search),
695
-					[
696
-						'timeout' => 10,
697
-						'connect_timeout' => 3,
698
-					]
699
-				);
700
-
701
-				$body = json_decode($response->getBody(), true);
702
-
703
-				$result = [];
704
-				foreach ($body as $lookup) {
705
-					$result[] = [
706
-						'label' => $lookup['federationId'],
707
-						'value' => [
708
-							'shareType' => Share::SHARE_TYPE_REMOTE,
709
-							'shareWith' => $lookup['federationId'],
710
-						],
711
-						'extra' => $lookup,
712
-					];
713
-				}
714
-			} catch (\Exception $e) {}
715
-		}
716
-
717
-		$this->result['lookup'] = $result;
718
-	}
719
-
720
-	/**
721
-	 * Check if a given user is already part of the result
722
-	 *
723
-	 * @param string $userId
724
-	 * @return bool
725
-	 */
726
-	protected function hasUserInResult($userId) {
727
-		foreach ($this->result['exact']['users'] as $result) {
728
-			if ($result['value']['shareWith'] === $userId) {
729
-				return true;
730
-			}
731
-		}
732
-
733
-		foreach ($this->result['users'] as $result) {
734
-			if ($result['value']['shareWith'] === $userId) {
735
-				return true;
736
-			}
737
-		}
738
-
739
-		return false;
740
-	}
741
-
742
-	/**
743
-	 * Generates a bunch of pagination links for the current page
744
-	 *
745
-	 * @param int $page Current page
746
-	 * @param array $params Parameters for the URL
747
-	 * @return string
748
-	 */
749
-	protected function getPaginationLink($page, array $params) {
750
-		if ($this->isV2()) {
751
-			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?';
752
-		} else {
753
-			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?';
754
-		}
755
-		$params['page'] = $page + 1;
756
-		$link = '<' . $url . http_build_query($params) . '>; rel="next"';
757
-
758
-		return $link;
759
-	}
760
-
761
-	/**
762
-	 * @return bool
763
-	 */
764
-	protected function isV2() {
765
-		return $this->request->getScriptName() === '/ocs/v2.php';
766
-	}
46
+    /** @var IGroupManager */
47
+    protected $groupManager;
48
+
49
+    /** @var IUserManager */
50
+    protected $userManager;
51
+
52
+    /** @var IManager */
53
+    protected $contactsManager;
54
+
55
+    /** @var IConfig */
56
+    protected $config;
57
+
58
+    /** @var IUserSession */
59
+    protected $userSession;
60
+
61
+    /** @var IURLGenerator */
62
+    protected $urlGenerator;
63
+
64
+    /** @var ILogger */
65
+    protected $logger;
66
+
67
+    /** @var \OCP\Share\IManager */
68
+    protected $shareManager;
69
+
70
+    /** @var IClientService */
71
+    protected $clientService;
72
+
73
+    /** @var ICloudIdManager  */
74
+    protected $cloudIdManager;
75
+
76
+    /** @var bool */
77
+    protected $shareWithGroupOnly = false;
78
+
79
+    /** @var bool */
80
+    protected $shareeEnumeration = true;
81
+
82
+    /** @var int */
83
+    protected $offset = 0;
84
+
85
+    /** @var int */
86
+    protected $limit = 10;
87
+
88
+    /** @var array */
89
+    protected $result = [
90
+        'exact' => [
91
+            'users' => [],
92
+            'groups' => [],
93
+            'remotes' => [],
94
+            'emails' => [],
95
+            'circles' => [],
96
+        ],
97
+        'users' => [],
98
+        'groups' => [],
99
+        'remotes' => [],
100
+        'emails' => [],
101
+        'lookup' => [],
102
+        'circles' => [],
103
+    ];
104
+
105
+    protected $reachedEndFor = [];
106
+
107
+    /**
108
+     * @param string $appName
109
+     * @param IRequest $request
110
+     * @param IGroupManager $groupManager
111
+     * @param IUserManager $userManager
112
+     * @param IManager $contactsManager
113
+     * @param IConfig $config
114
+     * @param IUserSession $userSession
115
+     * @param IURLGenerator $urlGenerator
116
+     * @param ILogger $logger
117
+     * @param \OCP\Share\IManager $shareManager
118
+     * @param IClientService $clientService
119
+     * @param ICloudIdManager $cloudIdManager
120
+     */
121
+    public function __construct($appName,
122
+                                IRequest $request,
123
+                                IGroupManager $groupManager,
124
+                                IUserManager $userManager,
125
+                                IManager $contactsManager,
126
+                                IConfig $config,
127
+                                IUserSession $userSession,
128
+                                IURLGenerator $urlGenerator,
129
+                                ILogger $logger,
130
+                                \OCP\Share\IManager $shareManager,
131
+                                IClientService $clientService,
132
+                                ICloudIdManager $cloudIdManager
133
+    ) {
134
+        parent::__construct($appName, $request);
135
+
136
+        $this->groupManager = $groupManager;
137
+        $this->userManager = $userManager;
138
+        $this->contactsManager = $contactsManager;
139
+        $this->config = $config;
140
+        $this->userSession = $userSession;
141
+        $this->urlGenerator = $urlGenerator;
142
+        $this->logger = $logger;
143
+        $this->shareManager = $shareManager;
144
+        $this->clientService = $clientService;
145
+        $this->cloudIdManager = $cloudIdManager;
146
+    }
147
+
148
+    /**
149
+     * @param string $search
150
+     */
151
+    protected function getUsers($search) {
152
+        $this->result['users'] = $this->result['exact']['users'] = $users = [];
153
+
154
+        $userGroups = [];
155
+        if ($this->shareWithGroupOnly) {
156
+            // Search in all the groups this user is part of
157
+            $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
158
+            foreach ($userGroups as $userGroup) {
159
+                $usersTmp = $this->groupManager->displayNamesInGroup($userGroup, $search, $this->limit, $this->offset);
160
+                foreach ($usersTmp as $uid => $userDisplayName) {
161
+                    $users[$uid] = $userDisplayName;
162
+                }
163
+            }
164
+        } else {
165
+            // Search in all users
166
+            $usersTmp = $this->userManager->searchDisplayName($search, $this->limit, $this->offset);
167
+
168
+            foreach ($usersTmp as $user) {
169
+                $users[$user->getUID()] = $user->getDisplayName();
170
+            }
171
+        }
172
+
173
+        if (!$this->shareeEnumeration || sizeof($users) < $this->limit) {
174
+            $this->reachedEndFor[] = 'users';
175
+        }
176
+
177
+        $foundUserById = false;
178
+        $lowerSearch = strtolower($search);
179
+        foreach ($users as $uid => $userDisplayName) {
180
+            if (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch) {
181
+                if (strtolower($uid) === $lowerSearch) {
182
+                    $foundUserById = true;
183
+                }
184
+                $this->result['exact']['users'][] = [
185
+                    'label' => $userDisplayName,
186
+                    'value' => [
187
+                        'shareType' => Share::SHARE_TYPE_USER,
188
+                        'shareWith' => $uid,
189
+                    ],
190
+                ];
191
+            } else {
192
+                $this->result['users'][] = [
193
+                    'label' => $userDisplayName,
194
+                    'value' => [
195
+                        'shareType' => Share::SHARE_TYPE_USER,
196
+                        'shareWith' => $uid,
197
+                    ],
198
+                ];
199
+            }
200
+        }
201
+
202
+        if ($this->offset === 0 && !$foundUserById) {
203
+            // On page one we try if the search result has a direct hit on the
204
+            // user id and if so, we add that to the exact match list
205
+            $user = $this->userManager->get($search);
206
+            if ($user instanceof IUser) {
207
+                $addUser = true;
208
+
209
+                if ($this->shareWithGroupOnly) {
210
+                    // Only add, if we have a common group
211
+                    $commonGroups = array_intersect($userGroups, $this->groupManager->getUserGroupIds($user));
212
+                    $addUser = !empty($commonGroups);
213
+                }
214
+
215
+                if ($addUser) {
216
+                    array_push($this->result['exact']['users'], [
217
+                        'label' => $user->getDisplayName(),
218
+                        'value' => [
219
+                            'shareType' => Share::SHARE_TYPE_USER,
220
+                            'shareWith' => $user->getUID(),
221
+                        ],
222
+                    ]);
223
+                }
224
+            }
225
+        }
226
+
227
+        if (!$this->shareeEnumeration) {
228
+            $this->result['users'] = [];
229
+        }
230
+    }
231
+
232
+    /**
233
+     * @param string $search
234
+     */
235
+    protected function getGroups($search) {
236
+        $this->result['groups'] = $this->result['exact']['groups'] = [];
237
+
238
+        $groups = $this->groupManager->search($search, $this->limit, $this->offset);
239
+        $groupIds = array_map(function (IGroup $group) { return $group->getGID(); }, $groups);
240
+
241
+        if (!$this->shareeEnumeration || sizeof($groups) < $this->limit) {
242
+            $this->reachedEndFor[] = 'groups';
243
+        }
244
+
245
+        $userGroups =  [];
246
+        if (!empty($groups) && $this->shareWithGroupOnly) {
247
+            // Intersect all the groups that match with the groups this user is a member of
248
+            $userGroups = $this->groupManager->getUserGroups($this->userSession->getUser());
249
+            $userGroups = array_map(function (IGroup $group) { return $group->getGID(); }, $userGroups);
250
+            $groupIds = array_intersect($groupIds, $userGroups);
251
+        }
252
+
253
+        $lowerSearch = strtolower($search);
254
+        foreach ($groups as $group) {
255
+            // FIXME: use a more efficient approach
256
+            $gid = $group->getGID();
257
+            if (!in_array($gid, $groupIds)) {
258
+                continue;
259
+            }
260
+            if (strtolower($gid) === $lowerSearch || strtolower($group->getDisplayName()) === $lowerSearch) {
261
+                $this->result['exact']['groups'][] = [
262
+                    'label' => $group->getDisplayName(),
263
+                    'value' => [
264
+                        'shareType' => Share::SHARE_TYPE_GROUP,
265
+                        'shareWith' => $gid,
266
+                    ],
267
+                ];
268
+            } else {
269
+                $this->result['groups'][] = [
270
+                    'label' => $group->getDisplayName(),
271
+                    'value' => [
272
+                        'shareType' => Share::SHARE_TYPE_GROUP,
273
+                        'shareWith' => $gid,
274
+                    ],
275
+                ];
276
+            }
277
+        }
278
+
279
+        if ($this->offset === 0 && empty($this->result['exact']['groups'])) {
280
+            // On page one we try if the search result has a direct hit on the
281
+            // user id and if so, we add that to the exact match list
282
+            $group = $this->groupManager->get($search);
283
+            if ($group instanceof IGroup && (!$this->shareWithGroupOnly || in_array($group->getGID(), $userGroups))) {
284
+                array_push($this->result['exact']['groups'], [
285
+                    'label' => $group->getDisplayName(),
286
+                    'value' => [
287
+                        'shareType' => Share::SHARE_TYPE_GROUP,
288
+                        'shareWith' => $group->getGID(),
289
+                    ],
290
+                ]);
291
+            }
292
+        }
293
+
294
+        if (!$this->shareeEnumeration) {
295
+            $this->result['groups'] = [];
296
+        }
297
+    }
298
+
299
+
300
+    /**
301
+     * @param string $search
302
+     * @suppress PhanUndeclaredClassMethod
303
+     */
304
+    protected function getCircles($search) {
305
+        $this->result['circles'] = $this->result['exact']['circles'] = [];
306
+
307
+        $result = \OCA\Circles\Api\Sharees::search($search, $this->limit, $this->offset);
308
+        if (array_key_exists('circles', $result['exact'])) {
309
+            $this->result['exact']['circles'] = $result['exact']['circles'];
310
+        }
311
+        if (array_key_exists('circles', $result)) {
312
+            $this->result['circles'] = $result['circles'];
313
+        }
314
+    }
315
+
316
+
317
+    /**
318
+     * @param string $search
319
+     * @return array
320
+     */
321
+    protected function getRemote($search) {
322
+        $result = ['results' => [], 'exact' => []];
323
+
324
+        // Search in contacts
325
+        //@todo Pagination missing
326
+        $addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']);
327
+        $result['exactIdMatch'] = false;
328
+        foreach ($addressBookContacts as $contact) {
329
+            if (isset($contact['isLocalSystemBook'])) {
330
+                continue;
331
+            }
332
+            if (isset($contact['CLOUD'])) {
333
+                $cloudIds = $contact['CLOUD'];
334
+                if (!is_array($cloudIds)) {
335
+                    $cloudIds = [$cloudIds];
336
+                }
337
+                $lowerSearch = strtolower($search);
338
+                foreach ($cloudIds as $cloudId) {
339
+                    list(, $serverUrl) = $this->splitUserRemote($cloudId);
340
+                    if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) {
341
+                        if (strtolower($cloudId) === $lowerSearch) {
342
+                            $result['exactIdMatch'] = true;
343
+                        }
344
+                        $result['exact'][] = [
345
+                            'label' => $contact['FN'] . " ($cloudId)",
346
+                            'value' => [
347
+                                'shareType' => Share::SHARE_TYPE_REMOTE,
348
+                                'shareWith' => $cloudId,
349
+                                'server' => $serverUrl,
350
+                            ],
351
+                        ];
352
+                    } else {
353
+                        $result['results'][] = [
354
+                            'label' => $contact['FN'] . " ($cloudId)",
355
+                            'value' => [
356
+                                'shareType' => Share::SHARE_TYPE_REMOTE,
357
+                                'shareWith' => $cloudId,
358
+                                'server' => $serverUrl,
359
+                            ],
360
+                        ];
361
+                    }
362
+                }
363
+            }
364
+        }
365
+
366
+        if (!$this->shareeEnumeration) {
367
+            $result['results'] = [];
368
+        }
369
+
370
+        if (!$result['exactIdMatch'] && $this->cloudIdManager->isValidCloudId($search) && $this->offset === 0) {
371
+            $result['exact'][] = [
372
+                'label' => $search,
373
+                'value' => [
374
+                    'shareType' => Share::SHARE_TYPE_REMOTE,
375
+                    'shareWith' => $search,
376
+                ],
377
+            ];
378
+        }
379
+
380
+        $this->reachedEndFor[] = 'remotes';
381
+
382
+        return $result;
383
+    }
384
+
385
+    /**
386
+     * split user and remote from federated cloud id
387
+     *
388
+     * @param string $address federated share address
389
+     * @return array [user, remoteURL]
390
+     * @throws \Exception
391
+     */
392
+    public function splitUserRemote($address) {
393
+        try {
394
+            $cloudId = $this->cloudIdManager->resolveCloudId($address);
395
+            return [$cloudId->getUser(), $cloudId->getRemote()];
396
+        } catch (\InvalidArgumentException $e) {
397
+            throw new \Exception('Invalid Federated Cloud ID', 0, $e);
398
+        }
399
+    }
400
+
401
+    /**
402
+     * Strips away a potential file names and trailing slashes:
403
+     * - http://localhost
404
+     * - http://localhost/
405
+     * - http://localhost/index.php
406
+     * - http://localhost/index.php/s/{shareToken}
407
+     *
408
+     * all return: http://localhost
409
+     *
410
+     * @param string $remote
411
+     * @return string
412
+     */
413
+    protected function fixRemoteURL($remote) {
414
+        $remote = str_replace('\\', '/', $remote);
415
+        if ($fileNamePosition = strpos($remote, '/index.php')) {
416
+            $remote = substr($remote, 0, $fileNamePosition);
417
+        }
418
+        $remote = rtrim($remote, '/');
419
+
420
+        return $remote;
421
+    }
422
+
423
+    /**
424
+     * @NoAdminRequired
425
+     *
426
+     * @param string $search
427
+     * @param string $itemType
428
+     * @param int $page
429
+     * @param int $perPage
430
+     * @param int|int[] $shareType
431
+     * @param bool $lookup
432
+     * @return DataResponse
433
+     * @throws OCSBadRequestException
434
+     */
435
+    public function search($search = '', $itemType = null, $page = 1, $perPage = 200, $shareType = null, $lookup = true) {
436
+
437
+        // only search for string larger than a given threshold
438
+        $threshold = (int)$this->config->getSystemValue('sharing.minSearchStringLength', 0);
439
+        if (strlen($search) < $threshold) {
440
+            return new DataResponse($this->result);
441
+        }
442
+
443
+        // never return more than the max. number of results configured in the config.php
444
+        $maxResults = (int)$this->config->getSystemValue('sharing.maxAutocompleteResults', 0);
445
+        if ($maxResults > 0) {
446
+            $perPage = min($perPage, $maxResults);
447
+        }
448
+        if ($perPage <= 0) {
449
+            throw new OCSBadRequestException('Invalid perPage argument');
450
+        }
451
+        if ($page <= 0) {
452
+            throw new OCSBadRequestException('Invalid page');
453
+        }
454
+
455
+        $shareTypes = [
456
+            Share::SHARE_TYPE_USER,
457
+        ];
458
+
459
+        if ($itemType === 'file' || $itemType === 'folder') {
460
+            if ($this->shareManager->allowGroupSharing()) {
461
+                $shareTypes[] = Share::SHARE_TYPE_GROUP;
462
+            }
463
+
464
+            if ($this->isRemoteSharingAllowed($itemType)) {
465
+                $shareTypes[] = Share::SHARE_TYPE_REMOTE;
466
+            }
467
+
468
+            if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) {
469
+                $shareTypes[] = Share::SHARE_TYPE_EMAIL;
470
+            }
471
+        } else {
472
+            $shareTypes[] = Share::SHARE_TYPE_GROUP;
473
+            $shareTypes[] = Share::SHARE_TYPE_EMAIL;
474
+        }
475
+
476
+        if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
477
+            $shareTypes[] = Share::SHARE_TYPE_CIRCLE;
478
+        }
479
+
480
+        if (isset($_GET['shareType']) && is_array($_GET['shareType'])) {
481
+            $shareTypes = array_intersect($shareTypes, $_GET['shareType']);
482
+            sort($shareTypes);
483
+        } else if (is_numeric($shareType)) {
484
+            $shareTypes = array_intersect($shareTypes, [(int) $shareType]);
485
+            sort($shareTypes);
486
+        }
487
+
488
+        $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
489
+        $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
490
+        $this->limit = (int) $perPage;
491
+        $this->offset = $perPage * ($page - 1);
492
+
493
+        return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage, $lookup);
494
+    }
495
+
496
+    /**
497
+     * Method to get out the static call for better testing
498
+     *
499
+     * @param string $itemType
500
+     * @return bool
501
+     */
502
+    protected function isRemoteSharingAllowed($itemType) {
503
+        try {
504
+            $backend = Share::getBackend($itemType);
505
+            return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE);
506
+        } catch (\Exception $e) {
507
+            return false;
508
+        }
509
+    }
510
+
511
+    /**
512
+     * Testable search function that does not need globals
513
+     *
514
+     * @param string $search
515
+     * @param string $itemType
516
+     * @param array $shareTypes
517
+     * @param int $page
518
+     * @param int $perPage
519
+     * @param bool $lookup
520
+     * @return DataResponse
521
+     * @throws OCSBadRequestException
522
+     */
523
+    protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage, $lookup) {
524
+        // Verify arguments
525
+        if ($itemType === null) {
526
+            throw new OCSBadRequestException('Missing itemType');
527
+        }
528
+
529
+        // Get users
530
+        if (in_array(Share::SHARE_TYPE_USER, $shareTypes)) {
531
+            $this->getUsers($search);
532
+        }
533
+
534
+        // Get groups
535
+        if (in_array(Share::SHARE_TYPE_GROUP, $shareTypes)) {
536
+            $this->getGroups($search);
537
+        }
538
+
539
+        // Get circles
540
+        if (in_array(Share::SHARE_TYPE_CIRCLE, $shareTypes)) {
541
+            $this->getCircles($search);
542
+        }
543
+
544
+
545
+        // Get remote
546
+        $remoteResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
547
+        if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) {
548
+            $remoteResults = $this->getRemote($search);
549
+        }
550
+
551
+        // Get emails
552
+        $mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
553
+        if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) {
554
+            $mailResults = $this->getEmail($search);
555
+        }
556
+
557
+        // Get from lookup server
558
+        if ($lookup) {
559
+            $this->getLookup($search);
560
+        }
561
+
562
+        // if we have a exact match, either for the federated cloud id or for the
563
+        // email address we only return the exact match. It is highly unlikely
564
+        // that the exact same email address and federated cloud id exists
565
+        if ($mailResults['exactIdMatch'] && !$remoteResults['exactIdMatch']) {
566
+            $this->result['emails'] = $mailResults['results'];
567
+            $this->result['exact']['emails'] = $mailResults['exact'];
568
+        } else if (!$mailResults['exactIdMatch'] && $remoteResults['exactIdMatch']) {
569
+            $this->result['remotes'] = $remoteResults['results'];
570
+            $this->result['exact']['remotes'] = $remoteResults['exact'];
571
+        } else {
572
+            $this->result['remotes'] = $remoteResults['results'];
573
+            $this->result['exact']['remotes'] = $remoteResults['exact'];
574
+            $this->result['emails'] = $mailResults['results'];
575
+            $this->result['exact']['emails'] = $mailResults['exact'];
576
+        }
577
+
578
+        $response = new DataResponse($this->result);
579
+
580
+        if (sizeof($this->reachedEndFor) < 3) {
581
+            $response->addHeader('Link', $this->getPaginationLink($page, [
582
+                'search' => $search,
583
+                'itemType' => $itemType,
584
+                'shareType' => $shareTypes,
585
+                'perPage' => $perPage,
586
+            ]));
587
+        }
588
+
589
+        return $response;
590
+    }
591
+
592
+    /**
593
+     * @param string $search
594
+     * @return array
595
+     */
596
+    protected function getEmail($search) {
597
+        $result = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
598
+
599
+        // Search in contacts
600
+        //@todo Pagination missing
601
+        $addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']);
602
+        $lowerSearch = strtolower($search);
603
+        foreach ($addressBookContacts as $contact) {
604
+            if (isset($contact['EMAIL'])) {
605
+                $emailAddresses = $contact['EMAIL'];
606
+                if (!is_array($emailAddresses)) {
607
+                    $emailAddresses = [$emailAddresses];
608
+                }
609
+                foreach ($emailAddresses as $emailAddress) {
610
+                    $exactEmailMatch = strtolower($emailAddress) === $lowerSearch;
611
+
612
+                    if (isset($contact['isLocalSystemBook'])) {
613
+                        if ($exactEmailMatch) {
614
+                            $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
615
+                            if (!$this->hasUserInResult($cloud->getUser())) {
616
+                                $this->result['exact']['users'][] = [
617
+                                    'label' => $contact['FN'] . " ($emailAddress)",
618
+                                    'value' => [
619
+                                        'shareType' => Share::SHARE_TYPE_USER,
620
+                                        'shareWith' => $cloud->getUser(),
621
+                                    ],
622
+                                ];
623
+                            }
624
+                            return ['results' => [], 'exact' => [], 'exactIdMatch' => true];
625
+                        }
626
+                        if ($this->shareeEnumeration) {
627
+                            $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
628
+                            if (!$this->hasUserInResult($cloud->getUser())) {
629
+                                $this->result['users'][] = [
630
+                                    'label' => $contact['FN'] . " ($emailAddress)",
631
+                                    'value' => [
632
+                                        'shareType' => Share::SHARE_TYPE_USER,
633
+                                        'shareWith' => $cloud->getUser(),
634
+                                    ],
635
+                                ];
636
+                            }
637
+                        }
638
+                        continue;
639
+                    }
640
+
641
+                    if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) {
642
+                        if ($exactEmailMatch) {
643
+                            $result['exactIdMatch'] = true;
644
+                        }
645
+                        $result['exact'][] = [
646
+                            'label' => $contact['FN'] . " ($emailAddress)",
647
+                            'value' => [
648
+                                'shareType' => Share::SHARE_TYPE_EMAIL,
649
+                                'shareWith' => $emailAddress,
650
+                            ],
651
+                        ];
652
+                    } else {
653
+                        $result['results'][] = [
654
+                            'label' => $contact['FN'] . " ($emailAddress)",
655
+                            'value' => [
656
+                                'shareType' => Share::SHARE_TYPE_EMAIL,
657
+                                'shareWith' => $emailAddress,
658
+                            ],
659
+                        ];
660
+                    }
661
+                }
662
+            }
663
+        }
664
+
665
+        if (!$this->shareeEnumeration) {
666
+            $result['results'] = [];
667
+        }
668
+
669
+        if (!$result['exactIdMatch'] && filter_var($search, FILTER_VALIDATE_EMAIL)) {
670
+            $result['exact'][] = [
671
+                'label' => $search,
672
+                'value' => [
673
+                    'shareType' => Share::SHARE_TYPE_EMAIL,
674
+                    'shareWith' => $search,
675
+                ],
676
+            ];
677
+        }
678
+
679
+        $this->reachedEndFor[] = 'emails';
680
+
681
+        return $result;
682
+    }
683
+
684
+    protected function getLookup($search) {
685
+        $isEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
686
+        $lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
687
+        $lookupServerUrl = rtrim($lookupServerUrl, '/');
688
+        $result = [];
689
+
690
+        if($isEnabled === 'yes') {
691
+            try {
692
+                $client = $this->clientService->newClient();
693
+                $response = $client->get(
694
+                    $lookupServerUrl . '/users?search=' . urlencode($search),
695
+                    [
696
+                        'timeout' => 10,
697
+                        'connect_timeout' => 3,
698
+                    ]
699
+                );
700
+
701
+                $body = json_decode($response->getBody(), true);
702
+
703
+                $result = [];
704
+                foreach ($body as $lookup) {
705
+                    $result[] = [
706
+                        'label' => $lookup['federationId'],
707
+                        'value' => [
708
+                            'shareType' => Share::SHARE_TYPE_REMOTE,
709
+                            'shareWith' => $lookup['federationId'],
710
+                        ],
711
+                        'extra' => $lookup,
712
+                    ];
713
+                }
714
+            } catch (\Exception $e) {}
715
+        }
716
+
717
+        $this->result['lookup'] = $result;
718
+    }
719
+
720
+    /**
721
+     * Check if a given user is already part of the result
722
+     *
723
+     * @param string $userId
724
+     * @return bool
725
+     */
726
+    protected function hasUserInResult($userId) {
727
+        foreach ($this->result['exact']['users'] as $result) {
728
+            if ($result['value']['shareWith'] === $userId) {
729
+                return true;
730
+            }
731
+        }
732
+
733
+        foreach ($this->result['users'] as $result) {
734
+            if ($result['value']['shareWith'] === $userId) {
735
+                return true;
736
+            }
737
+        }
738
+
739
+        return false;
740
+    }
741
+
742
+    /**
743
+     * Generates a bunch of pagination links for the current page
744
+     *
745
+     * @param int $page Current page
746
+     * @param array $params Parameters for the URL
747
+     * @return string
748
+     */
749
+    protected function getPaginationLink($page, array $params) {
750
+        if ($this->isV2()) {
751
+            $url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?';
752
+        } else {
753
+            $url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?';
754
+        }
755
+        $params['page'] = $page + 1;
756
+        $link = '<' . $url . http_build_query($params) . '>; rel="next"';
757
+
758
+        return $link;
759
+    }
760
+
761
+    /**
762
+     * @return bool
763
+     */
764
+    protected function isV2() {
765
+        return $this->request->getScriptName() === '/ocs/v2.php';
766
+    }
767 767
 }
Please login to merge, or discard this patch.
apps/sharebymail/lib/ShareByMailProvider.php 1 patch
Indentation   +999 added lines, -999 removed lines patch added patch discarded remove patch
@@ -53,1017 +53,1017 @@
 block discarded – undo
53 53
  */
54 54
 class ShareByMailProvider implements IShareProvider {
55 55
 
56
-	/** @var  IDBConnection */
57
-	private $dbConnection;
58
-
59
-	/** @var ILogger */
60
-	private $logger;
61
-
62
-	/** @var ISecureRandom */
63
-	private $secureRandom;
64
-
65
-	/** @var IUserManager */
66
-	private $userManager;
67
-
68
-	/** @var IRootFolder */
69
-	private $rootFolder;
70
-
71
-	/** @var IL10N */
72
-	private $l;
73
-
74
-	/** @var IMailer */
75
-	private $mailer;
76
-
77
-	/** @var IURLGenerator */
78
-	private $urlGenerator;
79
-
80
-	/** @var IManager  */
81
-	private $activityManager;
82
-
83
-	/** @var SettingsManager */
84
-	private $settingsManager;
85
-
86
-	/** @var Defaults */
87
-	private $defaults;
88
-
89
-	/** @var IHasher */
90
-	private $hasher;
91
-
92
-	/** @var  CapabilitiesManager */
93
-	private $capabilitiesManager;
94
-
95
-	/**
96
-	 * Return the identifier of this provider.
97
-	 *
98
-	 * @return string Containing only [a-zA-Z0-9]
99
-	 */
100
-	public function identifier() {
101
-		return 'ocMailShare';
102
-	}
103
-
104
-	/**
105
-	 * DefaultShareProvider constructor.
106
-	 *
107
-	 * @param IDBConnection $connection
108
-	 * @param ISecureRandom $secureRandom
109
-	 * @param IUserManager $userManager
110
-	 * @param IRootFolder $rootFolder
111
-	 * @param IL10N $l
112
-	 * @param ILogger $logger
113
-	 * @param IMailer $mailer
114
-	 * @param IURLGenerator $urlGenerator
115
-	 * @param IManager $activityManager
116
-	 * @param SettingsManager $settingsManager
117
-	 * @param Defaults $defaults
118
-	 * @param IHasher $hasher
119
-	 * @param CapabilitiesManager $capabilitiesManager
120
-	 */
121
-	public function __construct(
122
-		IDBConnection $connection,
123
-		ISecureRandom $secureRandom,
124
-		IUserManager $userManager,
125
-		IRootFolder $rootFolder,
126
-		IL10N $l,
127
-		ILogger $logger,
128
-		IMailer $mailer,
129
-		IURLGenerator $urlGenerator,
130
-		IManager $activityManager,
131
-		SettingsManager $settingsManager,
132
-		Defaults $defaults,
133
-		IHasher $hasher,
134
-		CapabilitiesManager $capabilitiesManager
135
-	) {
136
-		$this->dbConnection = $connection;
137
-		$this->secureRandom = $secureRandom;
138
-		$this->userManager = $userManager;
139
-		$this->rootFolder = $rootFolder;
140
-		$this->l = $l;
141
-		$this->logger = $logger;
142
-		$this->mailer = $mailer;
143
-		$this->urlGenerator = $urlGenerator;
144
-		$this->activityManager = $activityManager;
145
-		$this->settingsManager = $settingsManager;
146
-		$this->defaults = $defaults;
147
-		$this->hasher = $hasher;
148
-		$this->capabilitiesManager = $capabilitiesManager;
149
-	}
150
-
151
-	/**
152
-	 * Share a path
153
-	 *
154
-	 * @param IShare $share
155
-	 * @return IShare The share object
156
-	 * @throws ShareNotFound
157
-	 * @throws \Exception
158
-	 */
159
-	public function create(IShare $share) {
160
-
161
-		$shareWith = $share->getSharedWith();
162
-		/*
56
+    /** @var  IDBConnection */
57
+    private $dbConnection;
58
+
59
+    /** @var ILogger */
60
+    private $logger;
61
+
62
+    /** @var ISecureRandom */
63
+    private $secureRandom;
64
+
65
+    /** @var IUserManager */
66
+    private $userManager;
67
+
68
+    /** @var IRootFolder */
69
+    private $rootFolder;
70
+
71
+    /** @var IL10N */
72
+    private $l;
73
+
74
+    /** @var IMailer */
75
+    private $mailer;
76
+
77
+    /** @var IURLGenerator */
78
+    private $urlGenerator;
79
+
80
+    /** @var IManager  */
81
+    private $activityManager;
82
+
83
+    /** @var SettingsManager */
84
+    private $settingsManager;
85
+
86
+    /** @var Defaults */
87
+    private $defaults;
88
+
89
+    /** @var IHasher */
90
+    private $hasher;
91
+
92
+    /** @var  CapabilitiesManager */
93
+    private $capabilitiesManager;
94
+
95
+    /**
96
+     * Return the identifier of this provider.
97
+     *
98
+     * @return string Containing only [a-zA-Z0-9]
99
+     */
100
+    public function identifier() {
101
+        return 'ocMailShare';
102
+    }
103
+
104
+    /**
105
+     * DefaultShareProvider constructor.
106
+     *
107
+     * @param IDBConnection $connection
108
+     * @param ISecureRandom $secureRandom
109
+     * @param IUserManager $userManager
110
+     * @param IRootFolder $rootFolder
111
+     * @param IL10N $l
112
+     * @param ILogger $logger
113
+     * @param IMailer $mailer
114
+     * @param IURLGenerator $urlGenerator
115
+     * @param IManager $activityManager
116
+     * @param SettingsManager $settingsManager
117
+     * @param Defaults $defaults
118
+     * @param IHasher $hasher
119
+     * @param CapabilitiesManager $capabilitiesManager
120
+     */
121
+    public function __construct(
122
+        IDBConnection $connection,
123
+        ISecureRandom $secureRandom,
124
+        IUserManager $userManager,
125
+        IRootFolder $rootFolder,
126
+        IL10N $l,
127
+        ILogger $logger,
128
+        IMailer $mailer,
129
+        IURLGenerator $urlGenerator,
130
+        IManager $activityManager,
131
+        SettingsManager $settingsManager,
132
+        Defaults $defaults,
133
+        IHasher $hasher,
134
+        CapabilitiesManager $capabilitiesManager
135
+    ) {
136
+        $this->dbConnection = $connection;
137
+        $this->secureRandom = $secureRandom;
138
+        $this->userManager = $userManager;
139
+        $this->rootFolder = $rootFolder;
140
+        $this->l = $l;
141
+        $this->logger = $logger;
142
+        $this->mailer = $mailer;
143
+        $this->urlGenerator = $urlGenerator;
144
+        $this->activityManager = $activityManager;
145
+        $this->settingsManager = $settingsManager;
146
+        $this->defaults = $defaults;
147
+        $this->hasher = $hasher;
148
+        $this->capabilitiesManager = $capabilitiesManager;
149
+    }
150
+
151
+    /**
152
+     * Share a path
153
+     *
154
+     * @param IShare $share
155
+     * @return IShare The share object
156
+     * @throws ShareNotFound
157
+     * @throws \Exception
158
+     */
159
+    public function create(IShare $share) {
160
+
161
+        $shareWith = $share->getSharedWith();
162
+        /*
163 163
 		 * Check if file is not already shared with the remote user
164 164
 		 */
165
-		$alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0);
166
-		if (!empty($alreadyShared)) {
167
-			$message = 'Sharing %s failed, this item is already shared with %s';
168
-			$message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
169
-			$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
170
-			throw new \Exception($message_t);
171
-		}
172
-
173
-		// if the admin enforces a password for all mail shares we create a
174
-		// random password and send it to the recipient
175
-		$password = '';
176
-		$passwordEnforced = $this->settingsManager->enforcePasswordProtection();
177
-		if ($passwordEnforced) {
178
-			$password = $this->autoGeneratePassword($share);
179
-		}
180
-
181
-		$shareId = $this->createMailShare($share);
182
-		$send = $this->sendPassword($share, $password);
183
-		if ($passwordEnforced && $send === false) {
184
-			$this->sendPasswordToOwner($share, $password);
185
-		}
186
-
187
-		$this->createShareActivity($share);
188
-		$data = $this->getRawShare($shareId);
189
-
190
-		return $this->createShareObject($data);
191
-
192
-	}
193
-
194
-	/**
195
-	 * auto generate password in case of password enforcement on mail shares
196
-	 *
197
-	 * @param IShare $share
198
-	 * @return string
199
-	 * @throws \Exception
200
-	 */
201
-	protected function autoGeneratePassword($share) {
202
-		$initiatorUser = $this->userManager->get($share->getSharedBy());
203
-		$initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
204
-		$allowPasswordByMail = $this->settingsManager->sendPasswordByMail();
205
-
206
-		if ($initiatorEMailAddress === null && !$allowPasswordByMail) {
207
-			throw new \Exception(
208
-				$this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.")
209
-			);
210
-		}
211
-
212
-		$passwordPolicy = $this->getPasswordPolicy();
213
-		$passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS;
214
-		$passwordLength = 8;
215
-		if (!empty($passwordPolicy)) {
216
-			$passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength;
217
-			$passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : '';
218
-		}
219
-
220
-		$password = $this->secureRandom->generate($passwordLength, $passwordCharset);
221
-
222
-		$share->setPassword($this->hasher->hash($password));
223
-
224
-		return $password;
225
-	}
226
-
227
-	/**
228
-	 * get password policy
229
-	 *
230
-	 * @return array
231
-	 */
232
-	protected function getPasswordPolicy() {
233
-		$capabilities = $this->capabilitiesManager->getCapabilities();
234
-		if (isset($capabilities['password_policy'])) {
235
-			return $capabilities['password_policy'];
236
-		}
237
-
238
-		return [];
239
-	}
240
-
241
-	/**
242
-	 * create activity if a file/folder was shared by mail
243
-	 *
244
-	 * @param IShare $share
245
-	 */
246
-	protected function createShareActivity(IShare $share) {
247
-
248
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
249
-
250
-		$this->publishActivity(
251
-			Activity::SUBJECT_SHARED_EMAIL_SELF,
252
-			[$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()],
253
-			$share->getSharedBy(),
254
-			$share->getNode()->getId(),
255
-			$userFolder->getRelativePath($share->getNode()->getPath())
256
-		);
257
-
258
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
259
-			$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
260
-			$fileId = $share->getNode()->getId();
261
-			$nodes = $ownerFolder->getById($fileId);
262
-			$ownerPath = $nodes[0]->getPath();
263
-			$this->publishActivity(
264
-				Activity::SUBJECT_SHARED_EMAIL_BY,
265
-				[$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()],
266
-				$share->getShareOwner(),
267
-				$fileId,
268
-				$ownerFolder->getRelativePath($ownerPath)
269
-			);
270
-		}
271
-
272
-	}
273
-
274
-	/**
275
-	 * create activity if a file/folder was shared by mail
276
-	 *
277
-	 * @param IShare $share
278
-	 * @param string $sharedWith
279
-	 * @param bool $sendToSelf
280
-	 */
281
-	protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) {
282
-
283
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
284
-
285
-		if ($sendToSelf) {
286
-			$this->publishActivity(
287
-				Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF,
288
-				[$userFolder->getRelativePath($share->getNode()->getPath())],
289
-				$share->getSharedBy(),
290
-				$share->getNode()->getId(),
291
-				$userFolder->getRelativePath($share->getNode()->getPath())
292
-			);
293
-		} else {
294
-			$this->publishActivity(
295
-				Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND,
296
-				[$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith],
297
-				$share->getSharedBy(),
298
-				$share->getNode()->getId(),
299
-				$userFolder->getRelativePath($share->getNode()->getPath())
300
-			);
301
-		}
302
-	}
303
-
304
-
305
-	/**
306
-	 * publish activity if a file/folder was shared by mail
307
-	 *
308
-	 * @param $subject
309
-	 * @param $parameters
310
-	 * @param $affectedUser
311
-	 * @param $fileId
312
-	 * @param $filePath
313
-	 */
314
-	protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) {
315
-		$event = $this->activityManager->generateEvent();
316
-		$event->setApp('sharebymail')
317
-			->setType('shared')
318
-			->setSubject($subject, $parameters)
319
-			->setAffectedUser($affectedUser)
320
-			->setObject('files', $fileId, $filePath);
321
-		$this->activityManager->publish($event);
322
-
323
-	}
324
-
325
-	/**
326
-	 * @param IShare $share
327
-	 * @return int
328
-	 * @throws \Exception
329
-	 */
330
-	protected function createMailShare(IShare $share) {
331
-		$share->setToken($this->generateToken());
332
-		$shareId = $this->addShareToDB(
333
-			$share->getNodeId(),
334
-			$share->getNodeType(),
335
-			$share->getSharedWith(),
336
-			$share->getSharedBy(),
337
-			$share->getShareOwner(),
338
-			$share->getPermissions(),
339
-			$share->getToken(),
340
-			$share->getPassword()
341
-		);
342
-
343
-		try {
344
-			$link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare',
345
-				['token' => $share->getToken()]);
346
-			$this->sendMailNotification(
347
-				$share->getNode()->getName(),
348
-				$link,
349
-				$share->getSharedBy(),
350
-				$share->getSharedWith()
351
-			);
352
-		} catch (HintException $hintException) {
353
-			$this->logger->error('Failed to send share by mail: ' . $hintException->getMessage());
354
-			$this->removeShareFromTable($shareId);
355
-			throw $hintException;
356
-		} catch (\Exception $e) {
357
-			$this->logger->error('Failed to send share by email: ' . $e->getMessage());
358
-			$this->removeShareFromTable($shareId);
359
-			throw new HintException('Failed to send share by mail',
360
-				$this->l->t('Failed to send share by email'));
361
-		}
362
-
363
-		return $shareId;
364
-
365
-	}
366
-
367
-	/**
368
-	 * @param string $filename
369
-	 * @param string $link
370
-	 * @param string $initiator
371
-	 * @param string $shareWith
372
-	 * @throws \Exception If mail couldn't be sent
373
-	 */
374
-	protected function sendMailNotification($filename,
375
-											$link,
376
-											$initiator,
377
-											$shareWith) {
378
-		$initiatorUser = $this->userManager->get($initiator);
379
-		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
380
-		$subject = (string)$this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename));
381
-
382
-		$message = $this->mailer->createMessage();
383
-
384
-		$emailTemplate = $this->mailer->createEMailTemplate();
385
-
386
-		$emailTemplate->addHeader();
387
-		$emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
388
-		$text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
389
-
390
-		$emailTemplate->addBodyText(
391
-			$text . ' ' . $this->l->t('Click the button below to open it.'),
392
-			$text
393
-		);
394
-		$emailTemplate->addBodyButton(
395
-			$this->l->t('Open »%s«', [$filename]),
396
-			$link
397
-		);
398
-
399
-		$message->setTo([$shareWith]);
400
-
401
-		// The "From" contains the sharers name
402
-		$instanceName = $this->defaults->getName();
403
-		$senderName = $this->l->t(
404
-			'%s via %s',
405
-			[
406
-				$initiatorDisplayName,
407
-				$instanceName
408
-			]
409
-		);
410
-		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
411
-
412
-		// The "Reply-To" is set to the sharer if an mail address is configured
413
-		// also the default footer contains a "Do not reply" which needs to be adjusted.
414
-		$initiatorEmail = $initiatorUser->getEMailAddress();
415
-		if($initiatorEmail !== null) {
416
-			$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
417
-			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
418
-		} else {
419
-			$emailTemplate->addFooter();
420
-		}
421
-
422
-		$message->setSubject($subject);
423
-		$message->setPlainBody($emailTemplate->renderText());
424
-		$message->setHtmlBody($emailTemplate->renderHtml());
425
-		$this->mailer->send($message);
426
-	}
427
-
428
-	/**
429
-	 * send password to recipient of a mail share
430
-	 *
431
-	 * @param IShare $share
432
-	 * @param string $password
433
-	 * @return bool
434
-	 */
435
-	protected function sendPassword(IShare $share, $password) {
436
-
437
-		$filename = $share->getNode()->getName();
438
-		$initiator = $share->getSharedBy();
439
-		$shareWith = $share->getSharedWith();
440
-
441
-		if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) {
442
-			return false;
443
-		}
444
-
445
-		$initiatorUser = $this->userManager->get($initiator);
446
-		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
447
-		$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
448
-
449
-		$subject = (string)$this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]);
450
-		$plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]);
451
-		$htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]);
452
-
453
-		$message = $this->mailer->createMessage();
454
-
455
-		$emailTemplate = $this->mailer->createEMailTemplate();
456
-		$emailTemplate->addHeader();
457
-		$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
458
-		$emailTemplate->addBodyText($htmlBodyPart, $plainBodyPart);
459
-		$emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password]));
460
-
461
-		// The "From" contains the sharers name
462
-		$instanceName = $this->defaults->getName();
463
-		$senderName = $this->l->t(
464
-			'%s via %s',
465
-			[
466
-				$initiatorDisplayName,
467
-				$instanceName
468
-			]
469
-		);
470
-		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
471
-		if ($initiatorEmailAddress !== null) {
472
-			$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
473
-			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
474
-		} else {
475
-			$emailTemplate->addFooter();
476
-		}
477
-
478
-		$message->setTo([$shareWith]);
479
-		$message->setSubject($subject);
480
-		$message->setBody($emailTemplate->renderText(), 'text/plain');
481
-		$message->setHtmlBody($emailTemplate->renderHtml());
482
-		$this->mailer->send($message);
483
-
484
-		$this->createPasswordSendActivity($share, $shareWith, false);
485
-
486
-		return true;
487
-	}
488
-
489
-	/**
490
-	 * send auto generated password to the owner. This happens if the admin enforces
491
-	 * a password for mail shares and forbid to send the password by mail to the recipient
492
-	 *
493
-	 * @param IShare $share
494
-	 * @param string $password
495
-	 * @return bool
496
-	 * @throws \Exception
497
-	 */
498
-	protected function sendPasswordToOwner(IShare $share, $password) {
499
-
500
-		$filename = $share->getNode()->getName();
501
-		$initiator = $this->userManager->get($share->getSharedBy());
502
-		$initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null;
503
-		$initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy();
504
-		$shareWith = $share->getSharedWith();
505
-
506
-		if ($initiatorEMailAddress === null) {
507
-			throw new \Exception(
508
-				$this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.")
509
-			);
510
-		}
511
-
512
-		$subject = (string)$this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]);
513
-		$bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]);
514
-
515
-		$message = $this->mailer->createMessage();
516
-		$emailTemplate = $this->mailer->createEMailTemplate();
517
-
518
-		$emailTemplate->addHeader();
519
-		$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
520
-		$emailTemplate->addBodyText($bodyPart);
521
-		$emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password]));
522
-		$emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.'));
523
-		$emailTemplate->addFooter();
524
-
525
-		if ($initiatorEMailAddress) {
526
-			$message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]);
527
-		}
528
-		$message->setTo([$initiatorEMailAddress => $initiatorDisplayName]);
529
-		$message->setSubject($subject);
530
-		$message->setBody($emailTemplate->renderText(), 'text/plain');
531
-		$message->setHtmlBody($emailTemplate->renderHtml());
532
-		$this->mailer->send($message);
533
-
534
-		$this->createPasswordSendActivity($share, $shareWith, true);
535
-
536
-		return true;
537
-	}
538
-
539
-	/**
540
-	 * generate share token
541
-	 *
542
-	 * @return string
543
-	 */
544
-	protected function generateToken($size = 15) {
545
-		$token = $this->secureRandom->generate(
546
-			$size, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS);
547
-		return $token;
548
-	}
549
-
550
-	/**
551
-	 * Get all children of this share
552
-	 *
553
-	 * @param IShare $parent
554
-	 * @return IShare[]
555
-	 */
556
-	public function getChildren(IShare $parent) {
557
-		$children = [];
558
-
559
-		$qb = $this->dbConnection->getQueryBuilder();
560
-		$qb->select('*')
561
-			->from('share')
562
-			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
563
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
564
-			->orderBy('id');
565
-
566
-		$cursor = $qb->execute();
567
-		while($data = $cursor->fetch()) {
568
-			$children[] = $this->createShareObject($data);
569
-		}
570
-		$cursor->closeCursor();
571
-
572
-		return $children;
573
-	}
574
-
575
-	/**
576
-	 * add share to the database and return the ID
577
-	 *
578
-	 * @param int $itemSource
579
-	 * @param string $itemType
580
-	 * @param string $shareWith
581
-	 * @param string $sharedBy
582
-	 * @param string $uidOwner
583
-	 * @param int $permissions
584
-	 * @param string $token
585
-	 * @return int
586
-	 */
587
-	protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) {
588
-		$qb = $this->dbConnection->getQueryBuilder();
589
-		$qb->insert('share')
590
-			->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
591
-			->setValue('item_type', $qb->createNamedParameter($itemType))
592
-			->setValue('item_source', $qb->createNamedParameter($itemSource))
593
-			->setValue('file_source', $qb->createNamedParameter($itemSource))
594
-			->setValue('share_with', $qb->createNamedParameter($shareWith))
595
-			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
596
-			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
597
-			->setValue('permissions', $qb->createNamedParameter($permissions))
598
-			->setValue('token', $qb->createNamedParameter($token))
599
-			->setValue('password', $qb->createNamedParameter($password))
600
-			->setValue('stime', $qb->createNamedParameter(time()));
601
-
602
-		/*
165
+        $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0);
166
+        if (!empty($alreadyShared)) {
167
+            $message = 'Sharing %s failed, this item is already shared with %s';
168
+            $message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
169
+            $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
170
+            throw new \Exception($message_t);
171
+        }
172
+
173
+        // if the admin enforces a password for all mail shares we create a
174
+        // random password and send it to the recipient
175
+        $password = '';
176
+        $passwordEnforced = $this->settingsManager->enforcePasswordProtection();
177
+        if ($passwordEnforced) {
178
+            $password = $this->autoGeneratePassword($share);
179
+        }
180
+
181
+        $shareId = $this->createMailShare($share);
182
+        $send = $this->sendPassword($share, $password);
183
+        if ($passwordEnforced && $send === false) {
184
+            $this->sendPasswordToOwner($share, $password);
185
+        }
186
+
187
+        $this->createShareActivity($share);
188
+        $data = $this->getRawShare($shareId);
189
+
190
+        return $this->createShareObject($data);
191
+
192
+    }
193
+
194
+    /**
195
+     * auto generate password in case of password enforcement on mail shares
196
+     *
197
+     * @param IShare $share
198
+     * @return string
199
+     * @throws \Exception
200
+     */
201
+    protected function autoGeneratePassword($share) {
202
+        $initiatorUser = $this->userManager->get($share->getSharedBy());
203
+        $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
204
+        $allowPasswordByMail = $this->settingsManager->sendPasswordByMail();
205
+
206
+        if ($initiatorEMailAddress === null && !$allowPasswordByMail) {
207
+            throw new \Exception(
208
+                $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.")
209
+            );
210
+        }
211
+
212
+        $passwordPolicy = $this->getPasswordPolicy();
213
+        $passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS;
214
+        $passwordLength = 8;
215
+        if (!empty($passwordPolicy)) {
216
+            $passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength;
217
+            $passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : '';
218
+        }
219
+
220
+        $password = $this->secureRandom->generate($passwordLength, $passwordCharset);
221
+
222
+        $share->setPassword($this->hasher->hash($password));
223
+
224
+        return $password;
225
+    }
226
+
227
+    /**
228
+     * get password policy
229
+     *
230
+     * @return array
231
+     */
232
+    protected function getPasswordPolicy() {
233
+        $capabilities = $this->capabilitiesManager->getCapabilities();
234
+        if (isset($capabilities['password_policy'])) {
235
+            return $capabilities['password_policy'];
236
+        }
237
+
238
+        return [];
239
+    }
240
+
241
+    /**
242
+     * create activity if a file/folder was shared by mail
243
+     *
244
+     * @param IShare $share
245
+     */
246
+    protected function createShareActivity(IShare $share) {
247
+
248
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
249
+
250
+        $this->publishActivity(
251
+            Activity::SUBJECT_SHARED_EMAIL_SELF,
252
+            [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()],
253
+            $share->getSharedBy(),
254
+            $share->getNode()->getId(),
255
+            $userFolder->getRelativePath($share->getNode()->getPath())
256
+        );
257
+
258
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
259
+            $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
260
+            $fileId = $share->getNode()->getId();
261
+            $nodes = $ownerFolder->getById($fileId);
262
+            $ownerPath = $nodes[0]->getPath();
263
+            $this->publishActivity(
264
+                Activity::SUBJECT_SHARED_EMAIL_BY,
265
+                [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()],
266
+                $share->getShareOwner(),
267
+                $fileId,
268
+                $ownerFolder->getRelativePath($ownerPath)
269
+            );
270
+        }
271
+
272
+    }
273
+
274
+    /**
275
+     * create activity if a file/folder was shared by mail
276
+     *
277
+     * @param IShare $share
278
+     * @param string $sharedWith
279
+     * @param bool $sendToSelf
280
+     */
281
+    protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) {
282
+
283
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
284
+
285
+        if ($sendToSelf) {
286
+            $this->publishActivity(
287
+                Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF,
288
+                [$userFolder->getRelativePath($share->getNode()->getPath())],
289
+                $share->getSharedBy(),
290
+                $share->getNode()->getId(),
291
+                $userFolder->getRelativePath($share->getNode()->getPath())
292
+            );
293
+        } else {
294
+            $this->publishActivity(
295
+                Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND,
296
+                [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith],
297
+                $share->getSharedBy(),
298
+                $share->getNode()->getId(),
299
+                $userFolder->getRelativePath($share->getNode()->getPath())
300
+            );
301
+        }
302
+    }
303
+
304
+
305
+    /**
306
+     * publish activity if a file/folder was shared by mail
307
+     *
308
+     * @param $subject
309
+     * @param $parameters
310
+     * @param $affectedUser
311
+     * @param $fileId
312
+     * @param $filePath
313
+     */
314
+    protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) {
315
+        $event = $this->activityManager->generateEvent();
316
+        $event->setApp('sharebymail')
317
+            ->setType('shared')
318
+            ->setSubject($subject, $parameters)
319
+            ->setAffectedUser($affectedUser)
320
+            ->setObject('files', $fileId, $filePath);
321
+        $this->activityManager->publish($event);
322
+
323
+    }
324
+
325
+    /**
326
+     * @param IShare $share
327
+     * @return int
328
+     * @throws \Exception
329
+     */
330
+    protected function createMailShare(IShare $share) {
331
+        $share->setToken($this->generateToken());
332
+        $shareId = $this->addShareToDB(
333
+            $share->getNodeId(),
334
+            $share->getNodeType(),
335
+            $share->getSharedWith(),
336
+            $share->getSharedBy(),
337
+            $share->getShareOwner(),
338
+            $share->getPermissions(),
339
+            $share->getToken(),
340
+            $share->getPassword()
341
+        );
342
+
343
+        try {
344
+            $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare',
345
+                ['token' => $share->getToken()]);
346
+            $this->sendMailNotification(
347
+                $share->getNode()->getName(),
348
+                $link,
349
+                $share->getSharedBy(),
350
+                $share->getSharedWith()
351
+            );
352
+        } catch (HintException $hintException) {
353
+            $this->logger->error('Failed to send share by mail: ' . $hintException->getMessage());
354
+            $this->removeShareFromTable($shareId);
355
+            throw $hintException;
356
+        } catch (\Exception $e) {
357
+            $this->logger->error('Failed to send share by email: ' . $e->getMessage());
358
+            $this->removeShareFromTable($shareId);
359
+            throw new HintException('Failed to send share by mail',
360
+                $this->l->t('Failed to send share by email'));
361
+        }
362
+
363
+        return $shareId;
364
+
365
+    }
366
+
367
+    /**
368
+     * @param string $filename
369
+     * @param string $link
370
+     * @param string $initiator
371
+     * @param string $shareWith
372
+     * @throws \Exception If mail couldn't be sent
373
+     */
374
+    protected function sendMailNotification($filename,
375
+                                            $link,
376
+                                            $initiator,
377
+                                            $shareWith) {
378
+        $initiatorUser = $this->userManager->get($initiator);
379
+        $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
380
+        $subject = (string)$this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename));
381
+
382
+        $message = $this->mailer->createMessage();
383
+
384
+        $emailTemplate = $this->mailer->createEMailTemplate();
385
+
386
+        $emailTemplate->addHeader();
387
+        $emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
388
+        $text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
389
+
390
+        $emailTemplate->addBodyText(
391
+            $text . ' ' . $this->l->t('Click the button below to open it.'),
392
+            $text
393
+        );
394
+        $emailTemplate->addBodyButton(
395
+            $this->l->t('Open »%s«', [$filename]),
396
+            $link
397
+        );
398
+
399
+        $message->setTo([$shareWith]);
400
+
401
+        // The "From" contains the sharers name
402
+        $instanceName = $this->defaults->getName();
403
+        $senderName = $this->l->t(
404
+            '%s via %s',
405
+            [
406
+                $initiatorDisplayName,
407
+                $instanceName
408
+            ]
409
+        );
410
+        $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
411
+
412
+        // The "Reply-To" is set to the sharer if an mail address is configured
413
+        // also the default footer contains a "Do not reply" which needs to be adjusted.
414
+        $initiatorEmail = $initiatorUser->getEMailAddress();
415
+        if($initiatorEmail !== null) {
416
+            $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
417
+            $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
418
+        } else {
419
+            $emailTemplate->addFooter();
420
+        }
421
+
422
+        $message->setSubject($subject);
423
+        $message->setPlainBody($emailTemplate->renderText());
424
+        $message->setHtmlBody($emailTemplate->renderHtml());
425
+        $this->mailer->send($message);
426
+    }
427
+
428
+    /**
429
+     * send password to recipient of a mail share
430
+     *
431
+     * @param IShare $share
432
+     * @param string $password
433
+     * @return bool
434
+     */
435
+    protected function sendPassword(IShare $share, $password) {
436
+
437
+        $filename = $share->getNode()->getName();
438
+        $initiator = $share->getSharedBy();
439
+        $shareWith = $share->getSharedWith();
440
+
441
+        if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) {
442
+            return false;
443
+        }
444
+
445
+        $initiatorUser = $this->userManager->get($initiator);
446
+        $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
447
+        $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
448
+
449
+        $subject = (string)$this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]);
450
+        $plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]);
451
+        $htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]);
452
+
453
+        $message = $this->mailer->createMessage();
454
+
455
+        $emailTemplate = $this->mailer->createEMailTemplate();
456
+        $emailTemplate->addHeader();
457
+        $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
458
+        $emailTemplate->addBodyText($htmlBodyPart, $plainBodyPart);
459
+        $emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password]));
460
+
461
+        // The "From" contains the sharers name
462
+        $instanceName = $this->defaults->getName();
463
+        $senderName = $this->l->t(
464
+            '%s via %s',
465
+            [
466
+                $initiatorDisplayName,
467
+                $instanceName
468
+            ]
469
+        );
470
+        $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
471
+        if ($initiatorEmailAddress !== null) {
472
+            $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
473
+            $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
474
+        } else {
475
+            $emailTemplate->addFooter();
476
+        }
477
+
478
+        $message->setTo([$shareWith]);
479
+        $message->setSubject($subject);
480
+        $message->setBody($emailTemplate->renderText(), 'text/plain');
481
+        $message->setHtmlBody($emailTemplate->renderHtml());
482
+        $this->mailer->send($message);
483
+
484
+        $this->createPasswordSendActivity($share, $shareWith, false);
485
+
486
+        return true;
487
+    }
488
+
489
+    /**
490
+     * send auto generated password to the owner. This happens if the admin enforces
491
+     * a password for mail shares and forbid to send the password by mail to the recipient
492
+     *
493
+     * @param IShare $share
494
+     * @param string $password
495
+     * @return bool
496
+     * @throws \Exception
497
+     */
498
+    protected function sendPasswordToOwner(IShare $share, $password) {
499
+
500
+        $filename = $share->getNode()->getName();
501
+        $initiator = $this->userManager->get($share->getSharedBy());
502
+        $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null;
503
+        $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy();
504
+        $shareWith = $share->getSharedWith();
505
+
506
+        if ($initiatorEMailAddress === null) {
507
+            throw new \Exception(
508
+                $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.")
509
+            );
510
+        }
511
+
512
+        $subject = (string)$this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]);
513
+        $bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]);
514
+
515
+        $message = $this->mailer->createMessage();
516
+        $emailTemplate = $this->mailer->createEMailTemplate();
517
+
518
+        $emailTemplate->addHeader();
519
+        $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
520
+        $emailTemplate->addBodyText($bodyPart);
521
+        $emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password]));
522
+        $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.'));
523
+        $emailTemplate->addFooter();
524
+
525
+        if ($initiatorEMailAddress) {
526
+            $message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]);
527
+        }
528
+        $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]);
529
+        $message->setSubject($subject);
530
+        $message->setBody($emailTemplate->renderText(), 'text/plain');
531
+        $message->setHtmlBody($emailTemplate->renderHtml());
532
+        $this->mailer->send($message);
533
+
534
+        $this->createPasswordSendActivity($share, $shareWith, true);
535
+
536
+        return true;
537
+    }
538
+
539
+    /**
540
+     * generate share token
541
+     *
542
+     * @return string
543
+     */
544
+    protected function generateToken($size = 15) {
545
+        $token = $this->secureRandom->generate(
546
+            $size, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS);
547
+        return $token;
548
+    }
549
+
550
+    /**
551
+     * Get all children of this share
552
+     *
553
+     * @param IShare $parent
554
+     * @return IShare[]
555
+     */
556
+    public function getChildren(IShare $parent) {
557
+        $children = [];
558
+
559
+        $qb = $this->dbConnection->getQueryBuilder();
560
+        $qb->select('*')
561
+            ->from('share')
562
+            ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
563
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
564
+            ->orderBy('id');
565
+
566
+        $cursor = $qb->execute();
567
+        while($data = $cursor->fetch()) {
568
+            $children[] = $this->createShareObject($data);
569
+        }
570
+        $cursor->closeCursor();
571
+
572
+        return $children;
573
+    }
574
+
575
+    /**
576
+     * add share to the database and return the ID
577
+     *
578
+     * @param int $itemSource
579
+     * @param string $itemType
580
+     * @param string $shareWith
581
+     * @param string $sharedBy
582
+     * @param string $uidOwner
583
+     * @param int $permissions
584
+     * @param string $token
585
+     * @return int
586
+     */
587
+    protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) {
588
+        $qb = $this->dbConnection->getQueryBuilder();
589
+        $qb->insert('share')
590
+            ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
591
+            ->setValue('item_type', $qb->createNamedParameter($itemType))
592
+            ->setValue('item_source', $qb->createNamedParameter($itemSource))
593
+            ->setValue('file_source', $qb->createNamedParameter($itemSource))
594
+            ->setValue('share_with', $qb->createNamedParameter($shareWith))
595
+            ->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
596
+            ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
597
+            ->setValue('permissions', $qb->createNamedParameter($permissions))
598
+            ->setValue('token', $qb->createNamedParameter($token))
599
+            ->setValue('password', $qb->createNamedParameter($password))
600
+            ->setValue('stime', $qb->createNamedParameter(time()));
601
+
602
+        /*
603 603
 		 * Added to fix https://github.com/owncloud/core/issues/22215
604 604
 		 * Can be removed once we get rid of ajax/share.php
605 605
 		 */
606
-		$qb->setValue('file_target', $qb->createNamedParameter(''));
606
+        $qb->setValue('file_target', $qb->createNamedParameter(''));
607 607
 
608
-		$qb->execute();
609
-		$id = $qb->getLastInsertId();
608
+        $qb->execute();
609
+        $id = $qb->getLastInsertId();
610 610
 
611
-		return (int)$id;
612
-	}
611
+        return (int)$id;
612
+    }
613 613
 
614
-	/**
615
-	 * Update a share
616
-	 *
617
-	 * @param IShare $share
618
-	 * @param string|null $plainTextPassword
619
-	 * @return IShare The share object
620
-	 */
621
-	public function update(IShare $share, $plainTextPassword = null) {
614
+    /**
615
+     * Update a share
616
+     *
617
+     * @param IShare $share
618
+     * @param string|null $plainTextPassword
619
+     * @return IShare The share object
620
+     */
621
+    public function update(IShare $share, $plainTextPassword = null) {
622 622
 
623
-		$originalShare = $this->getShareById($share->getId());
623
+        $originalShare = $this->getShareById($share->getId());
624 624
 
625
-		// a real password was given
626
-		$validPassword = $plainTextPassword !== null && $plainTextPassword !== '';
625
+        // a real password was given
626
+        $validPassword = $plainTextPassword !== null && $plainTextPassword !== '';
627 627
 
628
-		if($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
629
-			$this->sendPassword($share, $plainTextPassword);
630
-		}
631
-		/*
628
+        if($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
629
+            $this->sendPassword($share, $plainTextPassword);
630
+        }
631
+        /*
632 632
 		 * We allow updating the permissions and password of mail shares
633 633
 		 */
634
-		$qb = $this->dbConnection->getQueryBuilder();
635
-		$qb->update('share')
636
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
637
-			->set('permissions', $qb->createNamedParameter($share->getPermissions()))
638
-			->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
639
-			->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
640
-			->set('password', $qb->createNamedParameter($share->getPassword()))
641
-			->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
642
-			->execute();
643
-
644
-		return $share;
645
-	}
646
-
647
-	/**
648
-	 * @inheritdoc
649
-	 */
650
-	public function move(IShare $share, $recipient) {
651
-		/**
652
-		 * nothing to do here, mail shares are only outgoing shares
653
-		 */
654
-		return $share;
655
-	}
656
-
657
-	/**
658
-	 * Delete a share (owner unShares the file)
659
-	 *
660
-	 * @param IShare $share
661
-	 */
662
-	public function delete(IShare $share) {
663
-		$this->removeShareFromTable($share->getId());
664
-	}
665
-
666
-	/**
667
-	 * @inheritdoc
668
-	 */
669
-	public function deleteFromSelf(IShare $share, $recipient) {
670
-		// nothing to do here, mail shares are only outgoing shares
671
-		return;
672
-	}
673
-
674
-	/**
675
-	 * @inheritdoc
676
-	 */
677
-	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
678
-		$qb = $this->dbConnection->getQueryBuilder();
679
-		$qb->select('*')
680
-			->from('share');
681
-
682
-		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
683
-
684
-		/**
685
-		 * Reshares for this user are shares where they are the owner.
686
-		 */
687
-		if ($reshares === false) {
688
-			//Special case for old shares created via the web UI
689
-			$or1 = $qb->expr()->andX(
690
-				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
691
-				$qb->expr()->isNull('uid_initiator')
692
-			);
693
-
694
-			$qb->andWhere(
695
-				$qb->expr()->orX(
696
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
697
-					$or1
698
-				)
699
-			);
700
-		} else {
701
-			$qb->andWhere(
702
-				$qb->expr()->orX(
703
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
704
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
705
-				)
706
-			);
707
-		}
708
-
709
-		if ($node !== null) {
710
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
711
-		}
712
-
713
-		if ($limit !== -1) {
714
-			$qb->setMaxResults($limit);
715
-		}
716
-
717
-		$qb->setFirstResult($offset);
718
-		$qb->orderBy('id');
719
-
720
-		$cursor = $qb->execute();
721
-		$shares = [];
722
-		while($data = $cursor->fetch()) {
723
-			$shares[] = $this->createShareObject($data);
724
-		}
725
-		$cursor->closeCursor();
726
-
727
-		return $shares;
728
-	}
729
-
730
-	/**
731
-	 * @inheritdoc
732
-	 */
733
-	public function getShareById($id, $recipientId = null) {
734
-		$qb = $this->dbConnection->getQueryBuilder();
735
-
736
-		$qb->select('*')
737
-			->from('share')
738
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
739
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
740
-
741
-		$cursor = $qb->execute();
742
-		$data = $cursor->fetch();
743
-		$cursor->closeCursor();
744
-
745
-		if ($data === false) {
746
-			throw new ShareNotFound();
747
-		}
748
-
749
-		try {
750
-			$share = $this->createShareObject($data);
751
-		} catch (InvalidShare $e) {
752
-			throw new ShareNotFound();
753
-		}
754
-
755
-		return $share;
756
-	}
757
-
758
-	/**
759
-	 * Get shares for a given path
760
-	 *
761
-	 * @param \OCP\Files\Node $path
762
-	 * @return IShare[]
763
-	 */
764
-	public function getSharesByPath(Node $path) {
765
-		$qb = $this->dbConnection->getQueryBuilder();
766
-
767
-		$cursor = $qb->select('*')
768
-			->from('share')
769
-			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
770
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
771
-			->execute();
772
-
773
-		$shares = [];
774
-		while($data = $cursor->fetch()) {
775
-			$shares[] = $this->createShareObject($data);
776
-		}
777
-		$cursor->closeCursor();
778
-
779
-		return $shares;
780
-	}
781
-
782
-	/**
783
-	 * @inheritdoc
784
-	 */
785
-	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
786
-		/** @var IShare[] $shares */
787
-		$shares = [];
788
-
789
-		//Get shares directly with this user
790
-		$qb = $this->dbConnection->getQueryBuilder();
791
-		$qb->select('*')
792
-			->from('share');
793
-
794
-		// Order by id
795
-		$qb->orderBy('id');
796
-
797
-		// Set limit and offset
798
-		if ($limit !== -1) {
799
-			$qb->setMaxResults($limit);
800
-		}
801
-		$qb->setFirstResult($offset);
802
-
803
-		$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
804
-		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
805
-
806
-		// Filter by node if provided
807
-		if ($node !== null) {
808
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
809
-		}
810
-
811
-		$cursor = $qb->execute();
812
-
813
-		while($data = $cursor->fetch()) {
814
-			$shares[] = $this->createShareObject($data);
815
-		}
816
-		$cursor->closeCursor();
817
-
818
-
819
-		return $shares;
820
-	}
821
-
822
-	/**
823
-	 * Get a share by token
824
-	 *
825
-	 * @param string $token
826
-	 * @return IShare
827
-	 * @throws ShareNotFound
828
-	 */
829
-	public function getShareByToken($token) {
830
-		$qb = $this->dbConnection->getQueryBuilder();
831
-
832
-		$cursor = $qb->select('*')
833
-			->from('share')
834
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
835
-			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
836
-			->execute();
837
-
838
-		$data = $cursor->fetch();
839
-
840
-		if ($data === false) {
841
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
842
-		}
843
-
844
-		try {
845
-			$share = $this->createShareObject($data);
846
-		} catch (InvalidShare $e) {
847
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
848
-		}
849
-
850
-		return $share;
851
-	}
852
-
853
-	/**
854
-	 * remove share from table
855
-	 *
856
-	 * @param string $shareId
857
-	 */
858
-	protected function removeShareFromTable($shareId) {
859
-		$qb = $this->dbConnection->getQueryBuilder();
860
-		$qb->delete('share')
861
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
862
-		$qb->execute();
863
-	}
864
-
865
-	/**
866
-	 * Create a share object from an database row
867
-	 *
868
-	 * @param array $data
869
-	 * @return IShare
870
-	 * @throws InvalidShare
871
-	 * @throws ShareNotFound
872
-	 */
873
-	protected function createShareObject($data) {
874
-
875
-		$share = new Share($this->rootFolder, $this->userManager);
876
-		$share->setId((int)$data['id'])
877
-			->setShareType((int)$data['share_type'])
878
-			->setPermissions((int)$data['permissions'])
879
-			->setTarget($data['file_target'])
880
-			->setMailSend((bool)$data['mail_send'])
881
-			->setToken($data['token']);
882
-
883
-		$shareTime = new \DateTime();
884
-		$shareTime->setTimestamp((int)$data['stime']);
885
-		$share->setShareTime($shareTime);
886
-		$share->setSharedWith($data['share_with']);
887
-		$share->setPassword($data['password']);
888
-
889
-		if ($data['uid_initiator'] !== null) {
890
-			$share->setShareOwner($data['uid_owner']);
891
-			$share->setSharedBy($data['uid_initiator']);
892
-		} else {
893
-			//OLD SHARE
894
-			$share->setSharedBy($data['uid_owner']);
895
-			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
896
-
897
-			$owner = $path->getOwner();
898
-			$share->setShareOwner($owner->getUID());
899
-		}
900
-
901
-		if ($data['expiration'] !== null) {
902
-			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
903
-			if ($expiration !== false) {
904
-				$share->setExpirationDate($expiration);
905
-			}
906
-		}
907
-
908
-		$share->setNodeId((int)$data['file_source']);
909
-		$share->setNodeType($data['item_type']);
910
-
911
-		$share->setProviderId($this->identifier());
912
-
913
-		return $share;
914
-	}
915
-
916
-	/**
917
-	 * Get the node with file $id for $user
918
-	 *
919
-	 * @param string $userId
920
-	 * @param int $id
921
-	 * @return \OCP\Files\File|\OCP\Files\Folder
922
-	 * @throws InvalidShare
923
-	 */
924
-	private function getNode($userId, $id) {
925
-		try {
926
-			$userFolder = $this->rootFolder->getUserFolder($userId);
927
-		} catch (NoUserException $e) {
928
-			throw new InvalidShare();
929
-		}
930
-
931
-		$nodes = $userFolder->getById($id);
932
-
933
-		if (empty($nodes)) {
934
-			throw new InvalidShare();
935
-		}
936
-
937
-		return $nodes[0];
938
-	}
939
-
940
-	/**
941
-	 * A user is deleted from the system
942
-	 * So clean up the relevant shares.
943
-	 *
944
-	 * @param string $uid
945
-	 * @param int $shareType
946
-	 */
947
-	public function userDeleted($uid, $shareType) {
948
-		$qb = $this->dbConnection->getQueryBuilder();
949
-
950
-		$qb->delete('share')
951
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
952
-			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
953
-			->execute();
954
-	}
955
-
956
-	/**
957
-	 * This provider does not support group shares
958
-	 *
959
-	 * @param string $gid
960
-	 */
961
-	public function groupDeleted($gid) {
962
-		return;
963
-	}
964
-
965
-	/**
966
-	 * This provider does not support group shares
967
-	 *
968
-	 * @param string $uid
969
-	 * @param string $gid
970
-	 */
971
-	public function userDeletedFromGroup($uid, $gid) {
972
-		return;
973
-	}
974
-
975
-	/**
976
-	 * get database row of a give share
977
-	 *
978
-	 * @param $id
979
-	 * @return array
980
-	 * @throws ShareNotFound
981
-	 */
982
-	protected function getRawShare($id) {
983
-
984
-		// Now fetch the inserted share and create a complete share object
985
-		$qb = $this->dbConnection->getQueryBuilder();
986
-		$qb->select('*')
987
-			->from('share')
988
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
989
-
990
-		$cursor = $qb->execute();
991
-		$data = $cursor->fetch();
992
-		$cursor->closeCursor();
993
-
994
-		if ($data === false) {
995
-			throw new ShareNotFound;
996
-		}
997
-
998
-		return $data;
999
-	}
1000
-
1001
-	public function getSharesInFolder($userId, Folder $node, $reshares) {
1002
-		$qb = $this->dbConnection->getQueryBuilder();
1003
-		$qb->select('*')
1004
-			->from('share', 's')
1005
-			->andWhere($qb->expr()->orX(
1006
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1007
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1008
-			))
1009
-			->andWhere(
1010
-				$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
1011
-			);
1012
-
1013
-		/**
1014
-		 * Reshares for this user are shares where they are the owner.
1015
-		 */
1016
-		if ($reshares === false) {
1017
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
1018
-		} else {
1019
-			$qb->andWhere(
1020
-				$qb->expr()->orX(
1021
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
1022
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
1023
-				)
1024
-			);
1025
-		}
1026
-
1027
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
1028
-		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
1029
-
1030
-		$qb->orderBy('id');
1031
-
1032
-		$cursor = $qb->execute();
1033
-		$shares = [];
1034
-		while ($data = $cursor->fetch()) {
1035
-			$shares[$data['fileid']][] = $this->createShareObject($data);
1036
-		}
1037
-		$cursor->closeCursor();
1038
-
1039
-		return $shares;
1040
-	}
1041
-
1042
-	/**
1043
-	 * @inheritdoc
1044
-	 */
1045
-	public function getAccessList($nodes, $currentAccess) {
1046
-		$ids = [];
1047
-		foreach ($nodes as $node) {
1048
-			$ids[] = $node->getId();
1049
-		}
1050
-
1051
-		$qb = $this->dbConnection->getQueryBuilder();
1052
-		$qb->select('share_with')
1053
-			->from('share')
1054
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
1055
-			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1056
-			->andWhere($qb->expr()->orX(
1057
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1058
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1059
-			))
1060
-			->setMaxResults(1);
1061
-		$cursor = $qb->execute();
1062
-
1063
-		$mail = $cursor->fetch() !== false;
1064
-		$cursor->closeCursor();
1065
-
1066
-		return ['public' => $mail];
1067
-	}
634
+        $qb = $this->dbConnection->getQueryBuilder();
635
+        $qb->update('share')
636
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
637
+            ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
638
+            ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
639
+            ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
640
+            ->set('password', $qb->createNamedParameter($share->getPassword()))
641
+            ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
642
+            ->execute();
643
+
644
+        return $share;
645
+    }
646
+
647
+    /**
648
+     * @inheritdoc
649
+     */
650
+    public function move(IShare $share, $recipient) {
651
+        /**
652
+         * nothing to do here, mail shares are only outgoing shares
653
+         */
654
+        return $share;
655
+    }
656
+
657
+    /**
658
+     * Delete a share (owner unShares the file)
659
+     *
660
+     * @param IShare $share
661
+     */
662
+    public function delete(IShare $share) {
663
+        $this->removeShareFromTable($share->getId());
664
+    }
665
+
666
+    /**
667
+     * @inheritdoc
668
+     */
669
+    public function deleteFromSelf(IShare $share, $recipient) {
670
+        // nothing to do here, mail shares are only outgoing shares
671
+        return;
672
+    }
673
+
674
+    /**
675
+     * @inheritdoc
676
+     */
677
+    public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
678
+        $qb = $this->dbConnection->getQueryBuilder();
679
+        $qb->select('*')
680
+            ->from('share');
681
+
682
+        $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
683
+
684
+        /**
685
+         * Reshares for this user are shares where they are the owner.
686
+         */
687
+        if ($reshares === false) {
688
+            //Special case for old shares created via the web UI
689
+            $or1 = $qb->expr()->andX(
690
+                $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
691
+                $qb->expr()->isNull('uid_initiator')
692
+            );
693
+
694
+            $qb->andWhere(
695
+                $qb->expr()->orX(
696
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
697
+                    $or1
698
+                )
699
+            );
700
+        } else {
701
+            $qb->andWhere(
702
+                $qb->expr()->orX(
703
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
704
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
705
+                )
706
+            );
707
+        }
708
+
709
+        if ($node !== null) {
710
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
711
+        }
712
+
713
+        if ($limit !== -1) {
714
+            $qb->setMaxResults($limit);
715
+        }
716
+
717
+        $qb->setFirstResult($offset);
718
+        $qb->orderBy('id');
719
+
720
+        $cursor = $qb->execute();
721
+        $shares = [];
722
+        while($data = $cursor->fetch()) {
723
+            $shares[] = $this->createShareObject($data);
724
+        }
725
+        $cursor->closeCursor();
726
+
727
+        return $shares;
728
+    }
729
+
730
+    /**
731
+     * @inheritdoc
732
+     */
733
+    public function getShareById($id, $recipientId = null) {
734
+        $qb = $this->dbConnection->getQueryBuilder();
735
+
736
+        $qb->select('*')
737
+            ->from('share')
738
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
739
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
740
+
741
+        $cursor = $qb->execute();
742
+        $data = $cursor->fetch();
743
+        $cursor->closeCursor();
744
+
745
+        if ($data === false) {
746
+            throw new ShareNotFound();
747
+        }
748
+
749
+        try {
750
+            $share = $this->createShareObject($data);
751
+        } catch (InvalidShare $e) {
752
+            throw new ShareNotFound();
753
+        }
754
+
755
+        return $share;
756
+    }
757
+
758
+    /**
759
+     * Get shares for a given path
760
+     *
761
+     * @param \OCP\Files\Node $path
762
+     * @return IShare[]
763
+     */
764
+    public function getSharesByPath(Node $path) {
765
+        $qb = $this->dbConnection->getQueryBuilder();
766
+
767
+        $cursor = $qb->select('*')
768
+            ->from('share')
769
+            ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
770
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
771
+            ->execute();
772
+
773
+        $shares = [];
774
+        while($data = $cursor->fetch()) {
775
+            $shares[] = $this->createShareObject($data);
776
+        }
777
+        $cursor->closeCursor();
778
+
779
+        return $shares;
780
+    }
781
+
782
+    /**
783
+     * @inheritdoc
784
+     */
785
+    public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
786
+        /** @var IShare[] $shares */
787
+        $shares = [];
788
+
789
+        //Get shares directly with this user
790
+        $qb = $this->dbConnection->getQueryBuilder();
791
+        $qb->select('*')
792
+            ->from('share');
793
+
794
+        // Order by id
795
+        $qb->orderBy('id');
796
+
797
+        // Set limit and offset
798
+        if ($limit !== -1) {
799
+            $qb->setMaxResults($limit);
800
+        }
801
+        $qb->setFirstResult($offset);
802
+
803
+        $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
804
+        $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
805
+
806
+        // Filter by node if provided
807
+        if ($node !== null) {
808
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
809
+        }
810
+
811
+        $cursor = $qb->execute();
812
+
813
+        while($data = $cursor->fetch()) {
814
+            $shares[] = $this->createShareObject($data);
815
+        }
816
+        $cursor->closeCursor();
817
+
818
+
819
+        return $shares;
820
+    }
821
+
822
+    /**
823
+     * Get a share by token
824
+     *
825
+     * @param string $token
826
+     * @return IShare
827
+     * @throws ShareNotFound
828
+     */
829
+    public function getShareByToken($token) {
830
+        $qb = $this->dbConnection->getQueryBuilder();
831
+
832
+        $cursor = $qb->select('*')
833
+            ->from('share')
834
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
835
+            ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
836
+            ->execute();
837
+
838
+        $data = $cursor->fetch();
839
+
840
+        if ($data === false) {
841
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
842
+        }
843
+
844
+        try {
845
+            $share = $this->createShareObject($data);
846
+        } catch (InvalidShare $e) {
847
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
848
+        }
849
+
850
+        return $share;
851
+    }
852
+
853
+    /**
854
+     * remove share from table
855
+     *
856
+     * @param string $shareId
857
+     */
858
+    protected function removeShareFromTable($shareId) {
859
+        $qb = $this->dbConnection->getQueryBuilder();
860
+        $qb->delete('share')
861
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
862
+        $qb->execute();
863
+    }
864
+
865
+    /**
866
+     * Create a share object from an database row
867
+     *
868
+     * @param array $data
869
+     * @return IShare
870
+     * @throws InvalidShare
871
+     * @throws ShareNotFound
872
+     */
873
+    protected function createShareObject($data) {
874
+
875
+        $share = new Share($this->rootFolder, $this->userManager);
876
+        $share->setId((int)$data['id'])
877
+            ->setShareType((int)$data['share_type'])
878
+            ->setPermissions((int)$data['permissions'])
879
+            ->setTarget($data['file_target'])
880
+            ->setMailSend((bool)$data['mail_send'])
881
+            ->setToken($data['token']);
882
+
883
+        $shareTime = new \DateTime();
884
+        $shareTime->setTimestamp((int)$data['stime']);
885
+        $share->setShareTime($shareTime);
886
+        $share->setSharedWith($data['share_with']);
887
+        $share->setPassword($data['password']);
888
+
889
+        if ($data['uid_initiator'] !== null) {
890
+            $share->setShareOwner($data['uid_owner']);
891
+            $share->setSharedBy($data['uid_initiator']);
892
+        } else {
893
+            //OLD SHARE
894
+            $share->setSharedBy($data['uid_owner']);
895
+            $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
896
+
897
+            $owner = $path->getOwner();
898
+            $share->setShareOwner($owner->getUID());
899
+        }
900
+
901
+        if ($data['expiration'] !== null) {
902
+            $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
903
+            if ($expiration !== false) {
904
+                $share->setExpirationDate($expiration);
905
+            }
906
+        }
907
+
908
+        $share->setNodeId((int)$data['file_source']);
909
+        $share->setNodeType($data['item_type']);
910
+
911
+        $share->setProviderId($this->identifier());
912
+
913
+        return $share;
914
+    }
915
+
916
+    /**
917
+     * Get the node with file $id for $user
918
+     *
919
+     * @param string $userId
920
+     * @param int $id
921
+     * @return \OCP\Files\File|\OCP\Files\Folder
922
+     * @throws InvalidShare
923
+     */
924
+    private function getNode($userId, $id) {
925
+        try {
926
+            $userFolder = $this->rootFolder->getUserFolder($userId);
927
+        } catch (NoUserException $e) {
928
+            throw new InvalidShare();
929
+        }
930
+
931
+        $nodes = $userFolder->getById($id);
932
+
933
+        if (empty($nodes)) {
934
+            throw new InvalidShare();
935
+        }
936
+
937
+        return $nodes[0];
938
+    }
939
+
940
+    /**
941
+     * A user is deleted from the system
942
+     * So clean up the relevant shares.
943
+     *
944
+     * @param string $uid
945
+     * @param int $shareType
946
+     */
947
+    public function userDeleted($uid, $shareType) {
948
+        $qb = $this->dbConnection->getQueryBuilder();
949
+
950
+        $qb->delete('share')
951
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
952
+            ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
953
+            ->execute();
954
+    }
955
+
956
+    /**
957
+     * This provider does not support group shares
958
+     *
959
+     * @param string $gid
960
+     */
961
+    public function groupDeleted($gid) {
962
+        return;
963
+    }
964
+
965
+    /**
966
+     * This provider does not support group shares
967
+     *
968
+     * @param string $uid
969
+     * @param string $gid
970
+     */
971
+    public function userDeletedFromGroup($uid, $gid) {
972
+        return;
973
+    }
974
+
975
+    /**
976
+     * get database row of a give share
977
+     *
978
+     * @param $id
979
+     * @return array
980
+     * @throws ShareNotFound
981
+     */
982
+    protected function getRawShare($id) {
983
+
984
+        // Now fetch the inserted share and create a complete share object
985
+        $qb = $this->dbConnection->getQueryBuilder();
986
+        $qb->select('*')
987
+            ->from('share')
988
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
989
+
990
+        $cursor = $qb->execute();
991
+        $data = $cursor->fetch();
992
+        $cursor->closeCursor();
993
+
994
+        if ($data === false) {
995
+            throw new ShareNotFound;
996
+        }
997
+
998
+        return $data;
999
+    }
1000
+
1001
+    public function getSharesInFolder($userId, Folder $node, $reshares) {
1002
+        $qb = $this->dbConnection->getQueryBuilder();
1003
+        $qb->select('*')
1004
+            ->from('share', 's')
1005
+            ->andWhere($qb->expr()->orX(
1006
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1007
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1008
+            ))
1009
+            ->andWhere(
1010
+                $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
1011
+            );
1012
+
1013
+        /**
1014
+         * Reshares for this user are shares where they are the owner.
1015
+         */
1016
+        if ($reshares === false) {
1017
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
1018
+        } else {
1019
+            $qb->andWhere(
1020
+                $qb->expr()->orX(
1021
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
1022
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
1023
+                )
1024
+            );
1025
+        }
1026
+
1027
+        $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
1028
+        $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
1029
+
1030
+        $qb->orderBy('id');
1031
+
1032
+        $cursor = $qb->execute();
1033
+        $shares = [];
1034
+        while ($data = $cursor->fetch()) {
1035
+            $shares[$data['fileid']][] = $this->createShareObject($data);
1036
+        }
1037
+        $cursor->closeCursor();
1038
+
1039
+        return $shares;
1040
+    }
1041
+
1042
+    /**
1043
+     * @inheritdoc
1044
+     */
1045
+    public function getAccessList($nodes, $currentAccess) {
1046
+        $ids = [];
1047
+        foreach ($nodes as $node) {
1048
+            $ids[] = $node->getId();
1049
+        }
1050
+
1051
+        $qb = $this->dbConnection->getQueryBuilder();
1052
+        $qb->select('share_with')
1053
+            ->from('share')
1054
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
1055
+            ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1056
+            ->andWhere($qb->expr()->orX(
1057
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1058
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1059
+            ))
1060
+            ->setMaxResults(1);
1061
+        $cursor = $qb->execute();
1062
+
1063
+        $mail = $cursor->fetch() !== false;
1064
+        $cursor->closeCursor();
1065
+
1066
+        return ['public' => $mail];
1067
+    }
1068 1068
 
1069 1069
 }
Please login to merge, or discard this patch.
core/routes.php 1 patch
Indentation   +69 added lines, -69 removed lines patch added patch discarded remove patch
@@ -35,40 +35,40 @@  discard block
 block discarded – undo
35 35
 
36 36
 $application = new Application();
37 37
 $application->registerRoutes($this, [
38
-	'routes' => [
39
-		['name' => 'lost#email', 'url' => '/lostpassword/email', 'verb' => 'POST'],
40
-		['name' => 'lost#resetform', 'url' => '/lostpassword/reset/form/{token}/{userId}', 'verb' => 'GET'],
41
-		['name' => 'lost#setPassword', 'url' => '/lostpassword/set/{token}/{userId}', 'verb' => 'POST'],
42
-		['name' => 'user#getDisplayNames', 'url' => '/displaynames', 'verb' => 'POST'],
43
-		['name' => 'avatar#getAvatar', 'url' => '/avatar/{userId}/{size}', 'verb' => 'GET'],
44
-		['name' => 'avatar#deleteAvatar', 'url' => '/avatar/', 'verb' => 'DELETE'],
45
-		['name' => 'avatar#postCroppedAvatar', 'url' => '/avatar/cropped', 'verb' => 'POST'],
46
-		['name' => 'avatar#getTmpAvatar', 'url' => '/avatar/tmp', 'verb' => 'GET'],
47
-		['name' => 'avatar#postAvatar', 'url' => '/avatar/', 'verb' => 'POST'],
48
-		['name' => 'login#tryLogin', 'url' => '/login', 'verb' => 'POST'],
49
-		['name' => 'login#confirmPassword', 'url' => '/login/confirm', 'verb' => 'POST'],
50
-		['name' => 'login#showLoginForm', 'url' => '/login', 'verb' => 'GET'],
51
-		['name' => 'login#logout', 'url' => '/logout', 'verb' => 'GET'],
52
-		['name' => 'ClientFlowLogin#showAuthPickerPage', 'url' => '/login/flow', 'verb' => 'GET'],
53
-		['name' => 'ClientFlowLogin#redirectPage', 'url' => '/login/flow/redirect', 'verb' => 'GET'],
54
-		['name' => 'ClientFlowLogin#generateAppPassword', 'url' => '/login/flow', 'verb' => 'POST'],
55
-		['name' => 'TwoFactorChallenge#selectChallenge', 'url' => '/login/selectchallenge', 'verb' => 'GET'],
56
-		['name' => 'TwoFactorChallenge#showChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'GET'],
57
-		['name' => 'TwoFactorChallenge#solveChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'POST'],
58
-		['name' => 'OCJS#getConfig', 'url' => '/core/js/oc.js', 'verb' => 'GET'],
59
-		['name' => 'Preview#getPreview', 'url' => '/core/preview', 'verb' => 'GET'],
60
-		['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'],
61
-		['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'],
62
-		['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'],
63
-		['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'POST'],
64
-		['name' => 'contactsMenu#findOne', 'url' => '/contactsmenu/findOne', 'verb' => 'POST'],
65
-	],
66
-	'ocs' => [
67
-		['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'],
68
-		['root' => '', 'name' => 'OCS#getConfig', 'url' => '/config', 'verb' => 'GET'],
69
-		['root' => '/person', 'name' => 'OCS#personCheck', 'url' => '/check', 'verb' => 'POST'],
70
-		['root' => '/identityproof', 'name' => 'OCS#getIdentityProof', 'url' => '/key/{cloudId}', 'verb' => 'GET'],
71
-	],
38
+    'routes' => [
39
+        ['name' => 'lost#email', 'url' => '/lostpassword/email', 'verb' => 'POST'],
40
+        ['name' => 'lost#resetform', 'url' => '/lostpassword/reset/form/{token}/{userId}', 'verb' => 'GET'],
41
+        ['name' => 'lost#setPassword', 'url' => '/lostpassword/set/{token}/{userId}', 'verb' => 'POST'],
42
+        ['name' => 'user#getDisplayNames', 'url' => '/displaynames', 'verb' => 'POST'],
43
+        ['name' => 'avatar#getAvatar', 'url' => '/avatar/{userId}/{size}', 'verb' => 'GET'],
44
+        ['name' => 'avatar#deleteAvatar', 'url' => '/avatar/', 'verb' => 'DELETE'],
45
+        ['name' => 'avatar#postCroppedAvatar', 'url' => '/avatar/cropped', 'verb' => 'POST'],
46
+        ['name' => 'avatar#getTmpAvatar', 'url' => '/avatar/tmp', 'verb' => 'GET'],
47
+        ['name' => 'avatar#postAvatar', 'url' => '/avatar/', 'verb' => 'POST'],
48
+        ['name' => 'login#tryLogin', 'url' => '/login', 'verb' => 'POST'],
49
+        ['name' => 'login#confirmPassword', 'url' => '/login/confirm', 'verb' => 'POST'],
50
+        ['name' => 'login#showLoginForm', 'url' => '/login', 'verb' => 'GET'],
51
+        ['name' => 'login#logout', 'url' => '/logout', 'verb' => 'GET'],
52
+        ['name' => 'ClientFlowLogin#showAuthPickerPage', 'url' => '/login/flow', 'verb' => 'GET'],
53
+        ['name' => 'ClientFlowLogin#redirectPage', 'url' => '/login/flow/redirect', 'verb' => 'GET'],
54
+        ['name' => 'ClientFlowLogin#generateAppPassword', 'url' => '/login/flow', 'verb' => 'POST'],
55
+        ['name' => 'TwoFactorChallenge#selectChallenge', 'url' => '/login/selectchallenge', 'verb' => 'GET'],
56
+        ['name' => 'TwoFactorChallenge#showChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'GET'],
57
+        ['name' => 'TwoFactorChallenge#solveChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'POST'],
58
+        ['name' => 'OCJS#getConfig', 'url' => '/core/js/oc.js', 'verb' => 'GET'],
59
+        ['name' => 'Preview#getPreview', 'url' => '/core/preview', 'verb' => 'GET'],
60
+        ['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'],
61
+        ['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'],
62
+        ['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'],
63
+        ['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'POST'],
64
+        ['name' => 'contactsMenu#findOne', 'url' => '/contactsmenu/findOne', 'verb' => 'POST'],
65
+    ],
66
+    'ocs' => [
67
+        ['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'],
68
+        ['root' => '', 'name' => 'OCS#getConfig', 'url' => '/config', 'verb' => 'GET'],
69
+        ['root' => '/person', 'name' => 'OCS#personCheck', 'url' => '/check', 'verb' => 'POST'],
70
+        ['root' => '/identityproof', 'name' => 'OCS#getIdentityProof', 'url' => '/key/{cloudId}', 'verb' => 'GET'],
71
+    ],
72 72
 ]);
73 73
 
74 74
 // Post installation check
@@ -77,15 +77,15 @@  discard block
 block discarded – undo
77 77
 // Core ajax actions
78 78
 // Search
79 79
 $this->create('search_ajax_search', '/core/search')
80
-	->actionInclude('core/search/ajax/search.php');
80
+    ->actionInclude('core/search/ajax/search.php');
81 81
 // Routing
82 82
 $this->create('core_ajax_update', '/core/ajax/update.php')
83
-	->actionInclude('core/ajax/update.php');
83
+    ->actionInclude('core/ajax/update.php');
84 84
 
85 85
 // File routes
86 86
 $this->create('files.viewcontroller.showFile', '/f/{fileid}')->action(function($urlParams) {
87
-	$app = new \OCA\Files\AppInfo\Application($urlParams);
88
-	$app->dispatch('ViewController', 'index');
87
+    $app = new \OCA\Files\AppInfo\Application($urlParams);
88
+    $app->dispatch('ViewController', 'index');
89 89
 });
90 90
 
91 91
 // Call routes
@@ -94,49 +94,49 @@  discard block
 block discarded – undo
94 94
  * @suppress PhanUndeclaredClassMethod
95 95
  */
96 96
 $this->create('spreed.pagecontroller.showCall', '/call/{token}')->action(function($urlParams) {
97
-	if (class_exists(\OCA\Spreed\AppInfo\Application::class, false)) {
98
-		$app = new \OCA\Spreed\AppInfo\Application($urlParams);
99
-		$app->dispatch('PageController', 'index');
100
-	} else {
101
-		throw new \OC\HintException('App spreed is not enabled');
102
-	}
97
+    if (class_exists(\OCA\Spreed\AppInfo\Application::class, false)) {
98
+        $app = new \OCA\Spreed\AppInfo\Application($urlParams);
99
+        $app->dispatch('PageController', 'index');
100
+    } else {
101
+        throw new \OC\HintException('App spreed is not enabled');
102
+    }
103 103
 });
104 104
 
105 105
 // Sharing routes
106 106
 $this->create('files_sharing.sharecontroller.showShare', '/s/{token}')->action(function($urlParams) {
107
-	if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) {
108
-		$app = new \OCA\Files_Sharing\AppInfo\Application($urlParams);
109
-		$app->dispatch('ShareController', 'showShare');
110
-	} else {
111
-		throw new \OC\HintException('App file sharing is not enabled');
112
-	}
107
+    if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) {
108
+        $app = new \OCA\Files_Sharing\AppInfo\Application($urlParams);
109
+        $app->dispatch('ShareController', 'showShare');
110
+    } else {
111
+        throw new \OC\HintException('App file sharing is not enabled');
112
+    }
113 113
 });
114 114
 $this->create('files_sharing.sharecontroller.authenticate', '/s/{token}/authenticate')->post()->action(function($urlParams) {
115
-	if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) {
116
-		$app = new \OCA\Files_Sharing\AppInfo\Application($urlParams);
117
-		$app->dispatch('ShareController', 'authenticate');
118
-	} else {
119
-		throw new \OC\HintException('App file sharing is not enabled');
120
-	}
115
+    if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) {
116
+        $app = new \OCA\Files_Sharing\AppInfo\Application($urlParams);
117
+        $app->dispatch('ShareController', 'authenticate');
118
+    } else {
119
+        throw new \OC\HintException('App file sharing is not enabled');
120
+    }
121 121
 });
122 122
 $this->create('files_sharing.sharecontroller.showAuthenticate', '/s/{token}/authenticate')->get()->action(function($urlParams) {
123
-	if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) {
124
-		$app = new \OCA\Files_Sharing\AppInfo\Application($urlParams);
125
-		$app->dispatch('ShareController', 'showAuthenticate');
126
-	} else {
127
-		throw new \OC\HintException('App file sharing is not enabled');
128
-	}
123
+    if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) {
124
+        $app = new \OCA\Files_Sharing\AppInfo\Application($urlParams);
125
+        $app->dispatch('ShareController', 'showAuthenticate');
126
+    } else {
127
+        throw new \OC\HintException('App file sharing is not enabled');
128
+    }
129 129
 });
130 130
 $this->create('files_sharing.sharecontroller.downloadShare', '/s/{token}/download')->get()->action(function($urlParams) {
131
-	if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) {
132
-		$app = new \OCA\Files_Sharing\AppInfo\Application($urlParams);
133
-		$app->dispatch('ShareController', 'downloadShare');
134
-	} else {
135
-		throw new \OC\HintException('App file sharing is not enabled');
136
-	}
131
+    if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) {
132
+        $app = new \OCA\Files_Sharing\AppInfo\Application($urlParams);
133
+        $app->dispatch('ShareController', 'downloadShare');
134
+    } else {
135
+        throw new \OC\HintException('App file sharing is not enabled');
136
+    }
137 137
 });
138 138
 
139 139
 // used for heartbeat
140 140
 $this->create('heartbeat', '/heartbeat')->action(function(){
141
-	// do nothing
141
+    // do nothing
142 142
 });
Please login to merge, or discard this patch.